summaryrefslogtreecommitdiffstats
path: root/bsps
diff options
context:
space:
mode:
authorSebastian Huber <sebastian.huber@embedded-brains.de>2018-04-03 07:20:11 +0200
committerSebastian Huber <sebastian.huber@embedded-brains.de>2018-04-04 10:13:28 +0200
commit27de4e1fb8bcdbdd8cb882fc0d7a2c152b4e027a (patch)
treedef0664dcddc53fd5d599b455c64f76ca2293606 /bsps
parentbsps: Move config macros to RTEMS_BSP_CONFIGURE (diff)
downloadrtems-27de4e1fb8bcdbdd8cb882fc0d7a2c152b4e027a.tar.bz2
bsps: Move libchip to bsps
This patch is a part of the BSP source reorganization. Update #3285.
Diffstat (limited to 'bsps')
-rw-r--r--bsps/powerpc/gen5200/dev/mpc5200-ata.c239
-rw-r--r--bsps/shared/dev/display/disp_fonts.h55
-rw-r--r--bsps/shared/dev/display/disp_hcms29xx.c932
-rw-r--r--bsps/shared/dev/display/font_hcms29xx.c1820
-rw-r--r--bsps/shared/dev/display/font_hcms29xx.h36
-rw-r--r--bsps/shared/dev/flash/am29lv160.c473
-rw-r--r--bsps/shared/dev/i2c/i2c-2b-eeprom.c177
-rw-r--r--bsps/shared/dev/i2c/i2c-ds1621.c128
-rw-r--r--bsps/shared/dev/i2c/i2c-sc620.c91
-rw-r--r--bsps/shared/dev/i2c/spi-flash-m25p40.c60
-rw-r--r--bsps/shared/dev/i2c/spi-fram-fm25l256.c60
-rw-r--r--bsps/shared/dev/i2c/spi-memdrv.c442
-rw-r--r--bsps/shared/dev/i2c/spi-sd-card.c1322
-rw-r--r--bsps/shared/dev/ide/ata.c1360
-rw-r--r--bsps/shared/dev/ide/ata_util.c215
-rw-r--r--bsps/shared/dev/ide/ide_controller.c200
-rw-r--r--bsps/shared/dev/rtc/README.ds16433
-rw-r--r--bsps/shared/dev/rtc/README.icm717048
-rw-r--r--bsps/shared/dev/rtc/README.m48t0844
-rw-r--r--bsps/shared/dev/rtc/README.m48t181
-rw-r--r--bsps/shared/dev/rtc/README.mc146818a1
-rw-r--r--bsps/shared/dev/rtc/STATUS33
-rw-r--r--bsps/shared/dev/rtc/ds1375.c461
-rw-r--r--bsps/shared/dev/rtc/icm7170.c168
-rw-r--r--bsps/shared/dev/rtc/icm7170_reg.c60
-rw-r--r--bsps/shared/dev/rtc/icm7170_reg2.c20
-rw-r--r--bsps/shared/dev/rtc/icm7170_reg4.c20
-rw-r--r--bsps/shared/dev/rtc/icm7170_reg8.c20
-rw-r--r--bsps/shared/dev/rtc/m48t08.c161
-rw-r--r--bsps/shared/dev/rtc/m48t08_reg.c60
-rw-r--r--bsps/shared/dev/rtc/m48t08_reg2.c20
-rw-r--r--bsps/shared/dev/rtc/m48t08_reg4.c20
-rw-r--r--bsps/shared/dev/rtc/m48t08_reg8.c20
-rw-r--r--bsps/shared/dev/rtc/mc146818a.c180
-rw-r--r--bsps/shared/dev/rtc/mc146818a_ioreg.c56
-rw-r--r--bsps/shared/dev/rtc/rtcprobe.c21
-rw-r--r--bsps/shared/dev/serial/README13
-rw-r--r--bsps/shared/dev/serial/README.mc6868183
-rw-r--r--bsps/shared/dev/serial/README.ns1655082
-rw-r--r--bsps/shared/dev/serial/README.xr886812
-rw-r--r--bsps/shared/dev/serial/README.z85c3074
-rw-r--r--bsps/shared/dev/serial/STATUS48
-rw-r--r--bsps/shared/dev/serial/mc68681.c776
-rw-r--r--bsps/shared/dev/serial/mc68681_baud.c124
-rw-r--r--bsps/shared/dev/serial/mc68681_p.h323
-rw-r--r--bsps/shared/dev/serial/mc68681_reg.c61
-rw-r--r--bsps/shared/dev/serial/mc68681_reg2.c20
-rw-r--r--bsps/shared/dev/serial/mc68681_reg4.c20
-rw-r--r--bsps/shared/dev/serial/mc68681_reg8.c20
-rw-r--r--bsps/shared/dev/serial/ns16550-context.c814
-rw-r--r--bsps/shared/dev/serial/ns16550.c875
-rw-r--r--bsps/shared/dev/serial/serprobe.c13
-rw-r--r--bsps/shared/dev/serial/z85c30.c893
-rw-r--r--bsps/shared/dev/serial/z85c30_p.h420
-rw-r--r--bsps/shared/dev/serial/z85c30_reg.c72
-rw-r--r--bsps/shared/net/README12
-rw-r--r--bsps/shared/net/README.3com3
-rw-r--r--bsps/shared/net/README.cs890026
-rw-r--r--bsps/shared/net/README.dec21140116
-rw-r--r--bsps/shared/net/README.i825861
-rw-r--r--bsps/shared/net/README.open_eth72
-rw-r--r--bsps/shared/net/README.sonic135
-rw-r--r--bsps/shared/net/README.tulipclone101
-rw-r--r--bsps/shared/net/cs8900.c1216
-rw-r--r--bsps/shared/net/cs8900.c.bsp510
-rw-r--r--bsps/shared/net/cs8900.h.bsp38
-rw-r--r--bsps/shared/net/dec21140.c1112
-rw-r--r--bsps/shared/net/elnk.c3553
-rw-r--r--bsps/shared/net/greth2.c1196
-rw-r--r--bsps/shared/net/i82586.c2198
-rw-r--r--bsps/shared/net/i82586reg.h448
-rw-r--r--bsps/shared/net/if_dc.c3849
-rw-r--r--bsps/shared/net/if_fxp.c2339
-rw-r--r--bsps/shared/net/if_fxpreg.h370
-rw-r--r--bsps/shared/net/open_eth.c767
-rw-r--r--bsps/shared/net/smc91111.c1653
-rw-r--r--bsps/shared/net/smc91111config.h118
-rw-r--r--bsps/shared/net/sonic.c1685
-rw-r--r--bsps/shared/shared-sources.am52
79 files changed, 35330 insertions, 0 deletions
diff --git a/bsps/powerpc/gen5200/dev/mpc5200-ata.c b/bsps/powerpc/gen5200/dev/mpc5200-ata.c
new file mode 100644
index 0000000000..de7ee27fb3
--- /dev/null
+++ b/bsps/powerpc/gen5200/dev/mpc5200-ata.c
@@ -0,0 +1,239 @@
+/*
+ * Copyright (c) 2010-2013 embedded brains GmbH. All rights reserved.
+ *
+ * embedded brains GmbH
+ * Dornierstr. 4
+ * 82178 Puchheim
+ * Germany
+ * <rtems@embedded-brains.de>
+ *
+ * The license and distribution terms for this file may be
+ * found in the file LICENSE in this distribution or at
+ * http://www.rtems.org/license/LICENSE.
+ */
+
+#define NDEBUG
+
+#include <bsp/ata.h>
+
+#include <bsp.h>
+#include <bsp/fatal.h>
+#include <bsp/mpc5200.h>
+
+#include <libcpu/powerpc-utility.h>
+
+bool ata_execute_io_command(uint8_t command, uint32_t lba, uint32_t sector_count_32)
+{
+ assert(sector_count_32 >= 1);
+ assert(sector_count_32 <= 256);
+
+ assert(ata_is_drive_ready_for_selection());
+
+ ATA->write.head = (uint8_t) (0xe0 | ((lba >> 24) & 0x0f));
+
+ ata_wait_400_nano_seconds();
+ ata_wait_for_drive_ready();
+
+ /*
+ * It seems that the write to the ATA device registers is sometimes not
+ * successful. The write is retried until the read back values are all right
+ * or a timeout is reached.
+ */
+ bool ok = false;
+ uint8_t sector = (uint8_t) lba;
+ uint8_t cylinder_low = (uint8_t) (lba >> 8);
+ uint8_t cylinder_high = (uint8_t) (lba >> 16);
+ uint8_t sector_count = (uint8_t) sector_count_32;
+ int i;
+ for (i = 0; !ok && i < 100; ++i) {
+ ATA->write.sector = sector;
+ ATA->write.cylinder_low = cylinder_low;
+ ATA->write.cylinder_high = cylinder_high;
+ ATA->write.sector_count = sector_count;
+
+ uint8_t actual_sector = ATA->read.sector;
+ uint8_t actual_cylinder_low = ATA->read.cylinder_low;
+ uint8_t actual_cylinder_high = ATA->read.cylinder_high;
+ uint8_t actual_sector_count = ATA->read.sector_count;
+
+ ok = actual_cylinder_high == cylinder_high
+ && actual_cylinder_low == cylinder_low
+ && actual_sector == sector
+ && actual_sector_count == sector_count;
+ }
+
+ if (ok) {
+ ATA->write.command = command;
+ }
+
+ return ok;
+}
+
+void ata_reset_device(void)
+{
+ /* ATA/ATAPI-7 V2, 11.2 Software reset protocol */
+ ATA->write.control = DCTRL_SRST;
+ rtems_bsp_delay(5);
+ ATA->write.control = 0;
+ rtems_bsp_delay(2000);
+ ata_wait_for_not_busy();
+}
+
+bool ata_set_transfer_mode(uint8_t mode)
+{
+ assert(ata_is_drive_ready_for_selection());
+
+ ATA->write.head = 0xe0;
+
+ ata_wait_400_nano_seconds();
+ ata_wait_for_drive_ready();
+
+ ATA->write.feature = 0x3;
+ ATA->write.sector_count = mode;
+ ATA->write.command = 0xef;
+
+ ata_wait_for_not_busy();
+
+ return ata_check_status();
+}
+
+static bool probe(void)
+{
+ bool card_present = true;
+
+#ifdef MPC5200_BOARD_BRS5L
+ volatile struct mpc5200_gpt *gpt = &mpc5200.gpt[GPT2];
+
+ /* Enable card detection on GPT2 */
+ gpt->emsel = (GPT_EMSEL_GPIO_IN | GPT_EMSEL_TIMER_MS_GPIO);
+
+ /* Check for card detection (-CD0) */
+ if ((gpt->status & GPT_STATUS_PIN) != 0) {
+ card_present = false;
+ }
+#endif
+
+ return card_present;
+}
+
+static void create_lock(ata_driver *self)
+{
+ rtems_status_code sc = rtems_semaphore_create(
+ rtems_build_name('A', 'T', 'A', ' '),
+ 1,
+ RTEMS_LOCAL | RTEMS_BINARY_SEMAPHORE | RTEMS_PRIORITY | RTEMS_INHERIT_PRIORITY,
+ 0,
+ &self->lock
+ );
+ if (sc != RTEMS_SUCCESSFUL) {
+ bsp_fatal(MPC5200_FATAL_ATA_LOCK_CREATE);
+ }
+}
+
+static void destroy_lock(const ata_driver *self)
+{
+ rtems_status_code sc = rtems_semaphore_delete(self->lock);
+ if (sc != RTEMS_SUCCESSFUL) {
+ bsp_fatal(MPC5200_FATAL_ATA_LOCK_DESTROY);
+ }
+}
+
+void ata_driver_create(ata_driver *self, const char *device_file_path, rtems_block_device_ioctl io_control)
+{
+ self->card_present = probe();
+
+ if (ata_driver_is_card_present(self)) {
+ create_lock(self);
+ ata_reset_device();
+
+ uint16_t sector_buffer[256];
+ ata_dev_t ata_device;
+ rtems_status_code sc = ata_identify_device(0, 0, sector_buffer, &ata_device);
+
+ if (sc == RTEMS_SUCCESSFUL && ata_device.lba_avaible) {
+ sc = ide_controller_config_io_speed(0, ata_device.modes_available);
+
+ if (sc == RTEMS_SUCCESSFUL) {
+ sc = rtems_blkdev_create(
+ device_file_path,
+ ATA_SECTOR_SIZE,
+ ata_device.lba_sectors,
+ io_control,
+ self
+ );
+
+ if (sc != RTEMS_SUCCESSFUL) {
+ bsp_fatal(MPC5200_FATAL_ATA_DISK_CREATE);
+ }
+ }
+ }
+ }
+}
+
+void ata_driver_destroy(ata_driver *self)
+{
+ destroy_lock(self);
+ /* TODO */
+ assert(0);
+}
+
+static bool transfer_pio_polled(ata_driver *self, bool read, rtems_blkdev_sg_buffer *sg, size_t sg_count)
+{
+ bool ok = true;
+
+ ata_sg_context sg_context;
+ ata_sg_create(&sg_context, sg, sg_count);
+ rtems_blkdev_bnum start_sector = ata_sg_get_start_sector(&sg_context);
+ rtems_blkdev_bnum sector_count = ata_sg_get_sector_count(&sg_context);
+ rtems_blkdev_bnum relative_sector = 0;
+
+ uint8_t command = ata_read_or_write_sectors_command(read);
+
+ while (ok && relative_sector < sector_count) {
+ rtems_blkdev_bnum remaining_sectors = sector_count - relative_sector;
+ rtems_blkdev_bnum transfer_count = ata_max_transfer_count(remaining_sectors);
+ rtems_blkdev_bnum transfer_end = relative_sector + transfer_count;
+
+ ok = ata_execute_io_command(command, start_sector + relative_sector, transfer_count);
+
+ rtems_blkdev_bnum transfer;
+ for (transfer = relative_sector; ok && transfer < transfer_end; ++transfer) {
+ uint16_t *current = ata_sg_get_sector_data_begin(&sg_context, transfer);
+ uint16_t *end = ata_sg_get_sector_data_end(&sg_context, current);
+
+ ok = ata_wait_for_data_request();
+
+ if (ok) {
+ if (read) {
+ while (current != end) {
+ *current = ATA->read.data;
+ ++current;
+ }
+ } else {
+ while (current != end) {
+ ATA->write.data = *current;
+ ++current;
+ }
+ }
+ }
+ }
+
+ if (ok) {
+ ata_wait_for_not_busy();
+ ok = ata_check_status();
+ }
+
+ relative_sector += ATA_PER_TRANSFER_SECTOR_COUNT_MAX;
+ }
+
+ return ok;
+}
+
+int ata_driver_io_control_pio_polled(
+ rtems_disk_device *dd,
+ uint32_t cmd,
+ void *arg
+)
+{
+ return ata_driver_io_control(dd, cmd, arg, transfer_pio_polled);
+}
diff --git a/bsps/shared/dev/display/disp_fonts.h b/bsps/shared/dev/display/disp_fonts.h
new file mode 100644
index 0000000000..39e909390a
--- /dev/null
+++ b/bsps/shared/dev/display/disp_fonts.h
@@ -0,0 +1,55 @@
+/*===============================================================*\
+| Project: display driver for HCMS29xx |
++-----------------------------------------------------------------+
+| File: disp_fonts.h |
++-----------------------------------------------------------------+
+| Copyright (c) 2008 |
+| Embedded Brains GmbH |
+| Obere Lagerstr. 30 |
+| D-82178 Puchheim |
+| Germany |
+| rtems@embedded-brains.de |
++-----------------------------------------------------------------+
+| The license and distribution terms for this file may be |
+| found in the file LICENSE in this distribution or at |
+| http://www.rtems.org/license/LICENSE. |
+| |
++-----------------------------------------------------------------+
+| This file declares general data structures for font management |
+\*===============================================================*/
+
+#ifndef DISP_FONTS_H
+#define DISP_FONTS_H
+
+#include <rtems.h>
+
+typedef int8_t disp_font_dimen;
+
+struct disp_font_bounding_box
+{
+ disp_font_dimen w, h, x, y;
+};
+
+struct disp_font_glyph
+{
+ struct disp_font_bounding_box bb;
+ disp_font_dimen wx, wy;
+ const unsigned char *bitmap;
+};
+
+struct disp_font_base
+{
+ int8_t trans;
+ struct disp_font_bounding_box fbb;
+ disp_font_dimen ascent, descent;
+ uint8_t default_char;
+ struct disp_font_glyph *latin1[256];
+};
+
+typedef struct disp_font_base *disp_font_t;
+
+/* Prototypes ------------------------------------------------- */
+
+/* End -------------------------------------------------------- */
+
+#endif /* not defined DISP_FONTS_H */
diff --git a/bsps/shared/dev/display/disp_hcms29xx.c b/bsps/shared/dev/display/disp_hcms29xx.c
new file mode 100644
index 0000000000..5730b36ea9
--- /dev/null
+++ b/bsps/shared/dev/display/disp_hcms29xx.c
@@ -0,0 +1,932 @@
+/*===============================================================*\
+| Project: display driver for HCMS29xx |
++-----------------------------------------------------------------+
+| File: disp_hcms29xx.c |
++-----------------------------------------------------------------+
+| Copyright (c) 2008 |
+| Embedded Brains GmbH |
+| Obere Lagerstr. 30 |
+| D-82178 Puchheim |
+| Germany |
+| rtems@embedded-brains.de |
++-----------------------------------------------------------------+
+| The license and distribution terms for this file may be |
+| found in the file LICENSE in this distribution or at |
+| http://www.rtems.org/license/LICENSE. |
++-----------------------------------------------------------------+
+| this file contains the SPI based driver for a HCMS29xx 4 digit |
+| alphanumeric LED display |
+\*===============================================================*/
+
+#include <string.h>
+#include <stdlib.h>
+
+#include <rtems.h>
+#include <rtems/libio.h>
+#include <bsp.h>
+#include <rtems/libi2c.h>
+#include <libchip/disp_hcms29xx.h>
+#include "font_hcms29xx.h"
+#define FONT_BASE font_hcms29xx_base
+
+
+#define DISP_HCMS29XX_DIGIT_CNT (4)
+#define DISP_HCMS29XX_SEMA_NAME rtems_build_name('D','4','I','Q')
+#define DISP_HCMS29XX_TRNS_SEMA_NAME rtems_build_name('D','4','T','R')
+#define DISP_HCMS29XX_TIMER_NAME rtems_build_name('D','4','T','M')
+#define DISP_HCMS29XX_TASK_NAME rtems_build_name('D','4','T','A')
+
+#define DISP_HCMS29XX_EVENT_TIMER RTEMS_EVENT_1
+#define DISP_HCMS29XX_EVENT_NEWSTR RTEMS_EVENT_2
+
+
+static disp_font_t disp_hcms29xx_font_normal;
+static disp_font_t disp_hcms29xx_font_rotate;
+const rtems_libi2c_tfr_mode_t spi_disphcms29xx_tfr_mode = {
+ .baudrate = 1000000,
+ .bits_per_char = 8,
+ .lsb_first = true,
+ .clock_inv = true,
+ .clock_phs = true,
+ .idle_char = 0
+};
+
+static disp_hcms29xx_drv_t disp_hcms29xx_drv_tbl;
+
+/*=========================================
+ * font management functions
+ */
+
+/*=========================================================================*\
+| Function: |
+\*-------------------------------------------------------------------------*/
+static rtems_status_code disp_hcms29xx_font_struct_size
+ (
+/*-------------------------------------------------------------------------*\
+| Purpose: |
+| compute size of font data structure tree |
++---------------------------------------------------------------------------+
+| Input Parameters: |
+\*-------------------------------------------------------------------------*/
+ disp_font_t src, /* source font */
+ size_t *dst_size /* destination: size of font struct*/
+)
+/*-------------------------------------------------------------------------*\
+| Return Value: |
+| rtems_status_code |
+\*=========================================================================*/
+{
+
+ rtems_status_code rc = RTEMS_SUCCESSFUL;
+ size_t font_size = 0;
+ size_t glyph_idx;
+ /*
+ * check parameters
+ */
+ if ((rc == RTEMS_SUCCESSFUL) &&
+ (src == NULL)) {
+ rc = RTEMS_INVALID_ADDRESS;
+ }
+ if (rc == RTEMS_SUCCESSFUL) {
+ font_size =
+ sizeof(*src); /* font_base structure */
+ }
+ glyph_idx = 0;
+ while ((rc == RTEMS_SUCCESSFUL) &&
+ (glyph_idx < (sizeof(src->latin1)/sizeof(src->latin1[0])))) {
+ if (src->latin1[glyph_idx] != NULL) {
+ font_size += sizeof(*(src->latin1[glyph_idx]))
+ + (size_t) src->latin1[glyph_idx]->bb.w;
+ }
+ glyph_idx++;
+ }
+ *dst_size = font_size;
+
+ return rc;
+}
+
+/*=========================================================================*\
+| Function: |
+\*-------------------------------------------------------------------------*/
+static inline unsigned char disp_hcms29xx_bitswap
+ (
+/*-------------------------------------------------------------------------*\
+| Purpose: |
+| swap data bits in byte (7<->0 , 6<->1 etc) |
++---------------------------------------------------------------------------+
+| Input Parameters: |
+\*-------------------------------------------------------------------------*/
+ unsigned char byte
+)
+/*-------------------------------------------------------------------------*\
+| Return Value: |
+| rtems_status_code |
+\*=========================================================================*/
+{
+ unsigned char result = 0;
+ int smsk,dmsk;
+ for (smsk = 0x01,dmsk=0x80;
+ smsk < 0x100;
+ smsk<<=1 ,dmsk>>=1) {
+ if ((byte & smsk) != 0) {
+ result |= (unsigned char) dmsk;
+ }
+ }
+ return result;
+}
+
+/*=========================================================================*\
+| Function: |
+\*-------------------------------------------------------------------------*/
+static rtems_status_code disp_hcms29xx_copy_font
+ (
+/*-------------------------------------------------------------------------*\
+| Purpose: |
+| copy font data from source to dest font structure |
++---------------------------------------------------------------------------+
+| Input Parameters: |
+\*-------------------------------------------------------------------------*/
+ disp_font_t src, /* source font */
+ struct disp_font_base *dst, /* ptr to destination font */
+ int shift_cnt, /* shift count for font */
+ bool do_rotate /* rotate font, if true */
+)
+/*-------------------------------------------------------------------------*\
+| Return Value: |
+| rtems_status_code |
+\*=========================================================================*/
+{
+
+ rtems_status_code rc = RTEMS_SUCCESSFUL;
+ char *alloc_next = (char *)dst;
+ size_t glyph_idx = 0;
+ int glyph_size;
+ unsigned char byte;
+ int bcnt;
+
+ /*
+ * check parameters
+ */
+ if ((rc == RTEMS_SUCCESSFUL) &&
+ ((src == NULL) ||
+ (dst == NULL))) {
+ rc = RTEMS_INVALID_ADDRESS;
+ }
+ /*
+ * copy font_base structure
+ */
+ if (rc == RTEMS_SUCCESSFUL) {
+ *dst = *src;
+ alloc_next += sizeof(*dst);
+ }
+ /*
+ * for all glyphs: assign own glyph memory
+ */
+ glyph_idx = 0;
+ while ((rc == RTEMS_SUCCESSFUL) &&
+ glyph_idx < (sizeof(src->latin1)/sizeof(src->latin1[0]))) {
+ if (src->latin1[glyph_idx] != NULL) {
+ /*
+ * allocate space for glyph
+ */
+ dst->latin1[glyph_idx] = (struct disp_font_glyph *)alloc_next;
+ alloc_next += sizeof(*(dst->latin1[glyph_idx]));
+ /*
+ * copy source values.
+ * Note: bitmap will be reassigned later
+ */
+ *(struct disp_font_glyph *)(dst->latin1[glyph_idx]) =
+ *(src->latin1[glyph_idx]);
+ }
+ else {
+ dst->latin1[glyph_idx] = NULL;
+ }
+ glyph_idx++;
+ }
+
+ /*
+ * for all glyphs: reassign bitmap
+ */
+ glyph_idx = 0;
+ while ((rc == RTEMS_SUCCESSFUL) &&
+ glyph_idx < (sizeof(src->latin1)/sizeof(src->latin1[0]))) {
+ if (src->latin1[glyph_idx] != NULL) {
+ glyph_size = src->latin1[glyph_idx]->bb.w;
+ /*
+ * allocate space for glyph_bitmap
+ */
+ dst->latin1[glyph_idx]->bitmap = (const unsigned char *) alloc_next;
+ alloc_next += glyph_size;
+ /*
+ * copy/transform bitmap
+ */
+ for (bcnt = 0;bcnt < glyph_size;bcnt++) {
+ if (do_rotate) {
+ byte = src->latin1[glyph_idx]->bitmap[glyph_size - 1 - bcnt];
+ byte = disp_hcms29xx_bitswap(byte);
+ }
+ else {
+ byte = src->latin1[glyph_idx]->bitmap[bcnt];
+ }
+ if (shift_cnt < 0) {
+ byte = byte >> shift_cnt;
+ }
+ else if (shift_cnt > 0) {
+ byte = byte >> shift_cnt;
+ }
+ ((unsigned char *)(dst->latin1[glyph_idx]->bitmap))[bcnt] = byte;
+ }
+ }
+ glyph_idx++;
+ }
+ return rc;
+}
+
+/*=========================================================================*\
+| Function: |
+\*-------------------------------------------------------------------------*/
+static rtems_status_code disp_hcms29xx_alloc_copy_font
+ (
+/*-------------------------------------------------------------------------*\
+| Purpose: |
+| copy font data from source to dest font structure, alloc all data |
++---------------------------------------------------------------------------+
+| Input Parameters: |
+\*-------------------------------------------------------------------------*/
+ const disp_font_t src, /* source font */
+ disp_font_t *dst, /* ptr to destination font */
+ int shift_cnt, /* shift count for font */
+ bool do_rotate /* rotate font, if true */
+)
+/*-------------------------------------------------------------------------*\
+| Return Value: |
+| rtems_status_code |
+\*=========================================================================*/
+{
+
+ rtems_status_code rc = RTEMS_SUCCESSFUL;
+ size_t src_size = 0;
+ /*
+ * check parameters
+ */
+ if ((rc == RTEMS_SUCCESSFUL) &&
+ ((src == NULL)
+ || (dst == NULL))) {
+ rc = RTEMS_INVALID_ADDRESS;
+ }
+ /*
+ * determine size of source data
+ */
+ if (rc == RTEMS_SUCCESSFUL) {
+ rc = disp_hcms29xx_font_struct_size(src,&src_size);
+ }
+ /*
+ * allocate proper data area
+ */
+ if (rc == RTEMS_SUCCESSFUL) {
+ *dst = malloc(src_size);
+ if (*dst == NULL) {
+ rc = RTEMS_UNSATISFIED;
+ }
+ }
+ /*
+ * scan through source data, copy to dest
+ */
+ if (rc == RTEMS_SUCCESSFUL) {
+ rc = disp_hcms29xx_copy_font(src,*dst,shift_cnt,do_rotate);
+ }
+ return rc;
+}
+
+/*=========================================
+ * SPI communication functions
+ */
+
+/*=========================================================================*\
+| Function: |
+\*-------------------------------------------------------------------------*/
+static rtems_status_code disp_hcms29xx_send_to_display
+ (
+/*-------------------------------------------------------------------------*\
+| Purpose: |
+| request access semaphore to SPI, prepare buffer descriptors, start |
+| transfer via SPI to display |
++---------------------------------------------------------------------------+
+| Input Parameters: |
+\*-------------------------------------------------------------------------*/
+ disp_hcms29xx_drv_t *softc_ptr,
+ const volatile char *disp_buffer /* start of chars to display (4 chars or 'til \0)*/
+)
+/*-------------------------------------------------------------------------*\
+| Return Value: |
+| rtems_status_code |
+\*=========================================================================*/
+{
+ rtems_status_code rc = RTEMS_SUCCESSFUL;
+ bool char_avail;
+ const struct disp_font_glyph *glyph_ptr;
+ disp_font_t curr_font;
+ int i, ret_cnt;
+ unsigned char c;
+
+ /*
+ * select device, set transfer mode, address device
+ */
+ if (rc == RTEMS_SUCCESSFUL) {
+ rc = rtems_libi2c_send_start(softc_ptr->disp_param.minor);
+ }
+ /*
+ * set transfer mode
+ */
+ if (rc == RTEMS_SUCCESSFUL) {
+ rc = -rtems_libi2c_ioctl(softc_ptr->disp_param.minor,
+ RTEMS_LIBI2C_IOCTL_SET_TFRMODE,
+ &spi_disphcms29xx_tfr_mode);
+ }
+
+ /*
+ * address device
+ */
+ if (rc == RTEMS_SUCCESSFUL) {
+ rc = rtems_libi2c_send_addr(softc_ptr->disp_param.minor,true);
+ }
+
+ /*
+ * send data
+ */
+ if (rc == RTEMS_SUCCESSFUL) {
+ curr_font =
+ softc_ptr->disp_param.rotate
+ ? disp_hcms29xx_font_rotate
+ : disp_hcms29xx_font_normal;
+
+ char_avail = true;
+ /*
+ * FIXME: for rotated display, write last character first...
+ * maybe we should copy everything to a common buffer and use
+ * ONE SPI transfer?
+ */
+ for (i = 0;
+ ((rc == RTEMS_SUCCESSFUL) &&
+ (i < DISP_HCMS29XX_DIGIT_CNT));
+ i++) {
+ /* test for end of string... */
+ c = disp_buffer[i]; /* perform consistent read of disp_buffer */
+ if (char_avail && (c == '\0')) {
+ char_avail = false;
+ }
+ glyph_ptr = (char_avail
+ ? curr_font->latin1[c]
+ : NULL);
+ if (glyph_ptr == NULL) {
+ glyph_ptr = curr_font->latin1[' '];
+ }
+
+ /*
+ * send 5 bytes from (char *)glyph_ptr->bitmap to SPI
+ */
+ if (rc == RTEMS_SUCCESSFUL) {
+ ret_cnt = rtems_libi2c_write_bytes(softc_ptr->disp_param.minor,
+ glyph_ptr->bitmap,5);
+ if (ret_cnt < 0) {
+ rc = -ret_cnt;
+ }
+ }
+ }
+ }
+ /*
+ * finish transfer
+ */
+ if (rc == RTEMS_SUCCESSFUL) {
+ rc = rtems_libi2c_send_stop(softc_ptr->disp_param.minor);
+ }
+
+ return rc;
+}
+
+/*=========================================================================*\
+| Function: |
+\*-------------------------------------------------------------------------*/
+static rtems_status_code disp_hcms29xx_send_to_control
+ (
+/*-------------------------------------------------------------------------*\
+| Purpose: |
+| request access semaphore to SPI, prepare buffer descriptors, start |
+| transfer via SPI to display |
++---------------------------------------------------------------------------+
+| Input Parameters: |
+\*-------------------------------------------------------------------------*/
+ disp_hcms29xx_drv_t *softc_ptr,
+ int pwm, /* value for pwm of LEDs, 0..15 */
+ int peak, /* value for peak current for LEDs, 0..3 */
+ int sleep, /* value to make display "sleep" (0..1 */
+ int div, /* divider for external osc input, unused here */
+ int chain /* mode to drive other displays, unused here */
+)
+/*-------------------------------------------------------------------------*\
+| Return Value: |
+| rtems_status_code |
+\*=========================================================================*/
+{
+ rtems_status_code rc = RTEMS_SUCCESSFUL;
+ int run, ret_cnt;
+ uint8_t ctrl_buffer;
+
+ /* two accesses, control word 0 and 1 */
+ for (run = 0;
+ ((rc == RTEMS_SUCCESSFUL) && (run <= 1));
+ run++) {
+ if (rc == RTEMS_SUCCESSFUL) {
+ if (run == 0) {
+ ctrl_buffer =
+ (0 << 7) |
+ ((sleep & 0x01) << 6) |
+ ((peak & 0x03) << 4) |
+ ((pwm & 0x0f) << 0);
+ }
+ else {
+ ctrl_buffer =
+ (1 << 7) |
+ ((div & 0x01) << 1) |
+ ((chain & 0x01) << 0);
+ }
+ /*
+ * select device, set transfer mode, address device
+ */
+ if (rc == RTEMS_SUCCESSFUL) {
+ rc = rtems_libi2c_send_start(softc_ptr->disp_param.minor);
+ }
+ /*
+ * set transfer mode
+ */
+ if (rc == RTEMS_SUCCESSFUL) {
+ rc = -rtems_libi2c_ioctl(softc_ptr->disp_param.minor,
+ RTEMS_LIBI2C_IOCTL_SET_TFRMODE,
+ &spi_disphcms29xx_tfr_mode);
+ }
+
+ /*
+ * address device
+ */
+ if (rc == RTEMS_SUCCESSFUL) {
+ rc = rtems_libi2c_send_addr(softc_ptr->disp_param.minor,true);
+ }
+
+ /*
+ * send 1 byte from ctrl_buffer
+ */
+ if (rc == RTEMS_SUCCESSFUL) {
+ ret_cnt = rtems_libi2c_write_bytes(softc_ptr->disp_param.minor,
+ &ctrl_buffer,1);
+ if (ret_cnt < 0) {
+ rc = -ret_cnt;
+ }
+ }
+ }
+ } /* next run ... */
+
+ /*
+ * finish transfer
+ */
+ if (rc == RTEMS_SUCCESSFUL) {
+ rc = rtems_libi2c_send_stop(softc_ptr->disp_param.minor);
+ }
+
+ return rc;
+}
+
+/*=========================================================================*\
+| Function: |
+\*-------------------------------------------------------------------------*/
+static rtems_timer_service_routine disp_hcms29xx_timer_sr
+/*-------------------------------------------------------------------------*\
+| Purpose: |
+| this task updates the string in the display |
++---------------------------------------------------------------------------+
+| Input Parameters: |
+\*-------------------------------------------------------------------------*/
+(
+ rtems_id id, /* ID of timer, not used */
+ void * arg /* calling arg: softc_ptr */
+)
+/*-------------------------------------------------------------------------*\
+| Return Value: |
+| <none used> |
+\*=========================================================================*/
+{
+ disp_hcms29xx_drv_t *softc_ptr = arg;
+
+ rtems_event_send(softc_ptr->disp_param.task_id, DISP_HCMS29XX_EVENT_TIMER);
+}
+
+/*=========================================================================*\
+| Function: |
+\*-------------------------------------------------------------------------*/
+static rtems_task disp_hcms29xx_update_task
+ (
+/*-------------------------------------------------------------------------*\
+| Purpose: |
+| this task updates the string in the display |
++---------------------------------------------------------------------------+
+| Input Parameters: |
+\*-------------------------------------------------------------------------*/
+ rtems_task_argument argument
+)
+/*-------------------------------------------------------------------------*\
+| Return Value: |
+| <never exits> |
+\*=========================================================================*/
+{
+ rtems_event_set my_events;
+ rtems_status_code rc = RTEMS_SUCCESSFUL;
+ int disp_offset = 0;
+ rtems_id disp_hcms29xx_timer_id;
+ disp_hcms29xx_drv_t *softc_ptr = &disp_hcms29xx_drv_tbl;
+
+ /*
+ * initialize display:
+ */
+ /*
+ * set control attributes for display
+ * maximum brightness...
+ */
+ if (rc == RTEMS_SUCCESSFUL) {
+ rc = disp_hcms29xx_send_to_control(softc_ptr,
+ 14,3,1,0,0);/* pwm/peak/nosleep/div/chain */
+ }
+
+ /*
+ * set display to blank
+ */
+ if (rc == RTEMS_SUCCESSFUL) {
+ rc = disp_hcms29xx_send_to_display(softc_ptr,
+ "");
+ }
+
+ /*
+ * create timer for scrolling
+ */
+ if (rc == RTEMS_SUCCESSFUL) {
+ rc = rtems_timer_create(DISP_HCMS29XX_TIMER_NAME,
+ &disp_hcms29xx_timer_id);
+ }
+
+ while (rc == RTEMS_SUCCESSFUL) {
+ /*
+ * wait for any event
+ */
+ rc = rtems_event_receive(DISP_HCMS29XX_EVENT_NEWSTR |
+ DISP_HCMS29XX_EVENT_TIMER ,
+ RTEMS_WAIT | RTEMS_EVENT_ANY,
+ RTEMS_NO_TIMEOUT,
+ &my_events);
+ if (my_events & DISP_HCMS29XX_EVENT_NEWSTR) {
+ /*
+ * fetch new string consistently into local buffer
+ */
+ if (rc == RTEMS_SUCCESSFUL) {
+ rc = rtems_semaphore_obtain(softc_ptr->disp_param.trns_sema_id,
+ RTEMS_WAIT,RTEMS_NO_TIMEOUT);
+ }
+ if (rc == RTEMS_SUCCESSFUL) {
+ strncpy(softc_ptr->disp_param.disp_buffer,
+ softc_ptr->disp_param.trns_buffer,
+ sizeof(softc_ptr->disp_param.disp_buffer));
+ softc_ptr->disp_param.disp_buffer[sizeof(softc_ptr->disp_param.disp_buffer)-1] = '\0';
+ softc_ptr->disp_param.disp_buf_cnt =
+ (int) strlen(softc_ptr->disp_param.disp_buffer);
+ }
+ if (rc == RTEMS_SUCCESSFUL) {
+ rc = rtems_semaphore_release(softc_ptr->disp_param.trns_sema_id);
+ }
+ /*
+ * set initial offset to negative value
+ * to make string static for some ticks
+ */
+ disp_offset = -4;
+ }
+ if (my_events & DISP_HCMS29XX_EVENT_TIMER) {
+ /*
+ * increase disp_offset, if possible, otherwise reset it
+ */
+ if ((disp_offset < 0) ||
+ (disp_offset < softc_ptr->disp_param.disp_buf_cnt-
+ DISP_HCMS29XX_DIGIT_CNT/2)) {
+ disp_offset++;
+ }
+ else {
+ disp_offset = -4;
+ }
+ }
+ /*
+ * display string, starting from disp_offset
+ */
+ if (disp_offset < 0) {
+ rc = disp_hcms29xx_send_to_display(softc_ptr,
+ softc_ptr->disp_param.disp_buffer);
+ }
+ else if (disp_offset
+ < (softc_ptr->disp_param.disp_buf_cnt - DISP_HCMS29XX_DIGIT_CNT)) {
+ rc = disp_hcms29xx_send_to_display(softc_ptr,
+ softc_ptr->disp_param.disp_buffer+disp_offset);
+ }
+ else {
+ rc = disp_hcms29xx_send_to_display(softc_ptr,
+ softc_ptr->disp_param.disp_buffer
+ + softc_ptr->disp_param.disp_buf_cnt
+ - DISP_HCMS29XX_DIGIT_CNT);
+ }
+ /*
+ * activate timer, if needed
+ */
+ if (rc == RTEMS_SUCCESSFUL) {
+ if (softc_ptr->disp_param.disp_buf_cnt > DISP_HCMS29XX_DIGIT_CNT) {
+ rc = rtems_timer_fire_after(disp_hcms29xx_timer_id,
+ 50,
+ disp_hcms29xx_timer_sr,
+ NULL);
+ }
+ else {
+ rc = rtems_timer_cancel(disp_hcms29xx_timer_id);
+ }
+ }
+ }
+ /*
+ * FIXME: display task is dead...
+ */
+}
+
+/*=========================================================================*\
+| Function: |
+\*-------------------------------------------------------------------------*/
+static rtems_status_code disp_hcms29xx_update
+ (
+/*-------------------------------------------------------------------------*\
+| Purpose: |
+| move given string to display task |
++---------------------------------------------------------------------------+
+| Input Parameters: |
+\*-------------------------------------------------------------------------*/
+ disp_hcms29xx_drv_t *softc_ptr,
+ const char *src
+)
+/*-------------------------------------------------------------------------*\
+| Return Value: |
+| rtems_status_code |
+\*=========================================================================*/
+{
+ rtems_status_code rc = RTEMS_SUCCESSFUL;
+
+ /*
+ * obtain trns semaphore
+ */
+ if (rc == RTEMS_SUCCESSFUL) {
+ rc = rtems_semaphore_obtain(softc_ptr->disp_param.trns_sema_id,
+ RTEMS_WAIT,RTEMS_NO_TIMEOUT);
+ }
+ /*
+ * copy string...
+ */
+ strncpy(softc_ptr->disp_param.trns_buffer,src,
+ sizeof(softc_ptr->disp_param.trns_buffer));
+ softc_ptr->disp_param.trns_buffer[sizeof(softc_ptr->disp_param.trns_buffer)-1] = '\0';
+
+ /*
+ * release trns semaphore
+ */
+ if (rc == RTEMS_SUCCESSFUL) {
+ rc = rtems_semaphore_release(softc_ptr->disp_param.trns_sema_id);
+ }
+
+ /*
+ * send event to task
+ */
+ if (rc == RTEMS_SUCCESSFUL) {
+ rc = rtems_event_send(softc_ptr->disp_param.task_id,
+ DISP_HCMS29XX_EVENT_NEWSTR);
+ }
+
+ return rc;
+}
+
+/*=========================================================================*\
+| Function: |
+\*-------------------------------------------------------------------------*/
+rtems_device_driver disp_hcms29xx_dev_initialize
+ (
+/*-------------------------------------------------------------------------*\
+| Purpose: |
+| prepare the display device driver to accept write calls |
+| register device with its name |
++---------------------------------------------------------------------------+
+| Input Parameters: |
+\*-------------------------------------------------------------------------*/
+ rtems_device_major_number major,
+ rtems_device_minor_number minor,
+ void *arg
+)
+/*-------------------------------------------------------------------------*\
+| Return Value: |
+| rtems_status_code |
+\*=========================================================================*/
+/*
+ * Initialize and register the device
+ */
+{
+ rtems_status_code rc = RTEMS_SUCCESSFUL;
+ disp_hcms29xx_drv_t *softc_ptr = &disp_hcms29xx_drv_tbl;
+
+ /*
+ * initialize font management
+ * FIXME: check, that default glyph exists
+ * FIXME: check font size to be 5x7
+ */
+ /*
+ * translate font according to direction/baseline
+ */
+ if (rc == RTEMS_SUCCESSFUL) {
+ rc = disp_hcms29xx_alloc_copy_font(
+ &FONT_BASE,
+ &disp_hcms29xx_font_normal,
+ FONT_BASE.descent, /* shift to visibility... */
+ FALSE); /* do not rotate */
+ }
+ /* FIXME: translate font for rotation */
+ if (rc == RTEMS_SUCCESSFUL) {
+ rc = disp_hcms29xx_alloc_copy_font(&FONT_BASE,
+ &disp_hcms29xx_font_rotate,
+ 0, /* do not shift */
+ true); /* rotate font */
+ }
+ /*
+ * create the trns_buffer semaphore
+ */
+ if (rc == RTEMS_SUCCESSFUL) {
+ rc = rtems_semaphore_create (DISP_HCMS29XX_TRNS_SEMA_NAME,1,
+ RTEMS_PRIORITY
+ |RTEMS_BINARY_SEMAPHORE
+ |RTEMS_INHERIT_PRIORITY
+ |RTEMS_NO_PRIORITY_CEILING
+ |RTEMS_LOCAL,
+ 0,
+ &softc_ptr->disp_param.trns_sema_id);
+ }
+
+ /*
+ * create and start display task
+ */
+ if (rc == RTEMS_SUCCESSFUL) {
+ rc = rtems_task_create(DISP_HCMS29XX_TASK_NAME,
+ 20,
+ RTEMS_MINIMUM_STACK_SIZE,
+ RTEMS_INTERRUPT_LEVEL(0) | RTEMS_TIMESLICE,
+ RTEMS_DEFAULT_ATTRIBUTES,
+ &softc_ptr->disp_param.task_id);
+ }
+ if (rc == RTEMS_SUCCESSFUL) {
+ rc = rtems_task_start(softc_ptr->disp_param.task_id,
+ disp_hcms29xx_update_task,0);
+ }
+ return rc;
+}
+
+/*=========================================================================*\
+| Function: |
+\*-------------------------------------------------------------------------*/
+rtems_device_driver disp_hcms29xx_dev_open
+(
+/*-------------------------------------------------------------------------*\
+| Purpose: |
+| open the display device |
++---------------------------------------------------------------------------+
+| Input Parameters: |
+\*-------------------------------------------------------------------------*/
+ rtems_device_major_number major,
+ rtems_device_minor_number minor,
+ void *arg
+)
+/*-------------------------------------------------------------------------*\
+| Return Value: |
+| rtems_status_code |
+\*=========================================================================*/
+{
+ disp_hcms29xx_drv_t *softc_ptr = &disp_hcms29xx_drv_tbl;
+ /*
+ * ensure, that disp_hcms29xx device is assumed to be empty
+ */
+ softc_ptr->disp_param.dev_buf_cnt = 0;
+
+ return RTEMS_SUCCESSFUL;
+}
+
+/*=========================================================================*\
+| Function: |
+\*-------------------------------------------------------------------------*/
+rtems_device_driver disp_hcms29xx_dev_write
+(
+/*-------------------------------------------------------------------------*\
+| Purpose: |
+| write to display device |
++---------------------------------------------------------------------------+
+| Input Parameters: |
+\*-------------------------------------------------------------------------*/
+ rtems_device_major_number major,
+ rtems_device_minor_number minor,
+ void *arg
+)
+/*-------------------------------------------------------------------------*\
+| Return Value: |
+| rtems_status_code |
+\*=========================================================================*/
+{
+ rtems_libio_rw_args_t *args = arg;
+ uint32_t cnt;
+ disp_hcms29xx_drv_t *softc_ptr = &disp_hcms29xx_drv_tbl;
+
+ for (cnt = 0;cnt < args->count;cnt++) {
+ /*
+ * accumulate characters written into display dev buffer
+ */
+ if (((softc_ptr->disp_param.dev_buf_cnt > 0)
+ &&((args->buffer[cnt] == '\n')
+ || (args->buffer[cnt] == '\0'))
+ )
+ ||( softc_ptr->disp_param.dev_buf_cnt >=
+ (int) sizeof(softc_ptr->disp_param.dev_buffer) - 1)) {
+ softc_ptr->disp_param.dev_buffer[softc_ptr->disp_param.dev_buf_cnt] = '\0';
+ /*
+ * transfer string to display string, redisplay it...
+ */
+ disp_hcms29xx_update(softc_ptr,softc_ptr->disp_param.dev_buffer);
+ softc_ptr->disp_param.dev_buf_cnt = 0;
+ }
+ /*
+ * write to dev_buf, if '\n' occured or display device buffer is full
+ */
+ if ((args->buffer[cnt] != '\n') &&
+ (args->buffer[cnt] != '\0')) {
+ softc_ptr->disp_param.dev_buffer[softc_ptr->disp_param.dev_buf_cnt++] =
+ args->buffer[cnt];
+ }
+ }
+ args->bytes_moved = args->count;
+
+ return RTEMS_SUCCESSFUL;
+}
+
+/*=========================================================================*\
+| Function: |
+\*-------------------------------------------------------------------------*/
+rtems_device_driver disp_hcms29xx_dev_close
+(
+/*-------------------------------------------------------------------------*\
+| Purpose: |
+| close the display device |
++---------------------------------------------------------------------------+
+| Input Parameters: |
+\*-------------------------------------------------------------------------*/
+ rtems_device_major_number major,
+ rtems_device_minor_number minor,
+ void *arg
+)
+/*-------------------------------------------------------------------------*\
+| Return Value: |
+| rtems_status_code |
+\*=========================================================================*/
+{
+
+ return RTEMS_SUCCESSFUL;
+}
+
+/*
+ * driver operation tables
+ */
+static rtems_driver_address_table disp_hcms29xx_ops = {
+ .initialization_entry = disp_hcms29xx_dev_initialize,
+ .open_entry = disp_hcms29xx_dev_open,
+ .write_entry = disp_hcms29xx_dev_write,
+ .close_entry = disp_hcms29xx_dev_close
+};
+
+
+static disp_hcms29xx_drv_t disp_hcms29xx_drv_tbl = {
+ {/* public fields */
+ .ops = &disp_hcms29xx_ops,
+ .size = sizeof (disp_hcms29xx_drv_t),
+ },
+ { /* our private fields */
+ 0,
+ { 0 },
+ 0,
+ { 0 },
+ { 0 },
+ 0,
+ 0,
+ 0,
+ false
+ }
+};
+
+rtems_libi2c_drv_t *disp_hcms29xx_driver_descriptor =
+ &disp_hcms29xx_drv_tbl.libi2c_drv_entry;
+
diff --git a/bsps/shared/dev/display/font_hcms29xx.c b/bsps/shared/dev/display/font_hcms29xx.c
new file mode 100644
index 0000000000..e25cca2eca
--- /dev/null
+++ b/bsps/shared/dev/display/font_hcms29xx.c
@@ -0,0 +1,1820 @@
+/*===============================================================*\
+| Project: display driver for HCMS29xx |
++-----------------------------------------------------------------+
+| File: font_hcms29xx.c |
++-----------------------------------------------------------------+
+| Copyright (c) 2008 |
+| Embedded Brains GmbH |
+| Obere Lagerstr. 30 |
+| D-82178 Puchheim |
+| Germany |
+| rtems@embedded-brains.de |
++-----------------------------------------------------------------+
+| The license and distribution terms for this file may be |
+| found in the file LICENSE in this distribution or at |
+| http://www.rtems.org/license/LICENSE. |
++-----------------------------------------------------------------+
+| This file defines the 5x7 bit font used in disp_hcms29xx |
+\*===============================================================*/
+
+#include <stddef.h>
+#include "disp_fonts.h"
+
+const unsigned char bitmap_hp_fixed_5_7_0 [5] = {
+ 0x08,
+ 0x1c,
+ 0x3e,
+ 0x7f,
+ 0x00
+};
+struct disp_font_glyph glyph_hp_fixed_5_7_0 = {
+ {5,7,0,0},
+ 5, 0, bitmap_hp_fixed_5_7_0,
+};
+
+const unsigned char bitmap_hp_fixed_5_7_1 [5] = {
+ 0x30,
+ 0x45,
+ 0x48,
+ 0x40,
+ 0x30
+};
+struct disp_font_glyph glyph_hp_fixed_5_7_1 = {
+ {5,7,0,0},
+ 5, 0, bitmap_hp_fixed_5_7_1,
+};
+
+const unsigned char bitmap_hp_fixed_5_7_2 [5] = {
+ 0x45,
+ 0x29,
+ 0x11,
+ 0x29,
+ 0x45
+};
+struct disp_font_glyph glyph_hp_fixed_5_7_2 = {
+ {5,7,0,0},
+ 5, 0, bitmap_hp_fixed_5_7_2,
+};
+
+const unsigned char bitmap_hp_fixed_5_7_3 [5] = {
+ 0x7d,
+ 0x09,
+ 0x11,
+ 0x21,
+ 0x7d
+};
+struct disp_font_glyph glyph_hp_fixed_5_7_3 = {
+ {5,7,0,0},
+ 5, 0, bitmap_hp_fixed_5_7_3,
+};
+
+const unsigned char bitmap_hp_fixed_5_7_4 [5] = {
+ 0x7d,
+ 0x09,
+ 0x05,
+ 0x05,
+ 0x79
+};
+struct disp_font_glyph glyph_hp_fixed_5_7_4 = {
+ {5,7,0,0},
+ 5, 0, bitmap_hp_fixed_5_7_4,
+};
+
+const unsigned char bitmap_hp_fixed_5_7_5 [5] = {
+ 0x38,
+ 0x44,
+ 0x44,
+ 0x38,
+ 0x44
+};
+struct disp_font_glyph glyph_hp_fixed_5_7_5 = {
+ {5,7,0,0},
+ 5, 0, bitmap_hp_fixed_5_7_5,
+};
+
+const unsigned char bitmap_hp_fixed_5_7_6 [5] = {
+ 0x7e,
+ 0x01,
+ 0x29,
+ 0x2e,
+ 0x10
+};
+struct disp_font_glyph glyph_hp_fixed_5_7_6 = {
+ {5,7,0,0},
+ 5, 0, bitmap_hp_fixed_5_7_6,
+};
+
+const unsigned char bitmap_hp_fixed_5_7_7 [5] = {
+ 0x30,
+ 0x4a,
+ 0x4d,
+ 0x49,
+ 0x30
+};
+struct disp_font_glyph glyph_hp_fixed_5_7_7 = {
+ {5,7,0,0},
+ 5, 0, bitmap_hp_fixed_5_7_7,
+};
+
+const unsigned char bitmap_hp_fixed_5_7_8 [5] = {
+ 0x60,
+ 0x50,
+ 0x48,
+ 0x50,
+ 0x60
+};
+struct disp_font_glyph glyph_hp_fixed_5_7_8 = {
+ {5,7,0,0},
+ 5, 0, bitmap_hp_fixed_5_7_8,
+};
+
+const unsigned char bitmap_hp_fixed_5_7_9 [5] = {
+ 0x1e,
+ 0x04,
+ 0x04,
+ 0x38,
+ 0x40
+};
+struct disp_font_glyph glyph_hp_fixed_5_7_9 = {
+ {5,7,0,0},
+ 5, 0, bitmap_hp_fixed_5_7_9,
+};
+
+const unsigned char bitmap_hp_fixed_5_7_10 [5] = {
+ 0x3e,
+ 0x49,
+ 0x49,
+ 0x49,
+ 0x3e
+};
+struct disp_font_glyph glyph_hp_fixed_5_7_10 = {
+ {5,7,0,0},
+ 5, 0, bitmap_hp_fixed_5_7_10,
+};
+
+const unsigned char bitmap_hp_fixed_5_7_11 [5] = {
+ 0x62,
+ 0x14,
+ 0x08,
+ 0x10,
+ 0x60
+};
+struct disp_font_glyph glyph_hp_fixed_5_7_11 = {
+ {5,7,0,0},
+ 5, 0, bitmap_hp_fixed_5_7_11,
+};
+
+const unsigned char bitmap_hp_fixed_5_7_12 [5] = {
+ 0x40,
+ 0x3c,
+ 0x20,
+ 0x20,
+ 0x1c
+};
+struct disp_font_glyph glyph_hp_fixed_5_7_12 = {
+ {5,7,0,0},
+ 5, 0, bitmap_hp_fixed_5_7_12,
+};
+
+const unsigned char bitmap_hp_fixed_5_7_13 [5] = {
+ 0x08,
+ 0x7c,
+ 0x04,
+ 0x7c,
+ 0x02
+};
+struct disp_font_glyph glyph_hp_fixed_5_7_13 = {
+ {5,7,0,0},
+ 5, 0, bitmap_hp_fixed_5_7_13,
+};
+
+const unsigned char bitmap_hp_fixed_5_7_14 [5] = {
+ 0x38,
+ 0x44,
+ 0x44,
+ 0x3c,
+ 0x04
+};
+struct disp_font_glyph glyph_hp_fixed_5_7_14 = {
+ {5,7,0,0},
+ 5, 0, bitmap_hp_fixed_5_7_14,
+};
+
+const unsigned char bitmap_hp_fixed_5_7_15 [5] = {
+ 0x41,
+ 0x63,
+ 0x55,
+ 0x49,
+ 0x41
+};
+struct disp_font_glyph glyph_hp_fixed_5_7_15 = {
+ {5,7,0,0},
+ 5, 0, bitmap_hp_fixed_5_7_15,
+};
+
+const unsigned char bitmap_hp_fixed_5_7_16 [5] = {
+ 0x10,
+ 0x08,
+ 0x78,
+ 0x08,
+ 0x04
+};
+struct disp_font_glyph glyph_hp_fixed_5_7_16 = {
+ {5,7,0,0},
+ 5, 0, bitmap_hp_fixed_5_7_16,
+};
+
+const unsigned char bitmap_hp_fixed_5_7_17 [5] = {
+ 0x18,
+ 0x24,
+ 0x7e,
+ 0x24,
+ 0x18
+};
+struct disp_font_glyph glyph_hp_fixed_5_7_17 = {
+ {5,7,0,0},
+ 5, 0, bitmap_hp_fixed_5_7_17,
+};
+
+const unsigned char bitmap_hp_fixed_5_7_18 [5] = {
+ 0x5e,
+ 0x61,
+ 0x01,
+ 0x61,
+ 0x5e
+};
+struct disp_font_glyph glyph_hp_fixed_5_7_18 = {
+ {5,7,0,0},
+ 5, 0, bitmap_hp_fixed_5_7_18,
+};
+
+const unsigned char bitmap_hp_fixed_5_7_19 [5] = {
+ 0x78,
+ 0x14,
+ 0x15,
+ 0x14,
+ 0x78
+};
+struct disp_font_glyph glyph_hp_fixed_5_7_19 = {
+ {5,7,0,0},
+ 5, 0, bitmap_hp_fixed_5_7_19,
+};
+
+const unsigned char bitmap_hp_fixed_5_7_20 [5] = {
+ 0x38,
+ 0x44,
+ 0x45,
+ 0x3c,
+ 0x40
+};
+struct disp_font_glyph glyph_hp_fixed_5_7_20 = {
+ {5,7,0,0},
+ 5, 0, bitmap_hp_fixed_5_7_20,
+};
+
+const unsigned char bitmap_hp_fixed_5_7_21 [5] = {
+ 0x78,
+ 0x15,
+ 0x14,
+ 0x15,
+ 0x78
+};
+struct disp_font_glyph glyph_hp_fixed_5_7_21 = {
+ {5,7,0,0},
+ 5, 0, bitmap_hp_fixed_5_7_21,
+};
+
+const unsigned char bitmap_hp_fixed_5_7_22 [5] = {
+ 0x38,
+ 0x45,
+ 0x44,
+ 0x3d,
+ 0x40
+};
+struct disp_font_glyph glyph_hp_fixed_5_7_22 = {
+ {5,7,0,0},
+ 5, 0, bitmap_hp_fixed_5_7_22,
+};
+
+const unsigned char bitmap_hp_fixed_5_7_23 [5] = {
+ 0x3c,
+ 0x43,
+ 0x42,
+ 0x43,
+ 0x3c
+};
+struct disp_font_glyph glyph_hp_fixed_5_7_23 = {
+ {5,7,0,0},
+ 5, 0, bitmap_hp_fixed_5_7_23,
+};
+
+const unsigned char bitmap_hp_fixed_5_7_24 [5] = {
+ 0x38,
+ 0x45,
+ 0x44,
+ 0x45,
+ 0x38
+};
+struct disp_font_glyph glyph_hp_fixed_5_7_24 = {
+ {5,7,0,0},
+ 5, 0, bitmap_hp_fixed_5_7_24,
+};
+
+const unsigned char bitmap_hp_fixed_5_7_25 [5] = {
+ 0x3c,
+ 0x41,
+ 0x40,
+ 0x41,
+ 0x3c
+};
+struct disp_font_glyph glyph_hp_fixed_5_7_25 = {
+ {5,7,0,0},
+ 5, 0, bitmap_hp_fixed_5_7_25,
+};
+
+const unsigned char bitmap_hp_fixed_5_7_26 [5] = {
+ 0x38,
+ 0x42,
+ 0x40,
+ 0x42,
+ 0x38
+};
+struct disp_font_glyph glyph_hp_fixed_5_7_26 = {
+ {5,7,0,0},
+ 5, 0, bitmap_hp_fixed_5_7_26,
+};
+
+const unsigned char bitmap_hp_fixed_5_7_27 [5] = {
+ 0x08,
+ 0x08,
+ 0x2a,
+ 0x1c,
+ 0x08
+};
+struct disp_font_glyph glyph_hp_fixed_5_7_27 = {
+ {5,7,0,0},
+ 5, 0, bitmap_hp_fixed_5_7_27,
+};
+
+const unsigned char bitmap_hp_fixed_5_7_28 [5] = {
+ 0x20,
+ 0x7e,
+ 0x02,
+ 0x02,
+ 0x02
+};
+struct disp_font_glyph glyph_hp_fixed_5_7_28 = {
+ {5,7,0,0},
+ 5, 0, bitmap_hp_fixed_5_7_28,
+};
+
+const unsigned char bitmap_hp_fixed_5_7_29 [5] = {
+ 0x12,
+ 0x19,
+ 0x15,
+ 0x12,
+ 0x00
+};
+struct disp_font_glyph glyph_hp_fixed_5_7_29 = {
+ {5,7,0,0},
+ 5, 0, bitmap_hp_fixed_5_7_29,
+};
+
+const unsigned char bitmap_hp_fixed_5_7_30 [5] = {
+ 0x48,
+ 0x7e,
+ 0x49,
+ 0x41,
+ 0x42
+};
+struct disp_font_glyph glyph_hp_fixed_5_7_30 = {
+ {5,7,0,0},
+ 5, 0, bitmap_hp_fixed_5_7_30,
+};
+
+const unsigned char bitmap_hp_fixed_5_7_31 [5] = {
+ 0x01,
+ 0x12,
+ 0x7c,
+ 0x12,
+ 0x01
+};
+struct disp_font_glyph glyph_hp_fixed_5_7_31 = {
+ {5,7,0,0},
+ 5, 0, bitmap_hp_fixed_5_7_31,
+};
+
+const unsigned char bitmap_hp_fixed_5_7_32 [5] = {
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00
+};
+struct disp_font_glyph glyph_hp_fixed_5_7_32 = {
+ {5,7,0,0},
+ 5, 0, bitmap_hp_fixed_5_7_32,
+};
+
+const unsigned char bitmap_hp_fixed_5_7_33 [5] = {
+ 0x00,
+ 0x5f,
+ 0x00,
+ 0x00,
+ 0x00
+};
+struct disp_font_glyph glyph_hp_fixed_5_7_33 = {
+ {5,7,0,0},
+ 5, 0, bitmap_hp_fixed_5_7_33,
+};
+
+const unsigned char bitmap_hp_fixed_5_7_34 [5] = {
+ 0x00,
+ 0x03,
+ 0x00,
+ 0x03,
+ 0x00
+};
+struct disp_font_glyph glyph_hp_fixed_5_7_34 = {
+ {5,7,0,0},
+ 5, 0, bitmap_hp_fixed_5_7_34,
+};
+
+const unsigned char bitmap_hp_fixed_5_7_35 [5] = {
+ 0x14,
+ 0x7f,
+ 0x14,
+ 0x7f,
+ 0x14
+};
+struct disp_font_glyph glyph_hp_fixed_5_7_35 = {
+ {5,7,0,0},
+ 5, 0, bitmap_hp_fixed_5_7_35,
+};
+
+const unsigned char bitmap_hp_fixed_5_7_36 [5] = {
+ 0x24,
+ 0x2a,
+ 0x7f,
+ 0x2a,
+ 0x12
+};
+struct disp_font_glyph glyph_hp_fixed_5_7_36 = {
+ {5,7,0,0},
+ 5, 0, bitmap_hp_fixed_5_7_36,
+};
+
+const unsigned char bitmap_hp_fixed_5_7_37 [5] = {
+ 0x23,
+ 0x13,
+ 0x08,
+ 0x64,
+ 0x62
+};
+struct disp_font_glyph glyph_hp_fixed_5_7_37 = {
+ {5,7,0,0},
+ 5, 0, bitmap_hp_fixed_5_7_37,
+};
+
+const unsigned char bitmap_hp_fixed_5_7_38 [5] = {
+ 0x36,
+ 0x49,
+ 0x56,
+ 0x20,
+ 0x50
+};
+struct disp_font_glyph glyph_hp_fixed_5_7_38 = {
+ {5,7,0,0},
+ 5, 0, bitmap_hp_fixed_5_7_38,
+};
+
+const unsigned char bitmap_hp_fixed_5_7_39 [5] = {
+ 0x00,
+ 0x0b,
+ 0x07,
+ 0x00,
+ 0x00
+};
+struct disp_font_glyph glyph_hp_fixed_5_7_39 = {
+ {5,7,0,0},
+ 5, 0, bitmap_hp_fixed_5_7_39,
+};
+
+const unsigned char bitmap_hp_fixed_5_7_40 [5] = {
+ 0x00,
+ 0x00,
+ 0x3e,
+ 0x41,
+ 0x00
+};
+struct disp_font_glyph glyph_hp_fixed_5_7_40 = {
+ {5,7,0,0},
+ 5, 0, bitmap_hp_fixed_5_7_40,
+};
+
+const unsigned char bitmap_hp_fixed_5_7_41 [5] = {
+ 0x00,
+ 0x41,
+ 0x3e,
+ 0x00,
+ 0x00
+};
+struct disp_font_glyph glyph_hp_fixed_5_7_41 = {
+ {5,7,0,0},
+ 5, 0, bitmap_hp_fixed_5_7_41,
+};
+
+const unsigned char bitmap_hp_fixed_5_7_42 [5] = {
+ 0x08,
+ 0x2a,
+ 0x1c,
+ 0x2a,
+ 0x08
+};
+struct disp_font_glyph glyph_hp_fixed_5_7_42 = {
+ {5,7,0,0},
+ 5, 0, bitmap_hp_fixed_5_7_42,
+};
+
+const unsigned char bitmap_hp_fixed_5_7_43 [5] = {
+ 0x08,
+ 0x08,
+ 0x3e,
+ 0x08,
+ 0x08
+};
+struct disp_font_glyph glyph_hp_fixed_5_7_43 = {
+ {5,7,0,0},
+ 5, 0, bitmap_hp_fixed_5_7_43,
+};
+
+const unsigned char bitmap_hp_fixed_5_7_44 [5] = {
+ 0x00,
+ 0x58,
+ 0x38,
+ 0x00,
+ 0x00
+};
+struct disp_font_glyph glyph_hp_fixed_5_7_44 = {
+ {5,7,0,0},
+ 5, 0, bitmap_hp_fixed_5_7_44,
+};
+
+const unsigned char bitmap_hp_fixed_5_7_45 [5] = {
+ 0x08,
+ 0x08,
+ 0x08,
+ 0x08,
+ 0x08
+};
+struct disp_font_glyph glyph_hp_fixed_5_7_45 = {
+ {5,7,0,0},
+ 5, 0, bitmap_hp_fixed_5_7_45,
+};
+
+const unsigned char bitmap_hp_fixed_5_7_46 [5] = {
+ 0x00,
+ 0x30,
+ 0x30,
+ 0x00,
+ 0x00
+};
+struct disp_font_glyph glyph_hp_fixed_5_7_46 = {
+ {5,7,0,0},
+ 5, 0, bitmap_hp_fixed_5_7_46,
+};
+
+const unsigned char bitmap_hp_fixed_5_7_47 [5] = {
+ 0x20,
+ 0x10,
+ 0x08,
+ 0x04,
+ 0x02
+};
+struct disp_font_glyph glyph_hp_fixed_5_7_47 = {
+ {5,7,0,0},
+ 5, 0, bitmap_hp_fixed_5_7_47,
+};
+
+const unsigned char bitmap_hp_fixed_5_7_48 [5] = {
+ 0x3e,
+ 0x51,
+ 0x49,
+ 0x45,
+ 0x3e
+};
+struct disp_font_glyph glyph_hp_fixed_5_7_48 = {
+ {5,7,0,0},
+ 5, 0, bitmap_hp_fixed_5_7_48,
+};
+
+const unsigned char bitmap_hp_fixed_5_7_49 [5] = {
+ 0x00,
+ 0x42,
+ 0x7f,
+ 0x40,
+ 0x00
+};
+struct disp_font_glyph glyph_hp_fixed_5_7_49 = {
+ {5,7,0,0},
+ 5, 0, bitmap_hp_fixed_5_7_49,
+};
+
+const unsigned char bitmap_hp_fixed_5_7_50 [5] = {
+ 0x62,
+ 0x51,
+ 0x49,
+ 0x49,
+ 0x46
+};
+struct disp_font_glyph glyph_hp_fixed_5_7_50 = {
+ {5,7,0,0},
+ 5, 0, bitmap_hp_fixed_5_7_50,
+};
+
+const unsigned char bitmap_hp_fixed_5_7_51 [5] = {
+ 0x22,
+ 0x41,
+ 0x49,
+ 0x49,
+ 0x36
+};
+struct disp_font_glyph glyph_hp_fixed_5_7_51 = {
+ {5,7,0,0},
+ 5, 0, bitmap_hp_fixed_5_7_51,
+};
+
+const unsigned char bitmap_hp_fixed_5_7_52 [5] = {
+ 0x18,
+ 0x14,
+ 0x12,
+ 0x7f,
+ 0x10
+};
+struct disp_font_glyph glyph_hp_fixed_5_7_52 = {
+ {5,7,0,0},
+ 5, 0, bitmap_hp_fixed_5_7_52,
+};
+
+const unsigned char bitmap_hp_fixed_5_7_53 [5] = {
+ 0x27,
+ 0x45,
+ 0x45,
+ 0x45,
+ 0x39
+};
+struct disp_font_glyph glyph_hp_fixed_5_7_53 = {
+ {5,7,0,0},
+ 5, 0, bitmap_hp_fixed_5_7_53,
+};
+
+const unsigned char bitmap_hp_fixed_5_7_54 [5] = {
+ 0x3c,
+ 0x4a,
+ 0x49,
+ 0x49,
+ 0x30
+};
+struct disp_font_glyph glyph_hp_fixed_5_7_54 = {
+ {5,7,0,0},
+ 5, 0, bitmap_hp_fixed_5_7_54,
+};
+
+const unsigned char bitmap_hp_fixed_5_7_55 [5] = {
+ 0x01,
+ 0x71,
+ 0x09,
+ 0x05,
+ 0x03
+};
+struct disp_font_glyph glyph_hp_fixed_5_7_55 = {
+ {5,7,0,0},
+ 5, 0, bitmap_hp_fixed_5_7_55,
+};
+
+const unsigned char bitmap_hp_fixed_5_7_56 [5] = {
+ 0x36,
+ 0x49,
+ 0x49,
+ 0x49,
+ 0x36
+};
+struct disp_font_glyph glyph_hp_fixed_5_7_56 = {
+ {5,7,0,0},
+ 5, 0, bitmap_hp_fixed_5_7_56,
+};
+
+const unsigned char bitmap_hp_fixed_5_7_57 [5] = {
+ 0x06,
+ 0x49,
+ 0x49,
+ 0x29,
+ 0x1e
+};
+struct disp_font_glyph glyph_hp_fixed_5_7_57 = {
+ {5,7,0,0},
+ 5, 0, bitmap_hp_fixed_5_7_57,
+};
+
+const unsigned char bitmap_hp_fixed_5_7_58 [5] = {
+ 0x00,
+ 0x36,
+ 0x36,
+ 0x00,
+ 0x00
+};
+struct disp_font_glyph glyph_hp_fixed_5_7_58 = {
+ {5,7,0,0},
+ 5, 0, bitmap_hp_fixed_5_7_58,
+};
+
+const unsigned char bitmap_hp_fixed_5_7_59 [5] = {
+ 0x00,
+ 0x5b,
+ 0x3b,
+ 0x00,
+ 0x00
+};
+struct disp_font_glyph glyph_hp_fixed_5_7_59 = {
+ {5,7,0,0},
+ 5, 0, bitmap_hp_fixed_5_7_59,
+};
+
+const unsigned char bitmap_hp_fixed_5_7_60 [5] = {
+ 0x00,
+ 0x08,
+ 0x14,
+ 0x22,
+ 0x41
+};
+struct disp_font_glyph glyph_hp_fixed_5_7_60 = {
+ {5,7,0,0},
+ 5, 0, bitmap_hp_fixed_5_7_60,
+};
+
+const unsigned char bitmap_hp_fixed_5_7_61 [5] = {
+ 0x14,
+ 0x14,
+ 0x14,
+ 0x14,
+ 0x14
+};
+struct disp_font_glyph glyph_hp_fixed_5_7_61 = {
+ {5,7,0,0},
+ 5, 0, bitmap_hp_fixed_5_7_61,
+};
+
+const unsigned char bitmap_hp_fixed_5_7_62 [5] = {
+ 0x41,
+ 0x22,
+ 0x14,
+ 0x08,
+ 0x00
+};
+struct disp_font_glyph glyph_hp_fixed_5_7_62 = {
+ {5,7,0,0},
+ 5, 0, bitmap_hp_fixed_5_7_62,
+};
+
+const unsigned char bitmap_hp_fixed_5_7_63 [5] = {
+ 0x02,
+ 0x01,
+ 0x51,
+ 0x09,
+ 0x06
+};
+struct disp_font_glyph glyph_hp_fixed_5_7_63 = {
+ {5,7,0,0},
+ 5, 0, bitmap_hp_fixed_5_7_63,
+};
+
+const unsigned char bitmap_hp_fixed_5_7_64 [5] = {
+ 0x3e,
+ 0x41,
+ 0x5d,
+ 0x55,
+ 0x1e
+};
+struct disp_font_glyph glyph_hp_fixed_5_7_64 = {
+ {5,7,0,0},
+ 5, 0, bitmap_hp_fixed_5_7_64,
+};
+
+const unsigned char bitmap_hp_fixed_5_7_65 [5] = {
+ 0x7e,
+ 0x09,
+ 0x09,
+ 0x09,
+ 0x7e
+};
+struct disp_font_glyph glyph_hp_fixed_5_7_65 = {
+ {5,7,0,0},
+ 5, 0, bitmap_hp_fixed_5_7_65,
+};
+
+const unsigned char bitmap_hp_fixed_5_7_66 [5] = {
+ 0x7e,
+ 0x49,
+ 0x49,
+ 0x49,
+ 0x36
+};
+struct disp_font_glyph glyph_hp_fixed_5_7_66 = {
+ {5,7,0,0},
+ 5, 0, bitmap_hp_fixed_5_7_66,
+};
+
+const unsigned char bitmap_hp_fixed_5_7_67 [5] = {
+ 0x3e,
+ 0x41,
+ 0x41,
+ 0x41,
+ 0x22
+};
+struct disp_font_glyph glyph_hp_fixed_5_7_67 = {
+ {5,7,0,0},
+ 5, 0, bitmap_hp_fixed_5_7_67,
+};
+
+const unsigned char bitmap_hp_fixed_5_7_68 [5] = {
+ 0x7f,
+ 0x41,
+ 0x41,
+ 0x41,
+ 0x3e
+};
+struct disp_font_glyph glyph_hp_fixed_5_7_68 = {
+ {5,7,0,0},
+ 5, 0, bitmap_hp_fixed_5_7_68,
+};
+
+const unsigned char bitmap_hp_fixed_5_7_69 [5] = {
+ 0x7f,
+ 0x49,
+ 0x49,
+ 0x49,
+ 0x41
+};
+struct disp_font_glyph glyph_hp_fixed_5_7_69 = {
+ {5,7,0,0},
+ 5, 0, bitmap_hp_fixed_5_7_69,
+};
+
+const unsigned char bitmap_hp_fixed_5_7_70 [5] = {
+ 0x7f,
+ 0x09,
+ 0x09,
+ 0x09,
+ 0x01
+};
+struct disp_font_glyph glyph_hp_fixed_5_7_70 = {
+ {5,7,0,0},
+ 5, 0, bitmap_hp_fixed_5_7_70,
+};
+
+const unsigned char bitmap_hp_fixed_5_7_71 [5] = {
+ 0x3e,
+ 0x41,
+ 0x41,
+ 0x51,
+ 0x32
+};
+struct disp_font_glyph glyph_hp_fixed_5_7_71 = {
+ {5,7,0,0},
+ 5, 0, bitmap_hp_fixed_5_7_71,
+};
+
+const unsigned char bitmap_hp_fixed_5_7_72 [5] = {
+ 0x7f,
+ 0x08,
+ 0x08,
+ 0x08,
+ 0x7f
+};
+struct disp_font_glyph glyph_hp_fixed_5_7_72 = {
+ {5,7,0,0},
+ 5, 0, bitmap_hp_fixed_5_7_72,
+};
+
+const unsigned char bitmap_hp_fixed_5_7_73 [5] = {
+ 0x00,
+ 0x41,
+ 0x7f,
+ 0x41,
+ 0x00
+};
+struct disp_font_glyph glyph_hp_fixed_5_7_73 = {
+ {5,7,0,0},
+ 5, 0, bitmap_hp_fixed_5_7_73,
+};
+
+const unsigned char bitmap_hp_fixed_5_7_74 [5] = {
+ 0x20,
+ 0x40,
+ 0x40,
+ 0x40,
+ 0x3f
+};
+struct disp_font_glyph glyph_hp_fixed_5_7_74 = {
+ {5,7,0,0},
+ 5, 0, bitmap_hp_fixed_5_7_74,
+};
+
+const unsigned char bitmap_hp_fixed_5_7_75 [5] = {
+ 0x7f,
+ 0x08,
+ 0x14,
+ 0x22,
+ 0x41
+};
+struct disp_font_glyph glyph_hp_fixed_5_7_75 = {
+ {5,7,0,0},
+ 5, 0, bitmap_hp_fixed_5_7_75,
+};
+
+const unsigned char bitmap_hp_fixed_5_7_76 [5] = {
+ 0x7f,
+ 0x40,
+ 0x40,
+ 0x40,
+ 0x40
+};
+struct disp_font_glyph glyph_hp_fixed_5_7_76 = {
+ {5,7,0,0},
+ 5, 0, bitmap_hp_fixed_5_7_76,
+};
+
+const unsigned char bitmap_hp_fixed_5_7_77 [5] = {
+ 0x7f,
+ 0x02,
+ 0x0c,
+ 0x02,
+ 0x7f
+};
+struct disp_font_glyph glyph_hp_fixed_5_7_77 = {
+ {5,7,0,0},
+ 5, 0, bitmap_hp_fixed_5_7_77,
+};
+
+const unsigned char bitmap_hp_fixed_5_7_78 [5] = {
+ 0x7f,
+ 0x04,
+ 0x08,
+ 0x10,
+ 0x7f
+};
+struct disp_font_glyph glyph_hp_fixed_5_7_78 = {
+ {5,7,0,0},
+ 5, 0, bitmap_hp_fixed_5_7_78,
+};
+
+const unsigned char bitmap_hp_fixed_5_7_79 [5] = {
+ 0x3e,
+ 0x41,
+ 0x41,
+ 0x41,
+ 0x3e
+};
+struct disp_font_glyph glyph_hp_fixed_5_7_79 = {
+ {5,7,0,0},
+ 5, 0, bitmap_hp_fixed_5_7_79,
+};
+
+const unsigned char bitmap_hp_fixed_5_7_80 [5] = {
+ 0x7f,
+ 0x09,
+ 0x09,
+ 0x09,
+ 0x06
+};
+struct disp_font_glyph glyph_hp_fixed_5_7_80 = {
+ {5,7,0,0},
+ 5, 0, bitmap_hp_fixed_5_7_80,
+};
+
+const unsigned char bitmap_hp_fixed_5_7_81 [5] = {
+ 0x3e,
+ 0x41,
+ 0x51,
+ 0x21,
+ 0x5e
+};
+struct disp_font_glyph glyph_hp_fixed_5_7_81 = {
+ {5,7,0,0},
+ 5, 0, bitmap_hp_fixed_5_7_81,
+};
+
+const unsigned char bitmap_hp_fixed_5_7_82 [5] = {
+ 0x7f,
+ 0x09,
+ 0x19,
+ 0x29,
+ 0x46
+};
+struct disp_font_glyph glyph_hp_fixed_5_7_82 = {
+ {5,7,0,0},
+ 5, 0, bitmap_hp_fixed_5_7_82,
+};
+
+const unsigned char bitmap_hp_fixed_5_7_83 [5] = {
+ 0x26,
+ 0x49,
+ 0x49,
+ 0x49,
+ 0x32
+};
+struct disp_font_glyph glyph_hp_fixed_5_7_83 = {
+ {5,7,0,0},
+ 5, 0, bitmap_hp_fixed_5_7_83,
+};
+
+const unsigned char bitmap_hp_fixed_5_7_84 [5] = {
+ 0x01,
+ 0x01,
+ 0x7f,
+ 0x01,
+ 0x01
+};
+struct disp_font_glyph glyph_hp_fixed_5_7_84 = {
+ {5,7,0,0},
+ 5, 0, bitmap_hp_fixed_5_7_84,
+};
+
+const unsigned char bitmap_hp_fixed_5_7_85 [5] = {
+ 0x3f,
+ 0x40,
+ 0x40,
+ 0x40,
+ 0x3f
+};
+struct disp_font_glyph glyph_hp_fixed_5_7_85 = {
+ {5,7,0,0},
+ 5, 0, bitmap_hp_fixed_5_7_85,
+};
+
+const unsigned char bitmap_hp_fixed_5_7_86 [5] = {
+ 0x07,
+ 0x18,
+ 0x60,
+ 0x18,
+ 0x07
+};
+struct disp_font_glyph glyph_hp_fixed_5_7_86 = {
+ {5,7,0,0},
+ 5, 0, bitmap_hp_fixed_5_7_86,
+};
+
+const unsigned char bitmap_hp_fixed_5_7_87 [5] = {
+ 0x7f,
+ 0x20,
+ 0x18,
+ 0x20,
+ 0x7f
+};
+struct disp_font_glyph glyph_hp_fixed_5_7_87 = {
+ {5,7,0,0},
+ 5, 0, bitmap_hp_fixed_5_7_87,
+};
+
+const unsigned char bitmap_hp_fixed_5_7_88 [5] = {
+ 0x63,
+ 0x14,
+ 0x08,
+ 0x14,
+ 0x63
+};
+struct disp_font_glyph glyph_hp_fixed_5_7_88 = {
+ {5,7,0,0},
+ 5, 0, bitmap_hp_fixed_5_7_88,
+};
+
+const unsigned char bitmap_hp_fixed_5_7_89 [5] = {
+ 0x03,
+ 0x04,
+ 0x78,
+ 0x04,
+ 0x03
+};
+struct disp_font_glyph glyph_hp_fixed_5_7_89 = {
+ {5,7,0,0},
+ 5, 0, bitmap_hp_fixed_5_7_89,
+};
+
+const unsigned char bitmap_hp_fixed_5_7_90 [5] = {
+ 0x61,
+ 0x51,
+ 0x49,
+ 0x45,
+ 0x43
+};
+struct disp_font_glyph glyph_hp_fixed_5_7_90 = {
+ {5,7,0,0},
+ 5, 0, bitmap_hp_fixed_5_7_90,
+};
+
+const unsigned char bitmap_hp_fixed_5_7_91 [5] = {
+ 0x00,
+ 0x00,
+ 0x7f,
+ 0x41,
+ 0x41
+};
+struct disp_font_glyph glyph_hp_fixed_5_7_91 = {
+ {5,7,0,0},
+ 5, 0, bitmap_hp_fixed_5_7_91,
+};
+
+const unsigned char bitmap_hp_fixed_5_7_92 [5] = {
+ 0x02,
+ 0x04,
+ 0x08,
+ 0x10,
+ 0x20
+};
+struct disp_font_glyph glyph_hp_fixed_5_7_92 = {
+ {5,7,0,0},
+ 5, 0, bitmap_hp_fixed_5_7_92,
+};
+
+const unsigned char bitmap_hp_fixed_5_7_93 [5] = {
+ 0x41,
+ 0x41,
+ 0x7f,
+ 0x00,
+ 0x00
+};
+struct disp_font_glyph glyph_hp_fixed_5_7_93 = {
+ {5,7,0,0},
+ 5, 0, bitmap_hp_fixed_5_7_93,
+};
+
+const unsigned char bitmap_hp_fixed_5_7_94 [5] = {
+ 0x04,
+ 0x02,
+ 0x7f,
+ 0x02,
+ 0x04
+};
+struct disp_font_glyph glyph_hp_fixed_5_7_94 = {
+ {5,7,0,0},
+ 5, 0, bitmap_hp_fixed_5_7_94,
+};
+
+const unsigned char bitmap_hp_fixed_5_7_95 [5] = {
+ 0x40,
+ 0x40,
+ 0x40,
+ 0x40,
+ 0x40
+};
+struct disp_font_glyph glyph_hp_fixed_5_7_95 = {
+ {5,7,0,0},
+ 5, 0, bitmap_hp_fixed_5_7_95,
+};
+
+const unsigned char bitmap_hp_fixed_5_7_96 [5] = {
+ 0x00,
+ 0x07,
+ 0x0b,
+ 0x00,
+ 0x00
+};
+struct disp_font_glyph glyph_hp_fixed_5_7_96 = {
+ {5,7,0,0},
+ 5, 0, bitmap_hp_fixed_5_7_96,
+};
+
+const unsigned char bitmap_hp_fixed_5_7_97 [5] = {
+ 0x38,
+ 0x44,
+ 0x44,
+ 0x3c,
+ 0x40
+};
+struct disp_font_glyph glyph_hp_fixed_5_7_97 = {
+ {5,7,0,0},
+ 5, 0, bitmap_hp_fixed_5_7_97,
+};
+
+const unsigned char bitmap_hp_fixed_5_7_98 [5] = {
+ 0x7f,
+ 0x48,
+ 0x44,
+ 0x44,
+ 0x38
+};
+struct disp_font_glyph glyph_hp_fixed_5_7_98 = {
+ {5,7,0,0},
+ 5, 0, bitmap_hp_fixed_5_7_98,
+};
+
+const unsigned char bitmap_hp_fixed_5_7_99 [5] = {
+ 0x38,
+ 0x44,
+ 0x44,
+ 0x44,
+ 0x44
+};
+struct disp_font_glyph glyph_hp_fixed_5_7_99 = {
+ {5,7,0,0},
+ 5, 0, bitmap_hp_fixed_5_7_99,
+};
+
+const unsigned char bitmap_hp_fixed_5_7_100 [5] = {
+ 0x38,
+ 0x44,
+ 0x44,
+ 0x48,
+ 0x7f
+};
+struct disp_font_glyph glyph_hp_fixed_5_7_100 = {
+ {5,7,0,0},
+ 5, 0, bitmap_hp_fixed_5_7_100,
+};
+
+const unsigned char bitmap_hp_fixed_5_7_101 [5] = {
+ 0x38,
+ 0x54,
+ 0x54,
+ 0x54,
+ 0x08
+};
+struct disp_font_glyph glyph_hp_fixed_5_7_101 = {
+ {5,7,0,0},
+ 5, 0, bitmap_hp_fixed_5_7_101,
+};
+
+const unsigned char bitmap_hp_fixed_5_7_102 [5] = {
+ 0x08,
+ 0x7e,
+ 0x09,
+ 0x02,
+ 0x00
+};
+struct disp_font_glyph glyph_hp_fixed_5_7_102 = {
+ {5,7,0,0},
+ 5, 0, bitmap_hp_fixed_5_7_102,
+};
+
+const unsigned char bitmap_hp_fixed_5_7_103 [5] = {
+ 0x08,
+ 0x14,
+ 0x54,
+ 0x54,
+ 0x3c
+};
+struct disp_font_glyph glyph_hp_fixed_5_7_103 = {
+ {5,7,0,0},
+ 5, 0, bitmap_hp_fixed_5_7_103,
+};
+
+const unsigned char bitmap_hp_fixed_5_7_104 [5] = {
+ 0x7f,
+ 0x08,
+ 0x04,
+ 0x04,
+ 0x78
+};
+struct disp_font_glyph glyph_hp_fixed_5_7_104 = {
+ {5,7,0,0},
+ 5, 0, bitmap_hp_fixed_5_7_104,
+};
+
+const unsigned char bitmap_hp_fixed_5_7_105 [5] = {
+ 0x00,
+ 0x44,
+ 0x7d,
+ 0x40,
+ 0x00
+};
+struct disp_font_glyph glyph_hp_fixed_5_7_105 = {
+ {5,7,0,0},
+ 5, 0, bitmap_hp_fixed_5_7_105,
+};
+
+const unsigned char bitmap_hp_fixed_5_7_106 [5] = {
+ 0x20,
+ 0x40,
+ 0x44,
+ 0x3d,
+ 0x00
+};
+struct disp_font_glyph glyph_hp_fixed_5_7_106 = {
+ {5,7,0,0},
+ 5, 0, bitmap_hp_fixed_5_7_106,
+};
+
+const unsigned char bitmap_hp_fixed_5_7_107 [5] = {
+ 0x00,
+ 0x7f,
+ 0x10,
+ 0x28,
+ 0x44
+};
+struct disp_font_glyph glyph_hp_fixed_5_7_107 = {
+ {5,7,0,0},
+ 5, 0, bitmap_hp_fixed_5_7_107,
+};
+
+const unsigned char bitmap_hp_fixed_5_7_108 [5] = {
+ 0x00,
+ 0x41,
+ 0x7f,
+ 0x40,
+ 0x00
+};
+struct disp_font_glyph glyph_hp_fixed_5_7_108 = {
+ {5,7,0,0},
+ 5, 0, bitmap_hp_fixed_5_7_108,
+};
+
+const unsigned char bitmap_hp_fixed_5_7_109 [5] = {
+ 0x78,
+ 0x04,
+ 0x18,
+ 0x04,
+ 0x78
+};
+struct disp_font_glyph glyph_hp_fixed_5_7_109 = {
+ {5,7,0,0},
+ 5, 0, bitmap_hp_fixed_5_7_109,
+};
+
+const unsigned char bitmap_hp_fixed_5_7_110 [5] = {
+ 0x7c,
+ 0x08,
+ 0x04,
+ 0x04,
+ 0x78
+};
+struct disp_font_glyph glyph_hp_fixed_5_7_110 = {
+ {5,7,0,0},
+ 5, 0, bitmap_hp_fixed_5_7_110,
+};
+
+const unsigned char bitmap_hp_fixed_5_7_111 [5] = {
+ 0x38,
+ 0x44,
+ 0x44,
+ 0x44,
+ 0x38
+};
+struct disp_font_glyph glyph_hp_fixed_5_7_111 = {
+ {5,7,0,0},
+ 5, 0, bitmap_hp_fixed_5_7_111,
+};
+
+const unsigned char bitmap_hp_fixed_5_7_112 [5] = {
+ 0x7c,
+ 0x14,
+ 0x24,
+ 0x24,
+ 0x18
+};
+struct disp_font_glyph glyph_hp_fixed_5_7_112 = {
+ {5,7,0,0},
+ 5, 0, bitmap_hp_fixed_5_7_112,
+};
+
+const unsigned char bitmap_hp_fixed_5_7_113 [5] = {
+ 0x18,
+ 0x24,
+ 0x14,
+ 0x7c,
+ 0x40
+};
+struct disp_font_glyph glyph_hp_fixed_5_7_113 = {
+ {5,7,0,0},
+ 5, 0, bitmap_hp_fixed_5_7_113,
+};
+
+const unsigned char bitmap_hp_fixed_5_7_114 [5] = {
+ 0x00,
+ 0x7c,
+ 0x08,
+ 0x04,
+ 0x04
+};
+struct disp_font_glyph glyph_hp_fixed_5_7_114 = {
+ {5,7,0,0},
+ 5, 0, bitmap_hp_fixed_5_7_114,
+};
+
+const unsigned char bitmap_hp_fixed_5_7_115 [5] = {
+ 0x48,
+ 0x54,
+ 0x54,
+ 0x54,
+ 0x20
+};
+struct disp_font_glyph glyph_hp_fixed_5_7_115 = {
+ {5,7,0,0},
+ 5, 0, bitmap_hp_fixed_5_7_115,
+};
+
+const unsigned char bitmap_hp_fixed_5_7_116 [5] = {
+ 0x04,
+ 0x3e,
+ 0x44,
+ 0x20,
+ 0x00
+};
+struct disp_font_glyph glyph_hp_fixed_5_7_116 = {
+ {5,7,0,0},
+ 5, 0, bitmap_hp_fixed_5_7_116,
+};
+
+const unsigned char bitmap_hp_fixed_5_7_117 [5] = {
+ 0x3c,
+ 0x40,
+ 0x40,
+ 0x20,
+ 0x7c
+};
+struct disp_font_glyph glyph_hp_fixed_5_7_117 = {
+ {5,7,0,0},
+ 5, 0, bitmap_hp_fixed_5_7_117,
+};
+
+const unsigned char bitmap_hp_fixed_5_7_118 [5] = {
+ 0x1c,
+ 0x20,
+ 0x40,
+ 0x20,
+ 0x1c
+};
+struct disp_font_glyph glyph_hp_fixed_5_7_118 = {
+ {5,7,0,0},
+ 5, 0, bitmap_hp_fixed_5_7_118,
+};
+
+const unsigned char bitmap_hp_fixed_5_7_119 [5] = {
+ 0x3c,
+ 0x40,
+ 0x30,
+ 0x40,
+ 0x3c
+};
+struct disp_font_glyph glyph_hp_fixed_5_7_119 = {
+ {5,7,0,0},
+ 5, 0, bitmap_hp_fixed_5_7_119,
+};
+
+const unsigned char bitmap_hp_fixed_5_7_120 [5] = {
+ 0x44,
+ 0x28,
+ 0x10,
+ 0x28,
+ 0x44
+};
+struct disp_font_glyph glyph_hp_fixed_5_7_120 = {
+ {5,7,0,0},
+ 5, 0, bitmap_hp_fixed_5_7_120,
+};
+
+const unsigned char bitmap_hp_fixed_5_7_121 [5] = {
+ 0x04,
+ 0x48,
+ 0x30,
+ 0x08,
+ 0x04
+};
+struct disp_font_glyph glyph_hp_fixed_5_7_121 = {
+ {5,7,0,0},
+ 5, 0, bitmap_hp_fixed_5_7_121,
+};
+
+const unsigned char bitmap_hp_fixed_5_7_122 [5] = {
+ 0x44,
+ 0x64,
+ 0x54,
+ 0x4c,
+ 0x44
+};
+struct disp_font_glyph glyph_hp_fixed_5_7_122 = {
+ {5,7,0,0},
+ 5, 0, bitmap_hp_fixed_5_7_122,
+};
+
+const unsigned char bitmap_hp_fixed_5_7_123 [5] = {
+ 0x00,
+ 0x08,
+ 0x36,
+ 0x41,
+ 0x00
+};
+struct disp_font_glyph glyph_hp_fixed_5_7_123 = {
+ {5,7,0,0},
+ 5, 0, bitmap_hp_fixed_5_7_123,
+};
+
+const unsigned char bitmap_hp_fixed_5_7_124 [5] = {
+ 0x00,
+ 0x00,
+ 0x77,
+ 0x00,
+ 0x00
+};
+struct disp_font_glyph glyph_hp_fixed_5_7_124 = {
+ {5,7,0,0},
+ 5, 0, bitmap_hp_fixed_5_7_124,
+};
+
+const unsigned char bitmap_hp_fixed_5_7_125 [5] = {
+ 0x00,
+ 0x41,
+ 0x36,
+ 0x08,
+ 0x00
+};
+struct disp_font_glyph glyph_hp_fixed_5_7_125 = {
+ {5,7,0,0},
+ 5, 0, bitmap_hp_fixed_5_7_125,
+};
+
+const unsigned char bitmap_hp_fixed_5_7_126 [5] = {
+ 0x08,
+ 0x04,
+ 0x08,
+ 0x10,
+ 0x08
+};
+struct disp_font_glyph glyph_hp_fixed_5_7_126 = {
+ {5,7,0,0},
+ 5, 0, bitmap_hp_fixed_5_7_126,
+};
+
+const unsigned char bitmap_hp_fixed_5_7_127 [5] = {
+ 0x2a,
+ 0x55,
+ 0x2a,
+ 0x55,
+ 0x2a
+};
+struct disp_font_glyph glyph_hp_fixed_5_7_127 = {
+ {5,7,0,0},
+ 5, 0, bitmap_hp_fixed_5_7_127,
+};
+
+const struct disp_font_base font_hcms29xx_base = {
+ 1, /* trans */
+ {5, 7, 0, -1}, /* fbb w, h, x, y */
+ 7, 0, /* ascent, descent */
+ 0, /* default_char */
+ {&glyph_hp_fixed_5_7_0,
+ &glyph_hp_fixed_5_7_1,
+ &glyph_hp_fixed_5_7_2,
+ &glyph_hp_fixed_5_7_3,
+ &glyph_hp_fixed_5_7_4,
+ &glyph_hp_fixed_5_7_5,
+ &glyph_hp_fixed_5_7_6,
+ &glyph_hp_fixed_5_7_7,
+ &glyph_hp_fixed_5_7_8,
+ &glyph_hp_fixed_5_7_9,
+ &glyph_hp_fixed_5_7_10,
+ &glyph_hp_fixed_5_7_11,
+ &glyph_hp_fixed_5_7_12,
+ &glyph_hp_fixed_5_7_13,
+ &glyph_hp_fixed_5_7_14,
+ &glyph_hp_fixed_5_7_15,
+ &glyph_hp_fixed_5_7_16,
+ &glyph_hp_fixed_5_7_17,
+ &glyph_hp_fixed_5_7_18,
+ &glyph_hp_fixed_5_7_19,
+ &glyph_hp_fixed_5_7_20,
+ &glyph_hp_fixed_5_7_21,
+ &glyph_hp_fixed_5_7_22,
+ &glyph_hp_fixed_5_7_23,
+ &glyph_hp_fixed_5_7_24,
+ &glyph_hp_fixed_5_7_25,
+ &glyph_hp_fixed_5_7_26,
+ &glyph_hp_fixed_5_7_27,
+ &glyph_hp_fixed_5_7_28,
+ &glyph_hp_fixed_5_7_29,
+ &glyph_hp_fixed_5_7_30,
+ &glyph_hp_fixed_5_7_31,
+ &glyph_hp_fixed_5_7_32,
+ &glyph_hp_fixed_5_7_33,
+ &glyph_hp_fixed_5_7_34,
+ &glyph_hp_fixed_5_7_35,
+ &glyph_hp_fixed_5_7_36,
+ &glyph_hp_fixed_5_7_37,
+ &glyph_hp_fixed_5_7_38,
+ &glyph_hp_fixed_5_7_39,
+ &glyph_hp_fixed_5_7_40,
+ &glyph_hp_fixed_5_7_41,
+ &glyph_hp_fixed_5_7_42,
+ &glyph_hp_fixed_5_7_43,
+ &glyph_hp_fixed_5_7_44,
+ &glyph_hp_fixed_5_7_45,
+ &glyph_hp_fixed_5_7_46,
+ &glyph_hp_fixed_5_7_47,
+ &glyph_hp_fixed_5_7_48,
+ &glyph_hp_fixed_5_7_49,
+ &glyph_hp_fixed_5_7_50,
+ &glyph_hp_fixed_5_7_51,
+ &glyph_hp_fixed_5_7_52,
+ &glyph_hp_fixed_5_7_53,
+ &glyph_hp_fixed_5_7_54,
+ &glyph_hp_fixed_5_7_55,
+ &glyph_hp_fixed_5_7_56,
+ &glyph_hp_fixed_5_7_57,
+ &glyph_hp_fixed_5_7_58,
+ &glyph_hp_fixed_5_7_59,
+ &glyph_hp_fixed_5_7_60,
+ &glyph_hp_fixed_5_7_61,
+ &glyph_hp_fixed_5_7_62,
+ &glyph_hp_fixed_5_7_63,
+ &glyph_hp_fixed_5_7_64,
+ &glyph_hp_fixed_5_7_65,
+ &glyph_hp_fixed_5_7_66,
+ &glyph_hp_fixed_5_7_67,
+ &glyph_hp_fixed_5_7_68,
+ &glyph_hp_fixed_5_7_69,
+ &glyph_hp_fixed_5_7_70,
+ &glyph_hp_fixed_5_7_71,
+ &glyph_hp_fixed_5_7_72,
+ &glyph_hp_fixed_5_7_73,
+ &glyph_hp_fixed_5_7_74,
+ &glyph_hp_fixed_5_7_75,
+ &glyph_hp_fixed_5_7_76,
+ &glyph_hp_fixed_5_7_77,
+ &glyph_hp_fixed_5_7_78,
+ &glyph_hp_fixed_5_7_79,
+ &glyph_hp_fixed_5_7_80,
+ &glyph_hp_fixed_5_7_81,
+ &glyph_hp_fixed_5_7_82,
+ &glyph_hp_fixed_5_7_83,
+ &glyph_hp_fixed_5_7_84,
+ &glyph_hp_fixed_5_7_85,
+ &glyph_hp_fixed_5_7_86,
+ &glyph_hp_fixed_5_7_87,
+ &glyph_hp_fixed_5_7_88,
+ &glyph_hp_fixed_5_7_89,
+ &glyph_hp_fixed_5_7_90,
+ &glyph_hp_fixed_5_7_91,
+ &glyph_hp_fixed_5_7_92,
+ &glyph_hp_fixed_5_7_93,
+ &glyph_hp_fixed_5_7_94,
+ &glyph_hp_fixed_5_7_95,
+ &glyph_hp_fixed_5_7_96,
+ &glyph_hp_fixed_5_7_97,
+ &glyph_hp_fixed_5_7_98,
+ &glyph_hp_fixed_5_7_99,
+ &glyph_hp_fixed_5_7_100,
+ &glyph_hp_fixed_5_7_101,
+ &glyph_hp_fixed_5_7_102,
+ &glyph_hp_fixed_5_7_103,
+ &glyph_hp_fixed_5_7_104,
+ &glyph_hp_fixed_5_7_105,
+ &glyph_hp_fixed_5_7_106,
+ &glyph_hp_fixed_5_7_107,
+ &glyph_hp_fixed_5_7_108,
+ &glyph_hp_fixed_5_7_109,
+ &glyph_hp_fixed_5_7_110,
+ &glyph_hp_fixed_5_7_111,
+ &glyph_hp_fixed_5_7_112,
+ &glyph_hp_fixed_5_7_113,
+ &glyph_hp_fixed_5_7_114,
+ &glyph_hp_fixed_5_7_115,
+ &glyph_hp_fixed_5_7_116,
+ &glyph_hp_fixed_5_7_117,
+ &glyph_hp_fixed_5_7_118,
+ &glyph_hp_fixed_5_7_119,
+ &glyph_hp_fixed_5_7_120,
+ &glyph_hp_fixed_5_7_121,
+ &glyph_hp_fixed_5_7_122,
+ &glyph_hp_fixed_5_7_123,
+ &glyph_hp_fixed_5_7_124,
+ &glyph_hp_fixed_5_7_125,
+ &glyph_hp_fixed_5_7_126,
+ &glyph_hp_fixed_5_7_127,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL}
+};
diff --git a/bsps/shared/dev/display/font_hcms29xx.h b/bsps/shared/dev/display/font_hcms29xx.h
new file mode 100644
index 0000000000..8638fcf600
--- /dev/null
+++ b/bsps/shared/dev/display/font_hcms29xx.h
@@ -0,0 +1,36 @@
+/*===============================================================*\
+| Project: display driver for HCMS29xx |
++-----------------------------------------------------------------+
+| File: font_hcms29xx.h |
++-----------------------------------------------------------------+
+| Copyright (c) 2008 |
+| Embedded Brains GmbH |
+| Obere Lagerstr. 30 |
+| D-82178 Puchheim |
+| Germany |
+| rtems@embedded-brains.de |
++-----------------------------------------------------------------+
+| The license and distribution terms for this file may be |
+| found in the file LICENSE in this distribution or at |
+| http://www.rtems.org/license/LICENSE. |
++-----------------------------------------------------------------+
+| This file declares the 5x7 bit font used in disp_hcms29xx |
+\*===============================================================*/
+
+#ifndef FONT_HCMS29XX_H
+#define FONT_HCMS29XX_H
+
+#include "disp_fonts.h"
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern struct disp_font_base font_hcms29xx_base;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* not defined FONT_HCMS29XX_H */
diff --git a/bsps/shared/dev/flash/am29lv160.c b/bsps/shared/dev/flash/am29lv160.c
new file mode 100644
index 0000000000..5cfaae4f24
--- /dev/null
+++ b/bsps/shared/dev/flash/am29lv160.c
@@ -0,0 +1,473 @@
+/*
+ * RTEMS Project (http://www.rtems.org/)
+ *
+ * Copyright 2007 Chris Johns (chrisj@rtems.org)
+ */
+/**
+ * Provide flash support for the AM26LV160 device.
+ *
+ * The M29W160D is the same device.
+ */
+
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+
+#include <rtems.h>
+
+#include <libchip/am29lv160.h>
+
+#ifndef AM26LV160_ERROR_TRACE
+#define AM26LV160_ERROR_TRACE (0)
+#endif
+
+/**
+ * Boot blocks at the top
+ */
+const rtems_fdisk_segment_desc rtems_am29lv160t_segments[4] =
+{
+ {
+ .count = 31,
+ .segment = 0,
+ .offset = 0x00000000,
+ .size = RTEMS_FDISK_KBYTES (64)
+ },
+ {
+ .count = 1,
+ .segment = 31,
+ .offset = 0x001f0000,
+ .size = RTEMS_FDISK_KBYTES (32)
+ },
+ {
+ .count = 2,
+ .segment = 32,
+ .offset = 0x001f8000,
+ .size = RTEMS_FDISK_KBYTES (8)
+ },
+ {
+ .count = 1,
+ .segment = 34,
+ .offset = 0x001fc000,
+ .size = RTEMS_FDISK_KBYTES (16)
+ }
+};
+
+/**
+ * Boot blocks at the bottom.
+ */
+const rtems_fdisk_segment_desc rtems_am29lv160b_segments[] =
+{
+ {
+ .count = 1,
+ .segment = 0,
+ .offset = 0x00000000,
+ .size = RTEMS_FDISK_KBYTES (16)
+ },
+ {
+ . count = 2,
+ .segment = 1,
+ .offset = 0x00004000,
+ .size = RTEMS_FDISK_KBYTES (8)
+ },
+ {
+ .count = 1,
+ .segment = 3,
+ .offset = 0x00008000,
+ .size = RTEMS_FDISK_KBYTES (32)
+ },
+ {
+ .count = 31,
+ .segment = 4,
+ .offset = 0x00010000,
+ .size = RTEMS_FDISK_KBYTES (64)
+ }
+};
+
+static int
+rtems_am29lv160_blank (const rtems_fdisk_segment_desc* sd,
+ uint32_t device,
+ uint32_t segment,
+ uint32_t offset,
+ uint32_t size)
+{
+ const rtems_am29lv160_config* ac = &rtems_am29lv160_configuration[device];
+ volatile uint8_t* seg_8 = ac->base;
+ volatile uint32_t* seg_32;
+ uint32_t count;
+
+ offset += sd->offset + (segment - sd->segment) * sd->size;
+
+ seg_8 += offset;
+
+ count = offset & (sizeof (uint32_t) - 1);
+ size -= count;
+
+ while (count--)
+ if (*seg_8++ != 0xff)
+ {
+#if AM26LV160_ERROR_TRACE
+ printf ("AM26LV160: blank check error: %p = 0x%02x\n",
+ seg_8 - 1, *(seg_8 - 1));
+#endif
+ return EIO;
+ }
+
+ seg_32 = (volatile uint32_t*) seg_8;
+
+ count = size / sizeof (uint32_t);
+ size -= count * sizeof (uint32_t);
+
+ while (count--)
+ if (*seg_32++ != 0xffffffff)
+ {
+#if AM26LV160_ERROR_TRACE
+ printf ("AM26LV160: blank check error: %p = 0x%08lx\n",
+ seg_32 - 1, *(seg_32 - 1));
+#endif
+ return EIO;
+ }
+
+ seg_8 = (volatile uint8_t*) seg_32;
+
+ while (size--)
+ if (*seg_8++ != 0xff)
+ {
+#if AM26LV160_ERROR_TRACE
+ printf ("AM26LV160: blank check error: %p = 0x%02x\n",
+ seg_8 - 1, *(seg_8 - 1));
+#endif
+ return EIO;
+ }
+
+ return 0;
+}
+
+static int
+rtems_am29lv160_verify (const rtems_fdisk_segment_desc* sd,
+ uint32_t device,
+ uint32_t segment,
+ uint32_t offset,
+ const void* buffer,
+ uint32_t size)
+{
+ const rtems_am29lv160_config* ac = &rtems_am29lv160_configuration[device];
+ const uint8_t* addr = ac->base;
+
+ addr += (sd->offset + (segment - sd->segment) * sd->size) + offset;
+
+ if (memcmp (addr, buffer, size) != 0)
+ return EIO;
+
+ return 0;
+}
+
+static int
+rtems_am29lv160_toggle_wait_8 (volatile uint8_t* status)
+{
+ while (1)
+ {
+ volatile uint8_t status1 = *status;
+ volatile uint8_t status2 = *status;
+
+ if (((status1 ^ status2) & (1 << 6)) == 0)
+ return 0;
+
+ if ((status1 & (1 << 5)) != 0)
+ {
+ status1 = *status;
+ status2 = *status;
+
+ if (((status1 ^ status2) & (1 << 6)) == 0)
+ return 0;
+
+#if AM26LV160_ERROR_TRACE
+ printf ("AM26LV160: error bit detected: %p = 0x%04x\n",
+ status, status1);
+#endif
+
+ *status = 0xf0;
+ return EIO;
+ }
+ }
+}
+
+static int
+rtems_am29lv160_toggle_wait_16 (volatile uint16_t* status)
+{
+ while (1)
+ {
+ volatile uint16_t status1 = *status;
+ volatile uint16_t status2 = *status;
+
+ if (((status1 ^ status2) & (1 << 6)) == 0)
+ return 0;
+
+ if ((status1 & (1 << 5)) != 0)
+ {
+ status1 = *status;
+ status2 = *status;
+
+ if (((status1 ^ status2) & (1 << 6)) == 0)
+ return 0;
+
+#if AM26LV160_ERROR_TRACE
+ printf ("AM26LV160: error bit detected: %p = 0x%04x/0x%04x\n",
+ status, status1, status2);
+#endif
+
+ *status = 0xf0;
+ return EIO;
+ }
+ }
+}
+
+static int
+rtems_am29lv160_write_data_8 (volatile uint8_t* base,
+ uint32_t offset,
+ const uint8_t* data,
+ uint32_t size)
+{
+ volatile uint8_t* seg = base + offset;
+ rtems_interrupt_level level;
+
+ /*
+ * Issue a reset.
+ */
+ *base = 0xf0;
+
+ while (size)
+ {
+ rtems_interrupt_disable (level);
+ *(base + 0xaaa) = 0xaa;
+ *(base + 0x555) = 0x55;
+ *(base + 0xaaa) = 0xa0;
+ *seg = *data++;
+ rtems_interrupt_enable (level);
+ if (rtems_am29lv160_toggle_wait_8 (seg++) != 0)
+ return EIO;
+ size--;
+ }
+
+ /*
+ * Issue a reset.
+ */
+ *base = 0xf0;
+
+ return 0;
+}
+
+static int
+rtems_am29lv160_write_data_16 (volatile uint16_t* base,
+ uint32_t offset,
+ const uint16_t* data,
+ uint32_t size)
+{
+ volatile uint16_t* seg = base + (offset / 2);
+ rtems_interrupt_level level;
+
+ size /= 2;
+
+ /*
+ * Issue a reset.
+ */
+ *base = 0xf0;
+
+ while (size)
+ {
+ rtems_interrupt_disable (level);
+ *(base + 0x555) = 0xaa;
+ *(base + 0x2aa) = 0x55;
+ *(base + 0x555) = 0xa0;
+ *seg = *data++;
+ rtems_interrupt_enable (level);
+ if (rtems_am29lv160_toggle_wait_16 (seg++) != 0)
+ return EIO;
+ size--;
+ }
+
+ /*
+ * Issue a reset.
+ */
+ *base = 0xf0;
+
+ return 0;
+}
+
+static int
+rtems_am29lv160_read (const rtems_fdisk_segment_desc* sd,
+ uint32_t device,
+ uint32_t segment,
+ uint32_t offset,
+ void* buffer,
+ uint32_t size)
+{
+ unsigned char* addr =
+ rtems_am29lv160_configuration[device].base +
+ sd->offset + ((segment - sd->segment) * sd->size) + offset;
+ memcpy (buffer, addr, size);
+ return 0;
+}
+
+/*
+ * @todo Fix the odd alignment and odd sizes.
+ */
+static int
+rtems_am29lv160_write (const rtems_fdisk_segment_desc* sd,
+ uint32_t device,
+ uint32_t segment,
+ uint32_t offset,
+ const void* buffer,
+ uint32_t size)
+{
+ int ret = rtems_am29lv160_verify (sd, device, segment, offset, buffer, size);
+
+ if (ret != 0)
+ {
+ const rtems_am29lv160_config* ac = &rtems_am29lv160_configuration[device];
+ uint32_t soffset;
+
+ soffset = offset + sd->offset + ((segment - sd->segment) * sd->size);
+
+ if (offset & 1)
+ printf ("rtems_am29lv160_write: offset is odd\n");
+
+ if (size & 1)
+ printf ("rtems_am29lv160_write: size is odd\n");
+
+ if (ac->bus_8bit)
+ ret = rtems_am29lv160_write_data_8 (ac->base, soffset, buffer, size);
+ else
+ ret = rtems_am29lv160_write_data_16 (ac->base, soffset, buffer, size);
+
+ /*
+ * Verify the write worked.
+ */
+ if (ret == 0)
+ {
+ ret = rtems_am29lv160_verify (sd, device, segment, offset, buffer, size);
+#if AM26LV160_ERROR_TRACE
+ if (ret)
+ printf ("AM26LV160: verify failed: %ld-%ld-%08lx: s=%ld\n",
+ device, segment, offset, size);
+#endif
+ }
+ }
+
+ return ret;
+}
+
+static int
+rtems_am29lv160_erase (const rtems_fdisk_segment_desc* sd,
+ uint32_t device,
+ uint32_t segment)
+{
+ int ret = rtems_am29lv160_blank (sd, device, segment, 0, sd->size);
+ if (ret != 0)
+ {
+ const rtems_am29lv160_config* ac = &rtems_am29lv160_configuration[device];
+ uint32_t offset;
+ rtems_interrupt_level level;
+
+ offset = sd->offset + ((segment - sd->segment) * sd->size);
+
+ if (ac->bus_8bit)
+ {
+ volatile uint8_t* base = ac->base;
+ volatile uint8_t* seg = base + offset;
+
+ /*
+ * Issue a reset.
+ */
+ rtems_interrupt_disable (level);
+ *base = 0xf0;
+ *(base + 0xaaa) = 0xaa;
+ *(base + 0x555) = 0x55;
+ *(base + 0xaaa) = 0x80;
+ *(base + 0xaaa) = 0xaa;
+ *(base + 0x555) = 0x55;
+ *seg = 0x30;
+ rtems_interrupt_enable (level);
+
+ ret = rtems_am29lv160_toggle_wait_8 (seg);
+
+ /*
+ * Issue a reset.
+ */
+ *base = 0xf0;
+ }
+ else
+ {
+ volatile uint16_t* base = ac->base;
+ volatile uint16_t* seg = base + (offset / 2);
+
+ /*
+ * Issue a reset.
+ */
+ rtems_interrupt_disable (level);
+ *base = 0xf0;
+ *(base + 0x555) = 0xaa;
+ *(base + 0x2aa) = 0x55;
+ *(base + 0x555) = 0x80;
+ *(base + 0x555) = 0xaa;
+ *(base + 0x2aa) = 0x55;
+ *seg = 0x30;
+ rtems_interrupt_enable (level);
+
+ ret = rtems_am29lv160_toggle_wait_16 (seg);
+
+ /*
+ * Issue a reset.
+ */
+ *base = 0xf0;
+ }
+
+ /*
+ * Check the erase worked.
+ */
+ if (ret == 0)
+ {
+ ret = rtems_am29lv160_blank (sd, device, segment, 0, sd->size);
+#if AM26LV160_ERROR_TRACE
+ if (ret)
+ printf ("AM26LV160: erase failed: %ld-%ld\n", device, segment);
+#endif
+ }
+ }
+
+ return ret;
+}
+
+static int
+rtems_am29lv160_erase_device (const rtems_fdisk_device_desc* dd,
+ uint32_t device)
+{
+ uint32_t segment;
+
+ for (segment = 0; segment < dd->segment_count; segment++)
+ {
+ uint32_t seg_segment;
+
+ for (seg_segment = 0;
+ seg_segment < dd->segments[segment].count;
+ seg_segment++)
+ {
+ int ret = rtems_am29lv160_erase (&dd->segments[segment],
+ device,
+ segment + seg_segment);
+ if (ret)
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+const rtems_fdisk_driver_handlers rtems_am29lv160_handlers =
+{
+ .read = rtems_am29lv160_read,
+ .write = rtems_am29lv160_write,
+ .blank = rtems_am29lv160_blank,
+ .verify = rtems_am29lv160_verify,
+ .erase = rtems_am29lv160_erase,
+ .erase_device = rtems_am29lv160_erase_device
+};
diff --git a/bsps/shared/dev/i2c/i2c-2b-eeprom.c b/bsps/shared/dev/i2c/i2c-2b-eeprom.c
new file mode 100644
index 0000000000..4a8b5fdb9c
--- /dev/null
+++ b/bsps/shared/dev/i2c/i2c-2b-eeprom.c
@@ -0,0 +1,177 @@
+/* Trivial i2c driver for reading "2-byte eeproms".
+ * On 'open' the read-pointer is reset to 0, subsequent
+ * read operations slurp data from there...
+ */
+
+/*
+ * Authorship
+ * ----------
+ * This software was created by
+ * Till Straumann <strauman@slac.stanford.edu>, 2005,
+ * Stanford Linear Accelerator Center, Stanford University.
+ *
+ * Acknowledgement of sponsorship
+ * ------------------------------
+ * This software was produced by
+ * the Stanford Linear Accelerator Center, Stanford University,
+ * under Contract DE-AC03-76SFO0515 with the Department of Energy.
+ *
+ * Government disclaimer of liability
+ * ----------------------------------
+ * Neither the United States nor the United States Department of Energy,
+ * nor any of their employees, makes any warranty, express or implied, or
+ * assumes any legal liability or responsibility for the accuracy,
+ * completeness, or usefulness of any data, apparatus, product, or process
+ * disclosed, or represents that its use would not infringe privately owned
+ * rights.
+ *
+ * Stanford disclaimer of liability
+ * --------------------------------
+ * Stanford University makes no representations or warranties, express or
+ * implied, nor assumes any liability for the use of this software.
+ *
+ * Stanford disclaimer of copyright
+ * --------------------------------
+ * Stanford University, owner of the copyright, hereby disclaims its
+ * copyright and all other rights in this software. Hence, anyone may
+ * freely use it for any purpose without restriction.
+ *
+ * Maintenance of notices
+ * ----------------------
+ * In the interest of clarity regarding the origin and status of this
+ * SLAC software, this and all the preceding Stanford University notices
+ * are to remain affixed to any copy or derivative of this software made
+ * or distributed by the recipient and are to be affixed to any copy of
+ * software made or distributed by the recipient that contains a copy or
+ * derivative of this software.
+ *
+ * ------------------ SLAC Software Notices, Set 4 OTT.002a, 2004 FEB 03
+ */
+
+
+#include <rtems.h>
+#include <rtems/libi2c.h>
+
+#include <libchip/i2c-2b-eeprom.h>
+#include <rtems/libio.h>
+
+#define EEPROM_PG_SZ 32
+#define ALGN(x) (((uint32_t)(x) + EEPROM_PG_SZ) & ~(EEPROM_PG_SZ-1))
+
+static rtems_status_code
+send_file_ptr (rtems_device_minor_number minor, unsigned pos, int tout)
+{
+ int sc;
+ unsigned char bytes[2];
+
+ bytes[0] = (pos >> 8) & 0xff;
+ bytes[1] = (pos) & 0xff;
+
+ /* poll addressing the next page; if 'tout' is <=0 we only try once
+ * and return the status. If 'tout' is positive, we try 'tout' times
+ * and return RTEMS_TIMEOUT if it didnt work
+ */
+ while ((sc = rtems_libi2c_start_write_bytes (minor, bytes, 2)) < 0) {
+ if (--tout <= 0)
+ return tout ? -sc : RTEMS_TIMEOUT;
+ rtems_task_wake_after (1);
+ }
+ return RTEMS_SUCCESSFUL;
+}
+
+static rtems_status_code
+i2c_2b_eeprom_write (rtems_device_major_number major,
+ rtems_device_minor_number minor, void *arg)
+{
+ rtems_libio_rw_args_t *rwargs = arg;
+ unsigned off = rwargs->offset;
+ int cnt = rwargs->count;
+ unsigned char *buf = (unsigned char *)rwargs->buffer;
+ int sc;
+ unsigned end;
+ int l;
+
+ if (cnt <= 0)
+ return RTEMS_SUCCESSFUL;
+
+ if ((sc = send_file_ptr (minor, off, 0)))
+ return sc;
+
+ do {
+ /* write up to next page boundary */
+ end = ALGN (off);
+ l = end - off;
+ if (l > cnt)
+ l = cnt;
+
+ sc = rtems_libi2c_write_bytes (minor, buf, l);
+ if (sc < 0)
+ return -sc;
+
+ sc = rtems_libi2c_send_stop (minor);
+ if (sc)
+ return sc;
+
+ rwargs->bytes_moved += l;
+
+ buf += l;
+ cnt -= l;
+ off += l;
+
+ /* poll addressing the next page */
+ if ((sc = send_file_ptr (minor, off, 100)))
+ return sc;
+
+ } while (cnt > 0);
+
+ return rtems_libi2c_send_stop (minor);
+}
+
+static rtems_status_code
+i2c_2b_eeprom_read (rtems_device_major_number major,
+ rtems_device_minor_number minor, void *arg)
+{
+ int sc;
+ rtems_libio_rw_args_t *rwargs = arg;
+
+ if (RTEMS_SUCCESSFUL != (sc = send_file_ptr (minor, rwargs->offset, 0)))
+ return -sc;
+
+ sc = rtems_libi2c_start_read_bytes(
+ minor,
+ (unsigned char *)rwargs->buffer,
+ rwargs->count
+ );
+
+ if (sc < 0) {
+ rwargs->bytes_moved = 0;
+ return -sc;
+ }
+ rwargs->bytes_moved = sc;
+
+ return rtems_libi2c_send_stop (minor);
+}
+
+static rtems_driver_address_table myops = {
+ .read_entry = i2c_2b_eeprom_read,
+ .write_entry = i2c_2b_eeprom_write,
+};
+
+static rtems_libi2c_drv_t my_drv_tbl = {
+ .ops = &myops,
+ .size = sizeof (my_drv_tbl),
+};
+
+/* provide a second table for R/O access */
+static rtems_driver_address_table my_ro_ops = {
+ .read_entry = i2c_2b_eeprom_read,
+};
+
+static rtems_libi2c_drv_t my_ro_drv_tbl = {
+ .ops = &my_ro_ops,
+ .size = sizeof (my_ro_drv_tbl),
+};
+
+
+rtems_libi2c_drv_t *i2c_2b_eeprom_driver_descriptor = &my_drv_tbl;
+rtems_libi2c_drv_t *i2c_2b_eeprom_ro_driver_descriptor = &my_ro_drv_tbl;
diff --git a/bsps/shared/dev/i2c/i2c-ds1621.c b/bsps/shared/dev/i2c/i2c-ds1621.c
new file mode 100644
index 0000000000..51f64de679
--- /dev/null
+++ b/bsps/shared/dev/i2c/i2c-ds1621.c
@@ -0,0 +1,128 @@
+/* Trivial i2c driver for the maxim DS1621 temperature sensor;
+ * just implements reading constant conversions with 8-bit
+ * resolution.
+ * Demonstrates the implementation of a i2c high-level driver.
+ */
+
+/*
+ * Authorship
+ * ----------
+ * This software was created by
+ * Till Straumann <strauman@slac.stanford.edu>, 2005,
+ * Stanford Linear Accelerator Center, Stanford University.
+ *
+ * Acknowledgement of sponsorship
+ * ------------------------------
+ * This software was produced by
+ * the Stanford Linear Accelerator Center, Stanford University,
+ * under Contract DE-AC03-76SFO0515 with the Department of Energy.
+ *
+ * Government disclaimer of liability
+ * ----------------------------------
+ * Neither the United States nor the United States Department of Energy,
+ * nor any of their employees, makes any warranty, express or implied, or
+ * assumes any legal liability or responsibility for the accuracy,
+ * completeness, or usefulness of any data, apparatus, product, or process
+ * disclosed, or represents that its use would not infringe privately owned
+ * rights.
+ *
+ * Stanford disclaimer of liability
+ * --------------------------------
+ * Stanford University makes no representations or warranties, express or
+ * implied, nor assumes any liability for the use of this software.
+ *
+ * Stanford disclaimer of copyright
+ * --------------------------------
+ * Stanford University, owner of the copyright, hereby disclaims its
+ * copyright and all other rights in this software. Hence, anyone may
+ * freely use it for any purpose without restriction.
+ *
+ * Maintenance of notices
+ * ----------------------
+ * In the interest of clarity regarding the origin and status of this
+ * SLAC software, this and all the preceding Stanford University notices
+ * are to remain affixed to any copy or derivative of this software made
+ * or distributed by the recipient and are to be affixed to any copy of
+ * software made or distributed by the recipient that contains a copy or
+ * derivative of this software.
+ *
+ * ------------------ SLAC Software Notices, Set 4 OTT.002a, 2004 FEB 03
+ */
+#include <rtems.h>
+#include <rtems/libi2c.h>
+
+#include <libchip/i2c-ds1621.h>
+
+#include <rtems/libio.h>
+
+
+static rtems_status_code
+ds1621_init (rtems_device_major_number major, rtems_device_minor_number minor,
+ void *arg)
+{
+ int sc;
+ unsigned char csr[2] = { DS1621_CMD_CSR_ACCESS, 0 }, cmd;
+
+ /* First start command acquires a lock for the bus */
+
+ /* Initialize; switch continuous conversion on */
+ sc = rtems_libi2c_start_write_bytes (minor, csr, 1);
+ if (sc < 0)
+ return -sc;
+
+ sc = rtems_libi2c_start_read_bytes (minor, csr + 1, 1);
+ if (sc < 0)
+ return -sc;
+
+ csr[1] &= ~DS1621_CSR_1SHOT;
+
+ sc = rtems_libi2c_start_write_bytes (minor, csr, 2);
+ if (sc < 0)
+ return -sc;
+
+ /* Start conversion */
+ cmd = DS1621_CMD_START_CONV;
+
+ sc = rtems_libi2c_start_write_bytes (minor, &cmd, 1);
+ if (sc < 0)
+ return -sc;
+
+ /* sending 'stop' relinquishes the bus mutex -- don't hold it
+ * across system calls!
+ */
+ return rtems_libi2c_send_stop (minor);
+}
+
+static rtems_status_code
+ds1621_read (rtems_device_major_number major, rtems_device_minor_number minor,
+ void *arg)
+{
+ int sc;
+ rtems_libio_rw_args_t *rwargs = arg;
+ unsigned char cmd = DS1621_CMD_READ_TEMP;
+
+ sc = rtems_libi2c_start_write_bytes (minor, &cmd, 1);
+ if (sc < 0)
+ return -sc;
+ if (sc < 1)
+ return RTEMS_IO_ERROR;
+ sc = rtems_libi2c_start_read_bytes(minor, (unsigned char *)rwargs->buffer, 1);
+ if (sc < 0) {
+ rwargs->bytes_moved = 0;
+ return -sc;
+ }
+ rwargs->bytes_moved = 1;
+ return rtems_libi2c_send_stop (minor);
+}
+
+static rtems_driver_address_table myops = {
+ .initialization_entry = ds1621_init,
+ .read_entry = ds1621_read,
+};
+
+static rtems_libi2c_drv_t my_drv_tbl = {
+ .ops = &myops,
+ .size = sizeof (my_drv_tbl),
+};
+
+rtems_libi2c_drv_t *i2c_ds1621_driver_descriptor = &my_drv_tbl;
diff --git a/bsps/shared/dev/i2c/i2c-sc620.c b/bsps/shared/dev/i2c/i2c-sc620.c
new file mode 100644
index 0000000000..7b30ae56af
--- /dev/null
+++ b/bsps/shared/dev/i2c/i2c-sc620.c
@@ -0,0 +1,91 @@
+/**
+ * @file
+ *
+ * @brief I2C Driver for SEMTECH SC620 Octal LED Driver
+ */
+
+/*
+ * Copyright (c) 2013 embedded brains GmbH. All rights reserved.
+ *
+ * embedded brains GmbH
+ * Obere Lagerstr. 30
+ * 82178 Puchheim
+ * Germany
+ * <rtems@embedded-brains.de>
+ *
+ * The license and distribution terms for this file may be
+ * found in the file LICENSE in this distribution or at
+ * http://www.rtems.org/license/LICENSE.
+ */
+
+#include <libchip/i2c-sc620.h>
+
+#include <rtems/libio.h>
+
+#define SC620_REG_COUNT 10
+
+static rtems_status_code i2c_sc620_write(
+ rtems_device_major_number major,
+ rtems_device_minor_number minor,
+ void *arg
+)
+{
+ rtems_status_code sc = RTEMS_IO_ERROR;
+ rtems_libio_rw_args_t *rw = arg;
+ unsigned char *buf = (unsigned char *) &rw->buffer[0];
+
+ if (rw->count == 2 && buf[0] < SC620_REG_COUNT) {
+ int rv;
+
+ rv = rtems_libi2c_start_write_bytes(
+ minor, buf, 2
+ );
+ if (rv == 2) {
+ sc = rtems_libi2c_send_stop(minor);
+ }
+ }
+
+ rw->bytes_moved = sc == RTEMS_SUCCESSFUL ? 2 : 0;
+
+ return sc;
+}
+
+static rtems_status_code i2c_sc620_read(
+ rtems_device_major_number major,
+ rtems_device_minor_number minor,
+ void *arg
+)
+{
+ rtems_status_code sc = RTEMS_IO_ERROR;
+ rtems_libio_rw_args_t *rw = arg;
+ unsigned char *buf = (unsigned char *) &rw->buffer[0];
+
+ if (rw->count == 1 && buf[0] < SC620_REG_COUNT) {
+ int rv;
+
+ rv = rtems_libi2c_start_write_bytes(minor, buf, 1);
+ if (rv == 1) {
+ sc = rtems_libi2c_send_addr(minor, 0);
+ if (sc == RTEMS_SUCCESSFUL) {
+ rv = rtems_libi2c_read_bytes(minor, buf, 1);
+ if (rv == 1) {
+ sc = rtems_libi2c_send_stop(minor);
+ }
+ }
+ }
+ }
+
+ rw->bytes_moved = sc == RTEMS_SUCCESSFUL ? 1 : 0;
+
+ return sc;
+}
+
+static rtems_driver_address_table i2c_sc620_ops = {
+ .read_entry = i2c_sc620_read,
+ .write_entry = i2c_sc620_write
+};
+
+rtems_libi2c_drv_t i2c_sc620_driver = {
+ .ops = &i2c_sc620_ops,
+ .size = sizeof(i2c_sc620_driver)
+};
diff --git a/bsps/shared/dev/i2c/spi-flash-m25p40.c b/bsps/shared/dev/i2c/spi-flash-m25p40.c
new file mode 100644
index 0000000000..075a4510b9
--- /dev/null
+++ b/bsps/shared/dev/i2c/spi-flash-m25p40.c
@@ -0,0 +1,60 @@
+/*===============================================================*\
+| Project: SPI driver for M25P40 like spi flash device |
++-----------------------------------------------------------------+
+| Copyright (c) 2007 |
+| Embedded Brains GmbH |
+| Obere Lagerstr. 30 |
+| D-82178 Puchheim |
+| Germany |
+| rtems@embedded-brains.de |
++-----------------------------------------------------------------+
+| The license and distribution terms for this file may be |
+| found in the file LICENSE in this distribution or at |
+| |
+| http://www.rtems.org/license/LICENSE. |
+| |
++-----------------------------------------------------------------+
+\*===============================================================*/
+
+#include <rtems.h>
+#include <rtems/libi2c.h>
+
+#include <libchip/spi-flash-m25p40.h>
+#include <rtems/libio.h>
+
+
+static spi_memdrv_t spi_flash_m25p40_rw_drv_t = {
+ {/* public fields */
+ .ops = &spi_memdrv_rw_ops, /* operations of general memdrv */
+ .size = sizeof (spi_flash_m25p40_rw_drv_t),
+ },
+ { /* our private fields */
+ .baudrate = 2000000,
+ .erase_before_program = true,
+ .empty_state = 0xff,
+ .page_size = 256, /* programming page size in bytes */
+ .sector_size = 0x10000, /* 64K - erase sector size in bytes */
+ .mem_size = 0x80000, /* 512K - total capacity in bytes */
+ }
+};
+
+rtems_libi2c_drv_t *spi_flash_m25p40_rw_driver_descriptor =
+&spi_flash_m25p40_rw_drv_t.libi2c_drv_entry;
+
+static spi_memdrv_t spi_flash_m25p40_ro_drv_t = {
+ {/* public fields */
+ .ops = &spi_memdrv_ro_ops, /* operations of general memdrv */
+ .size = sizeof (spi_flash_m25p40_ro_drv_t),
+ },
+ { /* our private fields */
+ .baudrate = 2000000,
+ .erase_before_program = true,
+ .empty_state = 0xff,
+ .page_size = 256, /* programming page size in bytes */
+ .sector_size = 0x10000, /* 64K erase sector size in bytes */
+ .mem_size = 0x80000, /* 512K total capacity in bytes */
+ }
+};
+
+rtems_libi2c_drv_t *spi_flash_m25p40_ro_driver_descriptor =
+&spi_flash_m25p40_ro_drv_t.libi2c_drv_entry;
diff --git a/bsps/shared/dev/i2c/spi-fram-fm25l256.c b/bsps/shared/dev/i2c/spi-fram-fm25l256.c
new file mode 100644
index 0000000000..086feb82bb
--- /dev/null
+++ b/bsps/shared/dev/i2c/spi-fram-fm25l256.c
@@ -0,0 +1,60 @@
+/*===============================================================*\
+| Project: SPI driver for FM25L256 like spi fram device |
++-----------------------------------------------------------------+
+| Copyright (c) 2008 |
+| Embedded Brains GmbH |
+| Obere Lagerstr. 30 |
+| D-82178 Puchheim |
+| Germany |
+| rtems@embedded-brains.de |
++-----------------------------------------------------------------+
+| The license and distribution terms for this file may be |
+| found in the file LICENSE in this distribution or at |
+| |
+| http://www.rtems.org/license/LICENSE. |
+| |
++-----------------------------------------------------------------+
+\*===============================================================*/
+
+#include <rtems.h>
+#include <rtems/libi2c.h>
+
+#include <libchip/spi-fram-fm25l256.h>
+#include <rtems/libio.h>
+
+
+static spi_memdrv_t spi_fram_fm25l256_rw_drv_t = {
+ {/* public fields */
+ .ops = &spi_memdrv_rw_ops, /* operations of general memdrv */
+ .size = sizeof (spi_fram_fm25l256_rw_drv_t),
+ },
+ { /* our private fields */
+ .baudrate = 2000000,
+ .erase_before_program = false,
+ .empty_state = 0xff,
+ .page_size = 0x8000, /* 32K programming page size in bytes */
+ .sector_size = 1, /* erase sector size in bytes */
+ .mem_size = 0x8000, /* 32K total capacity in bytes */
+ }
+};
+
+rtems_libi2c_drv_t *spi_fram_fm25l256_rw_driver_descriptor =
+&spi_fram_fm25l256_rw_drv_t.libi2c_drv_entry;
+
+static spi_memdrv_t spi_fram_fm25l256_ro_drv_t = {
+ {/* public fields */
+ .ops = &spi_memdrv_ro_ops, /* operations of general memdrv */
+ .size = sizeof (spi_fram_fm25l256_ro_drv_t),
+ },
+ { /* our private fields */
+ .baudrate = 2000000,
+ .erase_before_program = false,
+ .empty_state = 0xff,
+ .page_size = 0x8000, /* 32k programming page size in bytes */
+ .sector_size = 1, /* erase sector size in bytes */
+ .mem_size = 0x8000, /* 32k total capacity in bytes */
+ }
+};
+
+rtems_libi2c_drv_t *spi_fram_fm25l256_ro_driver_descriptor =
+&spi_fram_fm25l256_ro_drv_t.libi2c_drv_entry;
diff --git a/bsps/shared/dev/i2c/spi-memdrv.c b/bsps/shared/dev/i2c/spi-memdrv.c
new file mode 100644
index 0000000000..593029732e
--- /dev/null
+++ b/bsps/shared/dev/i2c/spi-memdrv.c
@@ -0,0 +1,442 @@
+/*===============================================================*\
+| Project: SPI driver for spi memory devices |
++-----------------------------------------------------------------+
+| Copyright (c) 2008 |
+| Embedded Brains GmbH |
+| Obere Lagerstr. 30 |
+| D-82178 Puchheim |
+| Germany |
+| rtems@embedded-brains.de |
++-----------------------------------------------------------------+
+| The license and distribution terms for this file may be |
+| found in the file LICENSE in this distribution or at |
+| |
+| http://www.rtems.org/license/LICENSE. |
+| |
++-----------------------------------------------------------------+
+\*===============================================================*/
+/*
+ * FIXME: currently, this driver only supports read/write accesses
+ * erase accesses are to be completed
+ */
+
+
+#include <rtems.h>
+#include <rtems/libi2c.h>
+
+#include <libchip/spi-memdrv.h>
+#include <rtems/libio.h>
+
+#define SPI_MEM_CMD_WREN 0x06
+#define SPI_MEM_CMD_WRDIS 0x04
+#define SPI_MEM_CMD_RDID 0x9F
+#define SPI_MEM_CMD_RDSR 0x05
+#define SPI_MEM_CMD_WRSR 0x01
+#define SPI_MEM_CMD_READ 0x03
+#define SPI_MEM_CMD_PP 0x02 /* page program */
+#define SPI_MEM_CMD_SE 0xD8 /* sector erase */
+#define SPI_MEM_CMD_BE 0xC7 /* bulk erase */
+#define SPI_MEM_CMD_DP 0xB9 /* deep power down */
+#define SPI_MEM_CMD_RES 0xAB /* release from deep power down */
+
+/*=========================================================================*\
+| Function: |
+\*-------------------------------------------------------------------------*/
+static rtems_status_code spi_memdrv_minor2param_ptr
+(
+/*-------------------------------------------------------------------------*\
+| Purpose: |
+| translate given minor device number to param pointer |
++---------------------------------------------------------------------------+
+| Input Parameters: |
+\*-------------------------------------------------------------------------*/
+ rtems_device_minor_number minor, /* minor number of device */
+ spi_memdrv_param_t **param_ptr /* ptr to param ptr */
+)
+/*-------------------------------------------------------------------------*\
+| Return Value: |
+| o = ok or error code |
+\*=========================================================================*/
+{
+ rtems_status_code rc = RTEMS_SUCCESSFUL;
+ spi_memdrv_t *drv_ptr;
+
+ if (rc == RTEMS_SUCCESSFUL) {
+ rc = -rtems_libi2c_ioctl(minor,
+ RTEMS_LIBI2C_IOCTL_GET_DRV_T,
+ &drv_ptr);
+ }
+ if ((rc == RTEMS_SUCCESSFUL) &&
+ (drv_ptr->libi2c_drv_entry.size != sizeof(spi_memdrv_t))) {
+ rc = RTEMS_INVALID_SIZE;
+ }
+ if (rc == RTEMS_SUCCESSFUL) {
+ *param_ptr = &(drv_ptr->spi_memdrv_param);
+ }
+ return rc;
+}
+
+/*=========================================================================*\
+| Function: |
+\*-------------------------------------------------------------------------*/
+static rtems_status_code spi_memdrv_wait_ms
+(
+/*-------------------------------------------------------------------------*\
+| Purpose: |
+| wait a certain interval given in ms |
++---------------------------------------------------------------------------+
+| Input Parameters: |
+\*-------------------------------------------------------------------------*/
+ int ms /* time to wait in milliseconds */
+)
+/*-------------------------------------------------------------------------*\
+| Return Value: |
+| o = ok or error code |
+\*=========================================================================*/
+{
+ rtems_interval ticks_per_second;
+
+ ticks_per_second = rtems_clock_get_ticks_per_second();
+ (void) rtems_task_wake_after(ticks_per_second * ms / 1000);
+ return 0;
+}
+
+/*=========================================================================*\
+| Function: |
+\*-------------------------------------------------------------------------*/
+rtems_status_code spi_memdrv_write
+(
+/*-------------------------------------------------------------------------*\
+| Purpose: |
+| write a block of data to flash |
++---------------------------------------------------------------------------+
+| Input Parameters: |
+\*-------------------------------------------------------------------------*/
+ rtems_device_major_number major, /* major device number */
+ rtems_device_minor_number minor, /* minor device number */
+ void *arg /* ptr to write argument struct */
+)
+/*-------------------------------------------------------------------------*\
+| Return Value: |
+| o = ok or error code |
+\*=========================================================================*/
+{
+ rtems_status_code rc = RTEMS_SUCCESSFUL;
+ rtems_libio_rw_args_t *rwargs = arg;
+ off_t off = rwargs->offset;
+ int cnt = rwargs->count;
+ unsigned char *buf = (unsigned char *)rwargs->buffer;
+ int bytes_sent = 0;
+ int curr_cnt;
+ unsigned char cmdbuf[4];
+ int ret_cnt = 0;
+ int cmd_size;
+ spi_memdrv_param_t *mem_param_ptr;
+ rtems_libi2c_tfr_mode_t tfr_mode = {
+ .baudrate = 20000000, /* maximum bits per second */
+ .bits_per_char = 8, /* how many bits per byte/word/longword? */
+ .lsb_first = FALSE, /* FALSE: send MSB first */
+ .clock_inv = FALSE, /* FALSE: non-inverted clock (high active) */
+ .clock_phs = FALSE /* FALSE: clock starts in middle of data tfr */
+ } ;
+
+ /*
+ * get mem parameters
+ */
+ if (rc == RTEMS_SUCCESSFUL) {
+ rc = spi_memdrv_minor2param_ptr(minor,&mem_param_ptr);
+ }
+ /*
+ * check arguments
+ */
+ if (rc == RTEMS_SUCCESSFUL) {
+ if ((cnt <= 0) ||
+ (cnt > mem_param_ptr->mem_size) ||
+ (off > (mem_param_ptr->mem_size-cnt))) {
+ rc = RTEMS_INVALID_SIZE;
+ }
+ else if (buf == NULL) {
+ rc = RTEMS_INVALID_ADDRESS;
+ }
+ }
+ while ((rc == RTEMS_SUCCESSFUL) &&
+ (cnt > bytes_sent)) {
+ curr_cnt = cnt - bytes_sent;
+ if ((mem_param_ptr->page_size > 0) &&
+ (off / mem_param_ptr->page_size) !=
+ ((off+curr_cnt+1) / mem_param_ptr->page_size)) {
+ curr_cnt = mem_param_ptr->page_size - (off % mem_param_ptr->page_size);
+ }
+ /*
+ * select device, set transfer mode, address device
+ */
+ if (rc == RTEMS_SUCCESSFUL) {
+ rc = rtems_libi2c_send_start(minor);
+ }
+ /*
+ * set transfer mode
+ */
+ if (rc == RTEMS_SUCCESSFUL) {
+ tfr_mode.baudrate = mem_param_ptr->baudrate;
+ rc = -rtems_libi2c_ioctl(minor,
+ RTEMS_LIBI2C_IOCTL_SET_TFRMODE,
+ &tfr_mode);
+ }
+
+ /*
+ * address device
+ */
+ if (rc == RTEMS_SUCCESSFUL) {
+ rc = rtems_libi2c_send_addr(minor,TRUE);
+ }
+
+ /*
+ * send write_enable command
+ */
+ if (rc == RTEMS_SUCCESSFUL) {
+ cmdbuf[0] = SPI_MEM_CMD_WREN;
+ ret_cnt = rtems_libi2c_write_bytes(minor,cmdbuf,1);
+ if (ret_cnt < 0) {
+ rc = -ret_cnt;
+ }
+ }
+ /*
+ * terminate transfer
+ */
+ if (rc == RTEMS_SUCCESSFUL) {
+ rc = rtems_libi2c_send_stop(minor);
+ }
+ /*
+ * select device, set transfer mode
+ */
+ if (rc == RTEMS_SUCCESSFUL) {
+ rc = rtems_libi2c_send_start(minor);
+ }
+
+ /*
+ * address device
+ */
+ if (rc == RTEMS_SUCCESSFUL) {
+ rc = rtems_libi2c_send_addr(minor,TRUE);
+ }
+
+ /*
+ * set transfer mode
+ */
+ if (rc == RTEMS_SUCCESSFUL) {
+ rc = -rtems_libi2c_ioctl(minor,
+ RTEMS_LIBI2C_IOCTL_SET_TFRMODE,
+ &tfr_mode);
+ }
+ /*
+ * send "page program" command and address
+ */
+ if (rc == RTEMS_SUCCESSFUL) {
+ cmdbuf[0] = SPI_MEM_CMD_PP;
+ if (mem_param_ptr->mem_size > 0x10000 /* 256*256 */) {
+ cmdbuf[1] = (off >> 16) & 0xff;
+ cmdbuf[2] = (off >> 8) & 0xff;
+ cmdbuf[3] = (off >> 0) & 0xff;
+ cmd_size = 4;
+ }
+ else if (mem_param_ptr->mem_size > 256) {
+ cmdbuf[1] = (off >> 8) & 0xff;
+ cmdbuf[2] = (off >> 0) & 0xff;
+ cmd_size = 3;
+ }
+ else {
+ cmdbuf[1] = (off >> 0) & 0xff;
+ cmd_size = 1;
+ }
+
+ ret_cnt = rtems_libi2c_write_bytes(minor,cmdbuf,cmd_size);
+ if (ret_cnt < 0) {
+ rc = -ret_cnt;
+ }
+ }
+ /*
+ * send write data
+ */
+ if (rc == RTEMS_SUCCESSFUL) {
+ ret_cnt = rtems_libi2c_write_bytes(minor,buf,curr_cnt);
+ if (ret_cnt < 0) {
+ rc = -ret_cnt;
+ }
+ }
+ /*
+ * terminate transfer
+ */
+ if (rc == RTEMS_SUCCESSFUL) {
+ rc = rtems_libi2c_send_stop(minor);
+ }
+ /*
+ * wait proper time for data to store: 5ms
+ * FIXME: select proper interval or poll, until device is finished
+ */
+ if (rc == RTEMS_SUCCESSFUL) {
+ rc = spi_memdrv_wait_ms(5);
+ }
+ /*
+ * adjust bytecount to be sent and pointers
+ */
+ bytes_sent += curr_cnt;
+ off += curr_cnt;
+ buf += curr_cnt;
+ }
+ rwargs->bytes_moved = bytes_sent;
+ return rc;
+}
+
+/*=========================================================================*\
+| Function: |
+\*-------------------------------------------------------------------------*/
+rtems_status_code spi_memdrv_read
+(
+/*-------------------------------------------------------------------------*\
+| Purpose: |
+| read a block of data from flash |
++---------------------------------------------------------------------------+
+| Input Parameters: |
+\*-------------------------------------------------------------------------*/
+ rtems_device_major_number major, /* major device number */
+ rtems_device_minor_number minor, /* minor device number */
+ void *arg /* ptr to read argument struct */
+)
+/*-------------------------------------------------------------------------*\
+| Return Value: |
+| o = ok or error code |
+\*=========================================================================*/
+{
+ rtems_status_code rc = RTEMS_SUCCESSFUL;
+ rtems_libio_rw_args_t *rwargs = arg;
+ off_t off = rwargs->offset;
+ int cnt = rwargs->count;
+ unsigned char *buf = (unsigned char *)rwargs->buffer;
+ unsigned char cmdbuf[4];
+ int ret_cnt = 0;
+ int cmd_size;
+ spi_memdrv_param_t *mem_param_ptr;
+ rtems_libi2c_tfr_mode_t tfr_mode = {
+ .baudrate = 20000000, /* maximum bits per second */
+ .bits_per_char = 8, /* how many bits per byte/word/longword? */
+ .lsb_first = FALSE, /* FALSE: send MSB first */
+ .clock_inv = FALSE, /* FALSE: non-inverted clock (high active) */
+ .clock_phs = FALSE /* FALSE: clock starts in middle of data tfr */
+ };
+
+ /*
+ * get mem parameters
+ */
+ if (rc == RTEMS_SUCCESSFUL) {
+ rc = spi_memdrv_minor2param_ptr(minor,&mem_param_ptr);
+ }
+ /*
+ * check arguments
+ */
+ if (rc == RTEMS_SUCCESSFUL) {
+ if ((cnt <= 0) ||
+ (cnt > mem_param_ptr->mem_size) ||
+ (off > (mem_param_ptr->mem_size-cnt))) {
+ rc = RTEMS_INVALID_SIZE;
+ }
+ else if (buf == NULL) {
+ rc = RTEMS_INVALID_ADDRESS;
+ }
+ }
+ /*
+ * select device, set transfer mode, address device
+ */
+ if (rc == RTEMS_SUCCESSFUL) {
+ rc = rtems_libi2c_send_start(minor);
+ }
+ /*
+ * set transfer mode
+ */
+ if (rc == RTEMS_SUCCESSFUL) {
+ tfr_mode.baudrate = mem_param_ptr->baudrate;
+ rc = -rtems_libi2c_ioctl(minor,
+ RTEMS_LIBI2C_IOCTL_SET_TFRMODE,
+ &tfr_mode);
+ }
+ /*
+ * address device
+ */
+ if (rc == RTEMS_SUCCESSFUL) {
+ rc = rtems_libi2c_send_addr(minor,TRUE);
+ }
+
+ if (off >= mem_param_ptr->mem_size) {
+ /*
+ * HACK: beyond size of memory array? then read status register instead
+ */
+ /*
+ * send read status register command
+ */
+ if (rc == RTEMS_SUCCESSFUL) {
+ cmdbuf[0] = SPI_MEM_CMD_RDSR;
+ ret_cnt = rtems_libi2c_write_bytes(minor,cmdbuf,1);
+ if (ret_cnt < 0) {
+ rc = -ret_cnt;
+ }
+ }
+ }
+ else {
+ /*
+ * send read command and address
+ */
+ if (rc == RTEMS_SUCCESSFUL) {
+ cmdbuf[0] = SPI_MEM_CMD_READ;
+ if (mem_param_ptr->mem_size > 0x10000 /* 256*256 */) {
+ cmdbuf[1] = (off >> 16) & 0xff;
+ cmdbuf[2] = (off >> 8) & 0xff;
+ cmdbuf[3] = (off >> 0) & 0xff;
+ cmd_size = 4;
+ }
+ else if (mem_param_ptr->mem_size > 256) {
+ cmdbuf[1] = (off >> 8) & 0xff;
+ cmdbuf[2] = (off >> 0) & 0xff;
+ cmd_size = 3;
+ }
+ else {
+ cmdbuf[1] = (off >> 0) & 0xff;
+ cmd_size = 1;
+ }
+ ret_cnt = rtems_libi2c_write_bytes(minor,cmdbuf,cmd_size);
+ if (ret_cnt < 0) {
+ rc = -ret_cnt;
+ }
+ }
+ }
+ /*
+ * fetch read data
+ */
+ if (rc == RTEMS_SUCCESSFUL) {
+ ret_cnt = rtems_libi2c_read_bytes (minor,buf,cnt);
+ if (ret_cnt < 0) {
+ rc = -ret_cnt;
+ }
+ }
+
+ /*
+ * terminate transfer
+ */
+ if (rc == RTEMS_SUCCESSFUL) {
+ rc = rtems_libi2c_send_stop(minor);
+ }
+ rwargs->bytes_moved = (rc == RTEMS_SUCCESSFUL) ? ret_cnt : 0;
+
+ return rc;
+}
+
+/*
+ * driver operation tables
+ */
+rtems_driver_address_table spi_memdrv_rw_ops = {
+ .read_entry = spi_memdrv_read,
+ .write_entry = spi_memdrv_write
+};
+
+rtems_driver_address_table spi_memdrv_ro_ops = {
+ .read_entry = spi_memdrv_read,
+};
+
diff --git a/bsps/shared/dev/i2c/spi-sd-card.c b/bsps/shared/dev/i2c/spi-sd-card.c
new file mode 100644
index 0000000000..a343f7faa8
--- /dev/null
+++ b/bsps/shared/dev/i2c/spi-sd-card.c
@@ -0,0 +1,1322 @@
+/**
+ * @file
+ *
+ * @brief SD Card LibI2C driver.
+ */
+
+/*
+ * Copyright (c) 2008
+ * Embedded Brains GmbH
+ * Obere Lagerstr. 30
+ * D-82178 Puchheim
+ * Germany
+ * rtems@embedded-brains.de
+ *
+ * The license and distribution terms for this file may be
+ * found in the file LICENSE in this distribution or at
+ * http://www.rtems.org/license/LICENSE.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <inttypes.h>
+
+#include <rtems.h>
+#include <rtems/libi2c.h>
+#include <rtems/libio.h>
+#include <rtems/diskdevs.h>
+#include <rtems/blkdev.h>
+
+#include <libchip/spi-sd-card.h>
+
+#include <rtems/status-checks.h>
+
+/**
+ * @name Integer to and from Byte-Stream Converter
+ * @{
+ */
+
+static inline uint16_t sd_card_get_uint16( const uint8_t *s)
+{
+ return (uint16_t) ((s [0] << 8) | s [1]);
+}
+
+static inline uint32_t sd_card_get_uint32( const uint8_t *s)
+{
+ return ((uint32_t) s [0] << 24) | ((uint32_t) s [1] << 16) | ((uint32_t) s [2] << 8) | (uint32_t) s [3];
+}
+
+static inline void sd_card_put_uint16( uint16_t v, uint8_t *s)
+{
+ *s++ = (uint8_t) (v >> 8);
+ *s = (uint8_t) (v);
+}
+
+static inline void sd_card_put_uint32( uint32_t v, uint8_t *s)
+{
+ *s++ = (uint8_t) (v >> 24);
+ *s++ = (uint8_t) (v >> 16);
+ *s++ = (uint8_t) (v >> 8);
+ *s = (uint8_t) (v);
+}
+
+/** @} */
+
+#define SD_CARD_BUSY_TOKEN 0
+
+#define SD_CARD_BLOCK_SIZE_DEFAULT 512
+
+#define SD_CARD_COMMAND_RESPONSE_START 7
+
+/**
+ * @name Commands
+ * @{
+ */
+
+#define SD_CARD_CMD_GO_IDLE_STATE 0
+#define SD_CARD_CMD_SEND_OP_COND 1
+#define SD_CARD_CMD_SEND_IF_COND 8
+#define SD_CARD_CMD_SEND_CSD 9
+#define SD_CARD_CMD_SEND_CID 10
+#define SD_CARD_CMD_STOP_TRANSMISSION 12
+#define SD_CARD_CMD_SEND_STATUS 13
+#define SD_CARD_CMD_SET_BLOCKLEN 16
+#define SD_CARD_CMD_READ_SINGLE_BLOCK 17
+#define SD_CARD_CMD_READ_MULTIPLE_BLOCK 18
+#define SD_CARD_CMD_SET_BLOCK_COUNT 23
+#define SD_CARD_CMD_WRITE_BLOCK 24
+#define SD_CARD_CMD_WRITE_MULTIPLE_BLOCK 25
+#define SD_CARD_CMD_PROGRAM_CSD 27
+#define SD_CARD_CMD_SET_WRITE_PROT 28
+#define SD_CARD_CMD_CLR_WRITE_PROT 29
+#define SD_CARD_CMD_SEND_WRITE_PROT 30
+#define SD_CARD_CMD_TAG_SECTOR_START 32
+#define SD_CARD_CMD_TAG_SECTOR_END 33
+#define SD_CARD_CMD_UNTAG_SECTOR 34
+#define SD_CARD_CMD_TAG_ERASE_GROUP_START 35
+#define SD_CARD_CMD_TAG_ERASE_GROUP_END 36
+#define SD_CARD_CMD_UNTAG_ERASE_GROUP 37
+#define SD_CARD_CMD_ERASE 38
+#define SD_CARD_CMD_LOCK_UNLOCK 42
+#define SD_CARD_CMD_APP_CMD 55
+#define SD_CARD_CMD_GEN_CND 56
+#define SD_CARD_CMD_READ_OCR 58
+#define SD_CARD_CMD_CRC_ON_OFF 59
+
+/** @} */
+
+/**
+ * @name Application Commands
+ * @{
+ */
+
+#define SD_CARD_ACMD_SD_SEND_OP_COND 41
+
+/** @} */
+
+/**
+ * @name Command Flags
+ * @{
+ */
+
+#define SD_CARD_FLAG_HCS 0x40000000U
+
+#define SD_CARD_FLAG_VHS_2_7_TO_3_3 0x00000100U
+
+#define SD_CARD_FLAG_CHECK_PATTERN 0x000000aaU
+
+/** @} */
+
+/**
+ * @name Command Fields
+ * @{
+ */
+
+#define SD_CARD_COMMAND_SET_COMMAND( c, cmd) (c) [1] = (uint8_t) (0x40 + ((cmd) & 0x3f))
+
+#define SD_CARD_COMMAND_SET_ARGUMENT( c, arg) sd_card_put_uint32( (arg), &((c) [2]))
+
+#define SD_CARD_COMMAND_SET_CRC7( c, crc7) ((c) [6] = ((crc7) << 1) | 1U)
+
+#define SD_CARD_COMMAND_GET_CRC7( c) ((c) [6] >> 1)
+
+/** @} */
+
+/**
+ * @name Response Fields
+ * @{
+ */
+
+#define SD_CARD_IS_RESPONSE( r) (((r) & 0x80) == 0)
+
+#define SD_CARD_IS_ERRORLESS_RESPONSE( r) (((r) & 0x7e) == 0)
+
+#define SD_CARD_IS_NOT_IDLE_RESPONSE( r) (((r) & 0x81) == 0)
+
+#define SD_CARD_IS_DATA_ERROR( r) (((r) & 0xe0) == 0)
+
+#define SD_CARD_IS_DATA_REJECTED( r) (((r) & 0x1f) != 0x05)
+
+/** @} */
+
+/**
+ * @name Card Identification
+ * @{
+ */
+
+#define SD_CARD_CID_SIZE 16
+
+#define SD_CARD_CID_GET_MID( cid) ((cid) [0])
+#define SD_CARD_CID_GET_OID( cid) sd_card_get_uint16( cid + 1)
+#define SD_CARD_CID_GET_PNM( cid, i) ((char) (cid) [3 + (i)])
+#define SD_CARD_CID_GET_PRV( cid) ((cid) [9])
+#define SD_CARD_CID_GET_PSN( cid) sd_card_get_uint32( cid + 10)
+#define SD_CARD_CID_GET_MDT( cid) ((cid) [14])
+#define SD_CARD_CID_GET_CRC7( cid) ((cid) [15] >> 1)
+
+/** @} */
+
+/**
+ * @name Card Specific Data
+ * @{
+ */
+
+#define SD_CARD_CSD_SIZE 16
+
+#define SD_CARD_CSD_GET_CSD_STRUCTURE( csd) ((csd) [0] >> 6)
+#define SD_CARD_CSD_GET_SPEC_VERS( csd) (((csd) [0] >> 2) & 0xf)
+#define SD_CARD_CSD_GET_TAAC( csd) ((csd) [1])
+#define SD_CARD_CSD_GET_NSAC( csd) ((uint32_t) (csd) [2])
+#define SD_CARD_CSD_GET_TRAN_SPEED( csd) ((csd) [3])
+#define SD_CARD_CSD_GET_C_SIZE( csd) ((((uint32_t) (csd) [6] & 0x3) << 10) + (((uint32_t) (csd) [7]) << 2) + ((((uint32_t) (csd) [8]) >> 6) & 0x3))
+#define SD_CARD_CSD_GET_C_SIZE_MULT( csd) ((((csd) [9] & 0x3) << 1) + (((csd) [10] >> 7) & 0x1))
+#define SD_CARD_CSD_GET_READ_BLK_LEN( csd) ((uint32_t) (csd) [5] & 0xf)
+#define SD_CARD_CSD_GET_WRITE_BLK_LEN( csd) ((((uint32_t) (csd) [12] & 0x3) << 2) + ((((uint32_t) (csd) [13]) >> 6) & 0x3))
+#define SD_CARD_CSD_1_GET_C_SIZE( csd) ((((uint32_t) (csd) [7] & 0x3f) << 16) + (((uint32_t) (csd) [8]) << 8) + (uint32_t) (csd) [9])
+
+/** @} */
+
+#define SD_CARD_INVALIDATE_RESPONSE_INDEX( e) e->response_index = SD_CARD_COMMAND_SIZE
+
+/**
+ * @name Data Start and Stop Tokens
+ * @{
+ */
+
+#define SD_CARD_START_BLOCK_SINGLE_BLOCK_READ 0xfe
+#define SD_CARD_START_BLOCK_MULTIPLE_BLOCK_READ 0xfe
+#define SD_CARD_START_BLOCK_SINGLE_BLOCK_WRITE 0xfe
+#define SD_CARD_START_BLOCK_MULTIPLE_BLOCK_WRITE 0xfc
+#define SD_CARD_STOP_TRANSFER_MULTIPLE_BLOCK_WRITE 0xfd
+
+/** @} */
+
+/**
+ * @name Card Specific Data Functions
+ * @{
+ */
+
+static inline uint32_t sd_card_block_number( const uint8_t *csd)
+{
+ uint32_t size = SD_CARD_CSD_GET_C_SIZE( csd);
+ uint32_t mult = 1U << (SD_CARD_CSD_GET_C_SIZE_MULT( csd) + 2);
+ return (size + 1) * mult;
+}
+
+static inline uint32_t sd_card_capacity( const uint8_t *csd)
+{
+ uint32_t block_size = 1U << SD_CARD_CSD_GET_READ_BLK_LEN( csd);
+ return sd_card_block_number( csd) * block_size;
+}
+
+static inline uint32_t sd_card_transfer_speed( const uint8_t *csd)
+{
+ uint32_t s = SD_CARD_CSD_GET_TRAN_SPEED( csd);
+ uint32_t e = s & 0x7;
+ uint32_t m = s >> 3;
+ switch (e) {
+ case 0: s = 10000; break;
+ case 1: s = 100000; break;
+ case 2: s = 1000000; break;
+ case 3: s = 10000000; break;
+ default: s = 0; break;
+ }
+ switch (m) {
+ case 1: s *= 10; break;
+ case 2: s *= 12; break;
+ case 3: s *= 13; break;
+ case 4: s *= 15; break;
+ case 5: s *= 20; break;
+ case 6: s *= 25; break;
+ case 7: s *= 30; break;
+ case 8: s *= 35; break;
+ case 9: s *= 40; break;
+ case 10: s *= 45; break;
+ case 11: s *= 50; break;
+ case 12: s *= 55; break;
+ case 13: s *= 60; break;
+ case 14: s *= 70; break;
+ case 15: s *= 80; break;
+ default: s *= 0; break;
+ }
+ return s;
+}
+
+static inline uint32_t sd_card_access_time( const uint8_t *csd)
+{
+ uint32_t ac = SD_CARD_CSD_GET_TAAC( csd);
+ uint32_t e = ac & 0x7;
+ uint32_t m = ac >> 3;
+ switch (e) {
+ case 0: ac = 1; break;
+ case 1: ac = 10; break;
+ case 2: ac = 100; break;
+ case 3: ac = 1000; break;
+ case 4: ac = 10000; break;
+ case 5: ac = 100000; break;
+ case 6: ac = 1000000; break;
+ case 7: ac = 10000000; break;
+ default: ac = 0; break;
+ }
+ switch (m) {
+ case 1: ac *= 10; break;
+ case 2: ac *= 12; break;
+ case 3: ac *= 13; break;
+ case 4: ac *= 15; break;
+ case 5: ac *= 20; break;
+ case 6: ac *= 25; break;
+ case 7: ac *= 30; break;
+ case 8: ac *= 35; break;
+ case 9: ac *= 40; break;
+ case 10: ac *= 45; break;
+ case 11: ac *= 50; break;
+ case 12: ac *= 55; break;
+ case 13: ac *= 60; break;
+ case 14: ac *= 70; break;
+ case 15: ac *= 80; break;
+ default: ac *= 0; break;
+ }
+ return ac / 10;
+}
+
+static inline uint32_t sd_card_max_access_time( const uint8_t *csd, uint32_t transfer_speed)
+{
+ uint64_t ac = sd_card_access_time( csd);
+ uint32_t ac_100ms = transfer_speed / 80;
+ uint32_t n = SD_CARD_CSD_GET_NSAC( csd) * 100;
+ /* ac is in ns, transfer_speed in bps, max_access_time in bytes.
+ max_access_time is 100 times typical access time (taac+nsac) */
+ ac = ac * transfer_speed / 80000000;
+ ac = ac + 100*n;
+ if ((uint32_t)ac > ac_100ms)
+ return ac_100ms;
+ else
+ return (uint32_t)ac;
+}
+
+/** @} */
+
+/**
+ * @name CRC functions
+ *
+ * Based on http://en.wikipedia.org/wiki/Computation_of_CRC
+ *
+ * @{
+ */
+
+static uint8_t sd_card_compute_crc7 (uint8_t *data, size_t len)
+{
+ uint8_t e, f, crc;
+ size_t i;
+
+ crc = 0;
+ for (i = 0; i < len; i++) {
+ e = crc ^ data[i];
+ f = e ^ (e >> 4) ^ (e >> 7);
+ crc = (f << 1) ^ (f << 4);
+ }
+ return crc >> 1;
+}
+
+static uint16_t sd_card_compute_crc16 (uint8_t *data, size_t len)
+{
+ uint8_t s, t;
+ uint16_t crc;
+ size_t i;
+
+ crc = 0;
+ for (i = 0; i < len; i++) {
+ s = data[i] ^ (crc >> 8);
+ t = s ^ (s >> 4);
+ crc = (crc << 8) ^ t ^ (t << 5) ^ (t << 12);
+ }
+ return crc;
+}
+
+/** @} */
+
+/**
+ * @name Communication Functions
+ * @{
+ */
+
+static inline int sd_card_query( sd_card_driver_entry *e, uint8_t *in, int n)
+{
+ return rtems_libi2c_read_bytes( e->bus, in, n);
+}
+
+static int sd_card_wait( sd_card_driver_entry *e)
+{
+ int rv = 0;
+ int r = 0;
+ int n = 2;
+ /* For writes, the timeout is 2.5 times that of reads; since we
+ don't know if it is a write or read, assume write.
+ FIXME should actually look at R2W_FACTOR for non-HC cards. */
+ int retries = e->n_ac_max * 25 / 10;
+ /* n_ac_max/100 is supposed to be the average waiting time. To
+ approximate this, we start with waiting n_ac_max/150 and
+ gradually increase the waiting time. */
+ int wait_time_bytes = (retries + 149) / 150;
+ while (e->busy) {
+ /* Query busy tokens */
+ rv = sd_card_query( e, e->response, n);
+ RTEMS_CHECK_RV( rv, "Busy");
+
+ /* Search for non busy tokens */
+ for (r = 0; r < n; ++r) {
+ if (e->response [r] != SD_CARD_BUSY_TOKEN) {
+ e->busy = false;
+ return 0;
+ }
+ }
+ retries -= n;
+ if (retries <= 0) {
+ return -RTEMS_TIMEOUT;
+ }
+
+ if (e->schedule_if_busy) {
+ uint64_t wait_time_us = wait_time_bytes;
+ wait_time_us *= 8000000;
+ wait_time_us /= e->transfer_mode.baudrate;
+ rtems_task_wake_after( RTEMS_MICROSECONDS_TO_TICKS(wait_time_us));
+ retries -= wait_time_bytes;
+ wait_time_bytes = wait_time_bytes * 15 / 10;
+ } else {
+ n = SD_CARD_COMMAND_SIZE;
+ }
+ }
+ return 0;
+}
+
+static int sd_card_send_command( sd_card_driver_entry *e, uint32_t command, uint32_t argument)
+{
+ int rv = 0;
+ rtems_libi2c_read_write_t rw = {
+ .rd_buf = e->response,
+ .wr_buf = e->command,
+ .byte_cnt = SD_CARD_COMMAND_SIZE
+ };
+ int r = 0;
+ uint8_t crc7;
+
+ SD_CARD_INVALIDATE_RESPONSE_INDEX( e);
+
+ /* Wait until card is not busy */
+ rv = sd_card_wait( e);
+ RTEMS_CHECK_RV( rv, "Wait");
+
+ /* Write command and read response */
+ SD_CARD_COMMAND_SET_COMMAND( e->command, command);
+ SD_CARD_COMMAND_SET_ARGUMENT( e->command, argument);
+ crc7 = sd_card_compute_crc7( e->command + 1, 5);
+ SD_CARD_COMMAND_SET_CRC7( e->command, crc7);
+ rv = rtems_libi2c_ioctl( e->bus, RTEMS_LIBI2C_IOCTL_READ_WRITE, &rw);
+ RTEMS_CHECK_RV( rv, "Write command and read response");
+
+ /* Check respose */
+ for (r = SD_CARD_COMMAND_RESPONSE_START; r < SD_CARD_COMMAND_SIZE; ++r) {
+ RTEMS_DEBUG_PRINT( "Token [%02u]: 0x%02x\n", r, e->response [r]);
+ e->response_index = r;
+ if (SD_CARD_IS_RESPONSE( e->response [r])) {
+ if (SD_CARD_IS_ERRORLESS_RESPONSE( e->response [r])) {
+ return 0;
+ } else {
+ RTEMS_SYSLOG_ERROR( "Command error [%02i]: 0x%02" PRIx8 "\n", r, e->response [r]);
+ goto sd_card_send_command_error;
+ }
+ } else if (e->response [r] != SD_CARD_IDLE_TOKEN) {
+ RTEMS_SYSLOG_ERROR( "Unexpected token [%02i]: 0x%02" PRIx8 "\n", r, e->response [r]);
+ goto sd_card_send_command_error;
+ }
+ }
+
+ RTEMS_SYSLOG_ERROR( "Timeout\n");
+
+sd_card_send_command_error:
+
+ RTEMS_SYSLOG_ERROR( "Response:");
+ for (r = 0; r < SD_CARD_COMMAND_SIZE; ++r) {
+ if (e->response_index == r) {
+ RTEMS_SYSLOG_PRINT( " %02" PRIx8 ":[%02" PRIx8 "]", e->command [r], e->response [r]);
+ } else {
+ RTEMS_SYSLOG_PRINT( " %02" PRIx8 ":%02" PRIx8 "", e->command [r], e->response [r]);
+ }
+ }
+ RTEMS_SYSLOG_PRINT( "\n");
+
+ return -RTEMS_IO_ERROR;
+}
+
+static int sd_card_send_register_command( sd_card_driver_entry *e, uint32_t command, uint32_t argument, uint32_t *reg)
+{
+ int rv = 0;
+ uint8_t crc7;
+
+ rv = sd_card_send_command( e, command, argument);
+ RTEMS_CHECK_RV( rv, "Send command");
+
+ if (e->response_index + 5 > SD_CARD_COMMAND_SIZE) {
+ /*
+ * TODO: If this happens in the wild we need to implement a
+ * more sophisticated response query.
+ */
+ RTEMS_SYSLOG_ERROR( "Unexpected response position\n");
+ return -RTEMS_IO_ERROR;
+ }
+
+ crc7 = sd_card_compute_crc7( e->response + e->response_index, 5);
+ if (crc7 != SD_CARD_COMMAND_GET_CRC7( e->response + e->response_index) &&
+ SD_CARD_COMMAND_GET_CRC7( e->response + e->response_index) != 0x7f) {
+ RTEMS_SYSLOG_ERROR( "CRC check failed on register command\n");
+ return -RTEMS_IO_ERROR;
+ }
+
+ *reg = sd_card_get_uint32( e->response + e->response_index + 1);
+
+ return 0;
+}
+
+static int sd_card_stop_multiple_block_read( sd_card_driver_entry *e)
+{
+ int rv = 0;
+ uint8_t crc7;
+
+ SD_CARD_COMMAND_SET_COMMAND( e->command, SD_CARD_CMD_STOP_TRANSMISSION);
+ SD_CARD_COMMAND_SET_ARGUMENT( e->command, 0);
+ /*crc7 = sd_card_compute_crc7( e->command + 1, 5);*/
+ crc7 = 0x30; /* Help compiler - command and argument are constants */
+ SD_CARD_COMMAND_SET_CRC7( e->command, crc7);
+ rv = rtems_libi2c_write_bytes( e->bus, e->command, SD_CARD_COMMAND_SIZE);
+ RTEMS_CHECK_RV( rv, "Write stop transfer token");
+
+ return 0;
+}
+
+static int sd_card_stop_multiple_block_write( sd_card_driver_entry *e)
+{
+ int rv = 0;
+ uint8_t stop_transfer [3] = { SD_CARD_IDLE_TOKEN, SD_CARD_STOP_TRANSFER_MULTIPLE_BLOCK_WRITE, SD_CARD_IDLE_TOKEN };
+
+ /* Wait until card is not busy */
+ rv = sd_card_wait( e);
+ RTEMS_CHECK_RV( rv, "Wait");
+
+ /* Send stop token */
+ rv = rtems_libi2c_write_bytes( e->bus, stop_transfer, 3);
+ RTEMS_CHECK_RV( rv, "Write stop transfer token");
+
+ /* Card is now busy */
+ e->busy = true;
+
+ return 0;
+}
+
+static int sd_card_read( sd_card_driver_entry *e, uint8_t start_token, uint8_t *in, int n)
+{
+ int rv = 0;
+
+ /* Discard command response */
+ int r = e->response_index + 1;
+
+ /* Standard response size */
+ int response_size = SD_CARD_COMMAND_SIZE;
+
+ /* Where the response is stored */
+ uint8_t *response = e->response;
+
+ /* Data input index */
+ int i = 0;
+
+ /* CRC check of data */
+ uint16_t crc16;
+
+ /* Maximum number of tokens to read. */
+ int retries = e->n_ac_max;
+
+ SD_CARD_INVALIDATE_RESPONSE_INDEX( e);
+
+ while (true) {
+ RTEMS_DEBUG_PRINT( "Search from %u to %u\n", r, response_size - 1);
+
+ /* Search the data start token in in current response buffer */
+ retries -= (response_size - r);
+ while (r < response_size) {
+ RTEMS_DEBUG_PRINT( "Token [%02u]: 0x%02x\n", r, response [r]);
+ if (response [r] == start_token) {
+ /* Discard data start token */
+ ++r;
+ goto sd_card_read_start;
+ } else if (SD_CARD_IS_DATA_ERROR( response [r])) {
+ RTEMS_SYSLOG_ERROR( "Data error token [%02i]: 0x%02" PRIx8 "\n", r, response [r]);
+ return -RTEMS_IO_ERROR;
+ } else if (response [r] != SD_CARD_IDLE_TOKEN) {
+ RTEMS_SYSLOG_ERROR( "Unexpected token [%02i]: 0x%02" PRIx8 "\n", r, response [r]);
+ return -RTEMS_IO_ERROR;
+ }
+ ++r;
+ }
+
+ if (retries <= 0) {
+ RTEMS_SYSLOG_ERROR( "Timeout\n");
+ return -RTEMS_IO_ERROR;
+ }
+
+ if (e->schedule_if_busy)
+ rtems_task_wake_after( RTEMS_YIELD_PROCESSOR);
+
+ /* Query more. We typically have to wait between 10 and 100
+ bytes. To reduce overhead, read the response in chunks of
+ 50 bytes - this doesn't introduce too much copy overhead
+ but does allow SPI DMA transfers to work efficiently. */
+ response = in;
+ response_size = 50;
+ if (response_size > n)
+ response_size = n;
+ rv = sd_card_query( e, response, response_size);
+ RTEMS_CHECK_RV( rv, "Query data start token");
+
+ /* Reset start position */
+ r = 0;
+ }
+
+sd_card_read_start:
+
+ /* Read data */
+ while (r < response_size && i < n) {
+ in [i++] = response [r++];
+ }
+
+ /* Read more data? */
+ if (i < n) {
+ rv = sd_card_query( e, &in [i], n - i);
+ RTEMS_CHECK_RV( rv, "Read data");
+ i += rv;
+ }
+
+ /* Read CRC 16 and N_RC */
+ rv = sd_card_query( e, e->response, 3);
+ RTEMS_CHECK_RV( rv, "Read CRC 16");
+
+ crc16 = sd_card_compute_crc16 (in, n);
+ if ((e->response[0] != ((crc16 >> 8) & 0xff)) ||
+ (e->response[1] != (crc16 & 0xff))) {
+ RTEMS_SYSLOG_ERROR( "CRC check failed on read\n");
+ return -RTEMS_IO_ERROR;
+ }
+
+ return i;
+}
+
+static int sd_card_write( sd_card_driver_entry *e, uint8_t start_token, uint8_t *out, int n)
+{
+ int rv = 0;
+ uint8_t crc16_bytes [2] = { 0, 0 };
+ uint16_t crc16;
+
+ /* Data output index */
+ int o = 0;
+
+ /* Wait until card is not busy */
+ rv = sd_card_wait( e);
+ RTEMS_CHECK_RV( rv, "Wait");
+
+ /* Write data start token */
+ rv = rtems_libi2c_write_bytes( e->bus, &start_token, 1);
+ RTEMS_CHECK_RV( rv, "Write data start token");
+
+ /* Write data */
+ o = rtems_libi2c_write_bytes( e->bus, out, n);
+ RTEMS_CHECK_RV( o, "Write data");
+
+ /* Write CRC 16 */
+ crc16 = sd_card_compute_crc16(out, n);
+ crc16_bytes[0] = (crc16>>8) & 0xff;
+ crc16_bytes[1] = (crc16) & 0xff;
+ rv = rtems_libi2c_write_bytes( e->bus, crc16_bytes, 2);
+ RTEMS_CHECK_RV( rv, "Write CRC 16");
+
+ /* Read data response */
+ rv = sd_card_query( e, e->response, 2);
+ RTEMS_CHECK_RV( rv, "Read data response");
+ if (SD_CARD_IS_DATA_REJECTED( e->response [0])) {
+ RTEMS_SYSLOG_ERROR( "Data rejected: 0x%02" PRIx8 "\n", e->response [0]);
+ return -RTEMS_IO_ERROR;
+ }
+
+ /* Card is now busy */
+ e->busy = true;
+
+ return o;
+}
+
+static inline rtems_status_code sd_card_start( sd_card_driver_entry *e)
+{
+ rtems_status_code sc = RTEMS_SUCCESSFUL;
+ int rv = 0;
+
+ sc = rtems_libi2c_send_start( e->bus);
+ RTEMS_CHECK_SC( sc, "Send start");
+
+ rv = rtems_libi2c_ioctl( e->bus, RTEMS_LIBI2C_IOCTL_SET_TFRMODE, &e->transfer_mode);
+ RTEMS_CHECK_RV_SC( rv, "Set transfer mode");
+
+ sc = rtems_libi2c_send_addr( e->bus, 1);
+ RTEMS_CHECK_SC( sc, "Send address");
+
+ return RTEMS_SUCCESSFUL;
+}
+
+static inline rtems_status_code sd_card_stop( sd_card_driver_entry *e)
+{
+ rtems_status_code sc = RTEMS_SUCCESSFUL;
+
+ sc = rtems_libi2c_send_stop( e->bus);
+ RTEMS_CHECK_SC( sc, "Send stop");
+
+ return RTEMS_SUCCESSFUL;
+}
+
+static rtems_status_code sd_card_init( sd_card_driver_entry *e)
+{
+ rtems_status_code sc = RTEMS_SUCCESSFUL;
+ int rv = 0;
+ uint8_t block [SD_CARD_BLOCK_SIZE_DEFAULT];
+ uint32_t transfer_speed = 0;
+ uint32_t read_block_size = 0;
+ uint32_t write_block_size = 0;
+ uint8_t csd_structure = 0;
+ uint64_t capacity = 0;
+ uint8_t crc7;
+
+ /* Assume first that we have a SD card and not a MMC card */
+ bool assume_sd = true;
+
+ /*
+ * Assume high capacity until proven wrong (applies to SD and not yet
+ * existing MMC).
+ */
+ bool high_capacity = true;
+
+ bool do_cmd58 = true;
+ uint32_t cmd_arg = 0;
+ uint32_t if_cond_test = SD_CARD_FLAG_VHS_2_7_TO_3_3 | SD_CARD_FLAG_CHECK_PATTERN;
+ uint32_t if_cond_reg = if_cond_test;
+
+ /* Start */
+ sc = sd_card_start( e);
+ RTEMS_CLEANUP_SC( sc, sd_card_driver_init_cleanup, "Start");
+
+ /* Wait until card is not busy */
+ rv = sd_card_wait( e);
+ RTEMS_CLEANUP_RV_SC( rv, sc, sd_card_driver_init_cleanup, "Wait");
+
+ /* Send idle tokens for at least 74 clock cycles with active chip select */
+ memset( block, SD_CARD_IDLE_TOKEN, SD_CARD_BLOCK_SIZE_DEFAULT);
+ rv = rtems_libi2c_write_bytes( e->bus, block, SD_CARD_BLOCK_SIZE_DEFAULT);
+ RTEMS_CLEANUP_RV_SC( rv, sc, sd_card_driver_init_cleanup, "Active chip select delay");
+
+ /* Stop */
+ sc = sd_card_stop( e);
+ RTEMS_CHECK_SC( sc, "Stop");
+
+ /* Start with inactive chip select */
+ sc = rtems_libi2c_send_start( e->bus);
+ RTEMS_CHECK_SC( sc, "Send start");
+
+ /* Set transfer mode */
+ rv = rtems_libi2c_ioctl( e->bus, RTEMS_LIBI2C_IOCTL_SET_TFRMODE, &e->transfer_mode);
+ RTEMS_CLEANUP_RV_SC( rv, sc, sd_card_driver_init_cleanup, "Set transfer mode");
+
+ /* Send idle tokens with inactive chip select */
+ rv = sd_card_query( e, e->response, SD_CARD_COMMAND_SIZE);
+ RTEMS_CLEANUP_RV_SC( rv, sc, sd_card_driver_init_cleanup, "Inactive chip select delay");
+
+ /* Activate chip select */
+ sc = rtems_libi2c_send_addr( e->bus, 1);
+ RTEMS_CLEANUP_SC( sc, sd_card_driver_init_cleanup, "Send address");
+
+ /* Stop multiple block write */
+ sd_card_stop_multiple_block_write( e);
+
+ /* Get card status */
+ sd_card_send_command( e, SD_CARD_CMD_SEND_STATUS, 0);
+
+ /* Stop multiple block read */
+ sd_card_stop_multiple_block_read( e);
+
+ /* Switch to SPI mode */
+ rv = sd_card_send_command( e, SD_CARD_CMD_GO_IDLE_STATE, 0);
+ RTEMS_CLEANUP_RV_SC( rv, sc, sd_card_driver_init_cleanup, "Send: SD_CARD_CMD_GO_IDLE_STATE");
+
+ /*
+ * Get interface condition, CMD8. This is new for SD 2.x and enables
+ * getting the High Capacity Support flag HCS and checks that the
+ * voltage is right. Some MMCs accept this command but will still fail
+ * on ACMD41. SD 1.x cards will fails this command and do not support
+ * HCS (> 2G capacity).
+ */
+ rv = sd_card_send_register_command( e, SD_CARD_CMD_SEND_IF_COND, if_cond_reg, &if_cond_reg);
+
+ /*
+ * Regardless of whether CMD8 above passes or fails, send ACMD41. If
+ * card is MMC it will fail. But older SD < 2.0 (which fail CMD8) will
+ * always stay "idle" if cmd_arg is non-zero, so set to 0 above on
+ * fail.
+ */
+ if (rv < 0) {
+ /* Failed CMD8, so SD 1.x or MMC */
+ cmd_arg = 0;
+ } else {
+ cmd_arg = SD_CARD_FLAG_HCS;
+ }
+
+ /* Enable CRC */
+ sd_card_send_command( e, SD_CARD_CMD_CRC_ON_OFF, 1);
+
+ /* Initialize card */
+ while (true) {
+ if (assume_sd) {
+ /* This command (CMD55) supported by SD and (most?) MMCs */
+ rv = sd_card_send_command( e, SD_CARD_CMD_APP_CMD, 0);
+ if (rv < 0) {
+ RTEMS_SYSLOG( "CMD55 failed. Assume MMC and try CMD1\n");
+ assume_sd = false;
+ continue;
+ }
+
+ /*
+ * This command (ACMD41) only supported by SD. Always
+ * fails if MMC.
+ */
+ rv = sd_card_send_command( e, SD_CARD_ACMD_SD_SEND_OP_COND, cmd_arg);
+ if (rv < 0) {
+ /*
+ * This *will* fail for MMC. If fails, bad/no
+ * card or card is MMC, do CMD58 then CMD1.
+ */
+ RTEMS_SYSLOG( "ACMD41 failed. Assume MMC and do CMD58 (once) then CMD1\n");
+ assume_sd = false;
+ cmd_arg = SD_CARD_FLAG_HCS;
+ do_cmd58 = true;
+ continue;
+ } else {
+ /*
+ * Passed ACMD41 so SD. It is now save to
+ * check if_cond_reg from CMD8. Reject the
+ * card in case of a indicated bad voltage.
+ */
+ if (if_cond_reg != if_cond_test) {
+ RTEMS_CLEANUP_RV_SC( -1, sc, sd_card_driver_init_cleanup, "Bad voltage for SD");
+ }
+ }
+ } else {
+ /*
+ * Does not seem to be SD card. Do init for MMC.
+ * First send CMD58 once to enable check for HCS
+ * (similar to CMD8 of SD) with bits 30:29 set to 10b.
+ * This will work for MMC >= 4.2. Older cards (<= 4.1)
+ * may may not respond to CMD1 unless CMD58 is sent
+ * again with zero argument.
+ */
+ if (do_cmd58) {
+ rv = sd_card_send_command( e, SD_CARD_CMD_READ_OCR, cmd_arg);
+ RTEMS_CLEANUP_RV_SC( rv, sc, sd_card_driver_init_cleanup, "Failed CMD58 for MMC");
+
+ /* A one-shot call */
+ do_cmd58 = false;
+ }
+
+ /* Do CMD1 */
+ rv = sd_card_send_command( e, SD_CARD_CMD_SEND_OP_COND, 0);
+ if (rv < 0) {
+ if (cmd_arg != 0) {
+ /*
+ * Send CMD58 again with zero argument
+ * value. Proves card is not
+ * high_capacity.
+ */
+ cmd_arg = 0;
+ do_cmd58 = true;
+ high_capacity = false;
+ continue;
+ }
+
+ RTEMS_CLEANUP_RV_SC( rv, sc, sd_card_driver_init_cleanup, "Failed to initialize MMC");
+ }
+ }
+
+ /*
+ * Not idle?
+ *
+ * This hangs forever if the card remains not idle and sends
+ * always a valid response.
+ */
+ if (SD_CARD_IS_NOT_IDLE_RESPONSE( e->response [e->response_index])) {
+ break;
+ }
+
+ /* Invoke the scheduler */
+ rtems_task_wake_after( RTEMS_YIELD_PROCESSOR);
+ }
+
+ /* Now we know if we are SD or MMC */
+ if (assume_sd) {
+ if (cmd_arg == 0) {
+ /* SD is < 2.0 so never high capacity (<= 2G) */
+ high_capacity = 0;
+ } else {
+ uint32_t reg = 0;
+
+ /*
+ * SD is definitely 2.x. Now need to send CMD58 to get
+ * the OCR to see if the HCS bit is set (capacity > 2G)
+ * or if bit is off (capacity <= 2G, standard
+ * capacity).
+ */
+ rv = sd_card_send_register_command( e, SD_CARD_CMD_READ_OCR, 0, &reg);
+ RTEMS_CLEANUP_RV_SC( rv, sc, sd_card_driver_init_cleanup, "Failed CMD58 for SD 2.x");
+
+ /* Check HCS bit of OCR */
+ high_capacity = (reg & SD_CARD_FLAG_HCS) != 0;
+ }
+ } else {
+ /*
+ * Card is MMC. Unless already proven to be not HCS (< 4.2)
+ * must do CMD58 again to check the OCR bits 30:29.
+ */
+ if (high_capacity) {
+ uint32_t reg = 0;
+
+ /*
+ * The argument should still be correct since was never
+ * set to 0
+ */
+ rv = sd_card_send_register_command( e, SD_CARD_CMD_READ_OCR, cmd_arg, &reg);
+ RTEMS_CLEANUP_RV_SC( rv, sc, sd_card_driver_init_cleanup, "Failed CMD58 for MMC 4.2");
+
+ /* Check HCS bit of the OCR */
+ high_capacity = (reg & SD_CARD_FLAG_HCS) != 0;
+ }
+ }
+
+ /* Card Identification */
+ if (e->verbose) {
+ rv = sd_card_send_command( e, SD_CARD_CMD_SEND_CID, 0);
+ RTEMS_CLEANUP_RV_SC( rv, sc, sd_card_driver_init_cleanup, "Send: SD_CARD_CMD_SEND_CID");
+ rv = sd_card_read( e, SD_CARD_START_BLOCK_SINGLE_BLOCK_READ, block, SD_CARD_CID_SIZE);
+ RTEMS_CLEANUP_RV_SC( rv, sc, sd_card_driver_init_cleanup, "Read: SD_CARD_CMD_SEND_CID");
+ RTEMS_SYSLOG( "*** Card Identification ***\n");
+ RTEMS_SYSLOG( "Manufacturer ID : %" PRIu8 "\n", SD_CARD_CID_GET_MID( block));
+ RTEMS_SYSLOG( "OEM/Application ID : %" PRIu16 "\n", SD_CARD_CID_GET_OID( block));
+ RTEMS_SYSLOG(
+ "Product name : %c%c%c%c%c%c\n",
+ SD_CARD_CID_GET_PNM( block, 0),
+ SD_CARD_CID_GET_PNM( block, 1),
+ SD_CARD_CID_GET_PNM( block, 2),
+ SD_CARD_CID_GET_PNM( block, 3),
+ SD_CARD_CID_GET_PNM( block, 4),
+ SD_CARD_CID_GET_PNM( block, 5)
+ );
+ RTEMS_SYSLOG( "Product revision : %" PRIu8 "\n", SD_CARD_CID_GET_PRV( block));
+ RTEMS_SYSLOG( "Product serial number : %" PRIu32 "\n", SD_CARD_CID_GET_PSN( block));
+ RTEMS_SYSLOG( "Manufacturing date : %" PRIu8 "\n", SD_CARD_CID_GET_MDT( block));
+ RTEMS_SYSLOG( "7-bit CRC checksum : %" PRIu8 "\n", SD_CARD_CID_GET_CRC7( block));
+ crc7 = sd_card_compute_crc7( block, 15);
+ if (crc7 != SD_CARD_CID_GET_CRC7( block))
+ RTEMS_SYSLOG( " Failed! (computed %02" PRIx8 ")\n", crc7);
+ }
+
+ /* Card Specific Data */
+
+ /* Read CSD */
+ rv = sd_card_send_command( e, SD_CARD_CMD_SEND_CSD, 0);
+ RTEMS_CLEANUP_RV_SC( rv, sc, sd_card_driver_init_cleanup, "Send: SD_CARD_CMD_SEND_CSD");
+ rv = sd_card_read( e, SD_CARD_START_BLOCK_SINGLE_BLOCK_READ, block, SD_CARD_CSD_SIZE);
+ RTEMS_CLEANUP_RV_SC( rv, sc, sd_card_driver_init_cleanup, "Read: SD_CARD_CMD_SEND_CSD");
+
+ crc7 = sd_card_compute_crc7( block, 15);
+ if (crc7 != SD_CARD_CID_GET_CRC7( block)) {
+ RTEMS_SYSLOG( "SD_CARD_CMD_SEND_CSD CRC failed\n");
+ sc = RTEMS_IO_ERROR;
+ goto sd_card_driver_init_cleanup;
+ }
+
+ /* CSD Structure */
+ csd_structure = SD_CARD_CSD_GET_CSD_STRUCTURE( block);
+
+ /* Transfer speed and access time */
+ transfer_speed = sd_card_transfer_speed( block);
+ e->transfer_mode.baudrate = transfer_speed;
+ e->n_ac_max = sd_card_max_access_time( block, transfer_speed);
+
+ /* Block sizes and capacity */
+ if (csd_structure == 0 || !assume_sd) {
+ /* Treat MMC same as CSD Version 1.0 */
+
+ read_block_size = 1U << SD_CARD_CSD_GET_READ_BLK_LEN( block);
+ e->block_size_shift = SD_CARD_CSD_GET_WRITE_BLK_LEN( block);
+ write_block_size = 1U << e->block_size_shift;
+ if (read_block_size < write_block_size) {
+ RTEMS_SYSLOG_ERROR( "Read block size smaller than write block size\n");
+ return -RTEMS_IO_ERROR;
+ }
+ e->block_size = write_block_size;
+ e->block_number = sd_card_block_number( block);
+ capacity = sd_card_capacity( block);
+ } else if (csd_structure == 1) {
+ uint32_t c_size = SD_CARD_CSD_1_GET_C_SIZE( block);
+
+ /* Block size is fixed in CSD Version 2.0 */
+ e->block_size_shift = 9;
+ e->block_size = 512;
+
+ e->block_number = (c_size + 1) * 1024;
+ capacity = (c_size + 1) * 512 * 1024;
+ read_block_size = 512;
+ write_block_size = 512;
+
+ /* Timeout is fixed at 100ms in CSD Version 2.0 */
+ e->n_ac_max = transfer_speed / 80;
+ } else {
+ RTEMS_DO_CLEANUP_SC( RTEMS_IO_ERROR, sc, sd_card_driver_init_cleanup, "Unexpected CSD Structure number");
+ }
+
+ /* Print CSD information */
+ if (e->verbose) {
+ RTEMS_SYSLOG( "*** Card Specific Data ***\n");
+ RTEMS_SYSLOG( "CSD structure : %" PRIu8 "\n", SD_CARD_CSD_GET_CSD_STRUCTURE( block));
+ RTEMS_SYSLOG( "Spec version : %" PRIu8 "\n", SD_CARD_CSD_GET_SPEC_VERS( block));
+ RTEMS_SYSLOG( "Access time [ns] : %" PRIu32 "\n", sd_card_access_time( block));
+ RTEMS_SYSLOG( "Access time [N] : %" PRIu32 "\n", SD_CARD_CSD_GET_NSAC( block)*100);
+ RTEMS_SYSLOG( "Max access time [N] : %" PRIu32 "\n", e->n_ac_max);
+ RTEMS_SYSLOG( "Max read block size [B] : %" PRIu32 "\n", read_block_size);
+ RTEMS_SYSLOG( "Max write block size [B] : %" PRIu32 "\n", write_block_size);
+ RTEMS_SYSLOG( "Block size [B] : %" PRIu32 "\n", e->block_size);
+ RTEMS_SYSLOG( "Block number : %" PRIu32 "\n", e->block_number);
+ RTEMS_SYSLOG( "Capacity [B] : %" PRIu64 "\n", capacity);
+ RTEMS_SYSLOG( "Max transfer speed [b/s] : %" PRIu32 "\n", transfer_speed);
+ }
+
+ if (high_capacity) {
+ /* For high capacity cards the address is in blocks */
+ e->block_size_shift = 0;
+ } else if (e->block_size_shift == 10) {
+ /*
+ * Low capacity 2GByte cards with reported block size of 1024
+ * need to be set back to block size of 512 per 'Simplified
+ * Physical Layer Specification Version 2.0' section 4.3.2.
+ * Otherwise, CMD16 fails if set to 1024.
+ */
+ e->block_size_shift = 9;
+ e->block_size = 512;
+ e->block_number *= 2;
+ }
+
+ /* Set read block size */
+ rv = sd_card_send_command( e, SD_CARD_CMD_SET_BLOCKLEN, e->block_size);
+ RTEMS_CLEANUP_RV_SC( rv, sc, sd_card_driver_init_cleanup, "Send: SD_CARD_CMD_SET_BLOCKLEN");
+
+ /* Stop */
+ sc = sd_card_stop( e);
+ RTEMS_CHECK_SC( sc, "Stop");
+
+ return RTEMS_SUCCESSFUL;
+
+sd_card_driver_init_cleanup:
+
+ /* Stop */
+ sd_card_stop( e);
+
+ return sc;
+}
+/** @} */
+
+/**
+ * @name Disk Driver Functions
+ * @{
+ */
+
+static int sd_card_disk_block_read( sd_card_driver_entry *e, rtems_blkdev_request *r)
+{
+ rtems_status_code sc = RTEMS_SUCCESSFUL;
+ int rv = 0;
+ uint32_t start_address = RTEMS_BLKDEV_START_BLOCK (r) << e->block_size_shift;
+ uint32_t i = 0;
+
+#ifdef DEBUG
+ /* Check request */
+ if (r->bufs[0].block >= e->block_number) {
+ RTEMS_SYSLOG_ERROR( "Start block number out of range");
+ return -RTEMS_INTERNAL_ERROR;
+ } else if (r->bufnum > e->block_number - RTEMS_BLKDEV_START_BLOCK (r)) {
+ RTEMS_SYSLOG_ERROR( "Block count out of range");
+ return -RTEMS_INTERNAL_ERROR;
+ }
+#endif /* DEBUG */
+
+ /* Start */
+ sc = sd_card_start( e);
+ RTEMS_CLEANUP_SC_RV( sc, rv, sd_card_disk_block_read_cleanup, "Start");
+
+ if (r->bufnum == 1) {
+#ifdef DEBUG
+ /* Check buffer */
+ if (r->bufs [0].length != e->block_size) {
+ RTEMS_DO_CLEANUP_RV( -RTEMS_INTERNAL_ERROR, rv, sd_card_disk_block_read_cleanup, "Buffer and disk block size are not equal");
+ }
+ RTEMS_DEBUG_PRINT( "[01:01]: buffer = 0x%08x, size = %u\n", r->bufs [0].buffer, r->bufs [0].length);
+#endif /* DEBUG */
+
+ /* Single block read */
+ rv = sd_card_send_command( e, SD_CARD_CMD_READ_SINGLE_BLOCK, start_address);
+ RTEMS_CLEANUP_RV( rv, sd_card_disk_block_read_cleanup, "Send: SD_CARD_CMD_READ_SINGLE_BLOCK");
+ rv = sd_card_read( e, SD_CARD_START_BLOCK_SINGLE_BLOCK_READ, (uint8_t *) r->bufs [0].buffer, (int) e->block_size);
+ RTEMS_CLEANUP_RV( rv, sd_card_disk_block_read_cleanup, "Read: SD_CARD_CMD_READ_SINGLE_BLOCK");
+ } else {
+ /* Start multiple block read */
+ rv = sd_card_send_command( e, SD_CARD_CMD_READ_MULTIPLE_BLOCK, start_address);
+ RTEMS_CLEANUP_RV( rv, sd_card_disk_block_read_stop_cleanup, "Send: SD_CARD_CMD_READ_MULTIPLE_BLOCK");
+
+ /* Multiple block read */
+ for (i = 0; i < r->bufnum; ++i) {
+#ifdef DEBUG
+ /* Check buffer */
+ if (r->bufs [i].length != e->block_size) {
+ RTEMS_DO_CLEANUP_RV( -RTEMS_INTERNAL_ERROR, rv, sd_card_disk_block_read_stop_cleanup, "Buffer and disk block size are not equal");
+ }
+ RTEMS_DEBUG_PRINT( "[%02u:%02u]: buffer = 0x%08x, size = %u\n", i + 1, r->bufnum, r->bufs [i].buffer, r->bufs [i].length);
+#endif /* DEBUG */
+
+ rv = sd_card_read( e, SD_CARD_START_BLOCK_MULTIPLE_BLOCK_READ, (uint8_t *) r->bufs [i].buffer, (int) e->block_size);
+ RTEMS_CLEANUP_RV( rv, sd_card_disk_block_read_stop_cleanup, "Read block");
+ }
+
+ /* Stop multiple block read */
+ rv = sd_card_stop_multiple_block_read( e);
+ RTEMS_CLEANUP_RV( rv, sd_card_disk_block_read_cleanup, "Stop multiple block read");
+ }
+
+ /* Stop */
+ sc = sd_card_stop( e);
+ RTEMS_CHECK_SC_RV( sc, "Stop");
+
+ /* Done */
+ rtems_blkdev_request_done( r, RTEMS_SUCCESSFUL);
+
+ return 0;
+
+sd_card_disk_block_read_stop_cleanup:
+
+ /* Stop multiple block read */
+ sd_card_stop_multiple_block_read( e);
+
+sd_card_disk_block_read_cleanup:
+
+ /* Stop */
+ sd_card_stop( e);
+
+ /* Done */
+ rtems_blkdev_request_done( r, RTEMS_IO_ERROR);
+
+ return 0;
+}
+
+static int sd_card_disk_block_write( sd_card_driver_entry *e, rtems_blkdev_request *r)
+{
+ rtems_status_code sc = RTEMS_SUCCESSFUL;
+ int rv = 0;
+ uint32_t start_address = RTEMS_BLKDEV_START_BLOCK (r) << e->block_size_shift;
+ uint32_t i = 0;
+
+#ifdef DEBUG
+ /* Check request */
+ if (r->bufs[0].block >= e->block_number) {
+ RTEMS_SYSLOG_ERROR( "Start block number out of range");
+ return -RTEMS_INTERNAL_ERROR;
+ } else if (r->bufnum > e->block_number - RTEMS_BLKDEV_START_BLOCK (r)) {
+ RTEMS_SYSLOG_ERROR( "Block count out of range");
+ return -RTEMS_INTERNAL_ERROR;
+ }
+#endif /* DEBUG */
+
+ /* Start */
+ sc = sd_card_start( e);
+ RTEMS_CLEANUP_SC_RV( sc, rv, sd_card_disk_block_write_cleanup, "Start");
+
+ if (r->bufnum == 1) {
+#ifdef DEBUG
+ /* Check buffer */
+ if (r->bufs [0].length != e->block_size) {
+ RTEMS_DO_CLEANUP_RV( -RTEMS_INTERNAL_ERROR, rv, sd_card_disk_block_write_cleanup, "Buffer and disk block size are not equal");
+ }
+ RTEMS_DEBUG_PRINT( "[01:01]: buffer = 0x%08x, size = %u\n", r->bufs [0].buffer, r->bufs [0].length);
+#endif /* DEBUG */
+
+ /* Single block write */
+ rv = sd_card_send_command( e, SD_CARD_CMD_WRITE_BLOCK, start_address);
+ RTEMS_CLEANUP_RV( rv, sd_card_disk_block_write_cleanup, "Send: SD_CARD_CMD_WRITE_BLOCK");
+ rv = sd_card_write( e, SD_CARD_START_BLOCK_SINGLE_BLOCK_WRITE, (uint8_t *) r->bufs [0].buffer, (int) e->block_size);
+ RTEMS_CLEANUP_RV( rv, sd_card_disk_block_write_cleanup, "Write: SD_CARD_CMD_WRITE_BLOCK");
+ } else {
+ /* Start multiple block write */
+ rv = sd_card_send_command( e, SD_CARD_CMD_WRITE_MULTIPLE_BLOCK, start_address);
+ RTEMS_CLEANUP_RV( rv, sd_card_disk_block_write_stop_cleanup, "Send: SD_CARD_CMD_WRITE_MULTIPLE_BLOCK");
+
+ /* Multiple block write */
+ for (i = 0; i < r->bufnum; ++i) {
+#ifdef DEBUG
+ /* Check buffer */
+ if (r->bufs [i].length != e->block_size) {
+ RTEMS_DO_CLEANUP_RV( -RTEMS_INTERNAL_ERROR, rv, sd_card_disk_block_write_stop_cleanup, "Buffer and disk block size are not equal");
+ }
+ RTEMS_DEBUG_PRINT( "[%02u:%02u]: buffer = 0x%08x, size = %u\n", i + 1, r->bufnum, r->bufs [i].buffer, r->bufs [i].length);
+#endif /* DEBUG */
+
+ rv = sd_card_write( e, SD_CARD_START_BLOCK_MULTIPLE_BLOCK_WRITE, (uint8_t *) r->bufs [i].buffer, (int) e->block_size);
+ RTEMS_CLEANUP_RV( rv, sd_card_disk_block_write_stop_cleanup, "Write block");
+ }
+
+ /* Stop multiple block write */
+ rv = sd_card_stop_multiple_block_write( e);
+ RTEMS_CLEANUP_RV( rv, sd_card_disk_block_write_cleanup, "Stop multiple block write");
+ }
+
+ /* Get card status */
+ rv = sd_card_send_command( e, SD_CARD_CMD_SEND_STATUS, 0);
+ RTEMS_CHECK_RV( rv, "Send: SD_CARD_CMD_SEND_STATUS");
+
+ /* Stop */
+ sc = sd_card_stop( e);
+ RTEMS_CHECK_SC_RV( sc, "Stop");
+
+ /* Done */
+ rtems_blkdev_request_done( r, RTEMS_SUCCESSFUL);
+
+ return 0;
+
+sd_card_disk_block_write_stop_cleanup:
+
+ /* Stop multiple block write */
+ sd_card_stop_multiple_block_write( e);
+
+sd_card_disk_block_write_cleanup:
+
+ /* Get card status */
+ rv = sd_card_send_command( e, SD_CARD_CMD_SEND_STATUS, 0);
+ RTEMS_CHECK_RV( rv, "Send: SD_CARD_CMD_SEND_STATUS");
+
+ /* Stop */
+ sd_card_stop( e);
+
+ /* Done */
+ rtems_blkdev_request_done( r, RTEMS_IO_ERROR);
+
+ return 0;
+}
+
+static int sd_card_disk_ioctl( rtems_disk_device *dd, uint32_t req, void *arg)
+{
+ RTEMS_DEBUG_PRINT( "sd_card_disk_ioctl minor = %u, req = 0x%08x, arg = %p\n",
+ (unsigned)rtems_filesystem_dev_minor_t(dd->dev), (unsigned)req, arg);
+ if (req == RTEMS_BLKIO_REQUEST) {
+ rtems_device_minor_number minor = rtems_disk_get_minor_number( dd);
+ sd_card_driver_entry *e = &sd_card_driver_table [minor];
+ rtems_blkdev_request *r = (rtems_blkdev_request *) arg;
+ int (*f)( sd_card_driver_entry *, rtems_blkdev_request *);
+ uint32_t retries = e->retries;
+ int result;
+
+ switch (r->req) {
+ case RTEMS_BLKDEV_REQ_READ:
+ f = sd_card_disk_block_read;
+ break;
+ case RTEMS_BLKDEV_REQ_WRITE:
+ f = sd_card_disk_block_write;
+ break;
+ default:
+ errno = EINVAL;
+ return -1;
+ }
+ do {
+ result = f( e, r);
+ } while (retries-- > 0 && result != 0);
+ return result;
+
+ } else if (req == RTEMS_BLKIO_CAPABILITIES) {
+ *(uint32_t *) arg = RTEMS_BLKDEV_CAP_MULTISECTOR_CONT;
+ return 0;
+ } else {
+ return rtems_blkdev_ioctl( dd, req, arg );
+ }
+}
+
+static rtems_status_code sd_card_disk_init( rtems_device_major_number major, rtems_device_minor_number minor, void *arg)
+{
+ rtems_status_code sc = RTEMS_SUCCESSFUL;
+
+ /* Initialize disk IO */
+ sc = rtems_disk_io_initialize();
+ RTEMS_CHECK_SC( sc, "Initialize RTEMS disk IO");
+
+ for (minor = 0; minor < sd_card_driver_table_size; ++minor) {
+ sd_card_driver_entry *e = &sd_card_driver_table [minor];
+ dev_t dev = rtems_filesystem_make_dev_t( major, minor);
+ uint32_t retries = e->retries;
+
+ /* Initialize SD Card */
+ do {
+ sc = sd_card_init( e);
+ } while (retries-- > 0 && sc != RTEMS_SUCCESSFUL);
+ RTEMS_CHECK_SC( sc, "Initialize SD Card");
+
+ /* Create disk device */
+ sc = rtems_disk_create_phys( dev, e->block_size, e->block_number, sd_card_disk_ioctl, NULL, e->device_name);
+ RTEMS_CHECK_SC( sc, "Create disk device");
+ }
+
+ return RTEMS_SUCCESSFUL;
+}
+
+/** @} */
+
+static const rtems_driver_address_table sd_card_disk_ops = {
+ .initialization_entry = sd_card_disk_init,
+ .open_entry = rtems_blkdev_generic_open,
+ .close_entry = rtems_blkdev_generic_close,
+ .read_entry = rtems_blkdev_generic_read,
+ .write_entry = rtems_blkdev_generic_write,
+ .control_entry = rtems_blkdev_generic_ioctl
+};
+
+rtems_status_code sd_card_register( void)
+{
+ rtems_status_code sc = RTEMS_SUCCESSFUL;
+ rtems_device_major_number major = 0;
+
+ sc = rtems_io_register_driver( 0, &sd_card_disk_ops, &major);
+ RTEMS_CHECK_SC( sc, "Register disk SD Card driver");
+
+ return RTEMS_SUCCESSFUL;
+}
diff --git a/bsps/shared/dev/ide/ata.c b/bsps/shared/dev/ide/ata.c
new file mode 100644
index 0000000000..7bb3f6ec73
--- /dev/null
+++ b/bsps/shared/dev/ide/ata.c
@@ -0,0 +1,1360 @@
+/*
+ * ata.c
+ *
+ * ATA RTEMS driver. ATA driver is hardware independant implementation of
+ * ATA-2 standard, working draft X3T10/0948D, revision 4c. ATA driver bases
+ * on RTEMS IDE controller driver.
+ *
+ * Copyright (C) 2001 OKTET Ltd., St.-Petersburg, Russia
+ * Authors: Eugeny S. Mints <Eugeny.Mints@oktet.ru>
+ *
+ * The license and distribution terms for this file may be
+ * found in the file LICENSE in this distribution or at
+ * http://www.rtems.org/license/LICENSE.
+ *
+ */
+#include <errno.h>
+#include <rtems/chain.h>
+#include <assert.h>
+#include <string.h> /* for "memset" declaration */
+
+#include <rtems/diskdevs.h>
+#include <rtems/blkdev.h>
+#include <libchip/ide_ctrl_io.h>
+#include <libchip/ide_ctrl_cfg.h>
+#include <libchip/ata_internal.h>
+#include <libchip/ata.h>
+
+#define ATA_DEBUG 0
+
+#if ATA_DEBUG
+#include <stdio.h>
+bool ata_trace;
+#define ata_printf if (ata_trace) printf
+#endif
+
+#if CPU_SIMPLE_VECTORED_INTERRUPTS != TRUE
+#include <rtems/irq.h>
+#define ATA_IRQ_CHAIN_MAX_CNT 4 /* support up to 4 ATA devices */
+typedef struct {
+ rtems_irq_number name;
+ rtems_chain_control irq_chain;
+} ata_irq_chain_t;
+
+ata_irq_chain_t ata_irq_chain[ATA_IRQ_CHAIN_MAX_CNT];
+int ata_irq_chain_cnt = 0;
+#endif
+
+static rtems_id ata_lock;
+static void
+rtems_ata_lock (void)
+{
+ rtems_status_code sc = rtems_semaphore_obtain (ata_lock,
+ RTEMS_WAIT,
+ RTEMS_NO_TIMEOUT);
+ if (sc != RTEMS_SUCCESSFUL)
+ rtems_fatal_error_occurred (RTEMS_INTERNAL_ERROR);
+}
+
+static void
+rtems_ata_unlock (void)
+{
+ rtems_status_code sc = rtems_semaphore_release (ata_lock);
+ if (sc != RTEMS_SUCCESSFUL)
+ rtems_fatal_error_occurred (RTEMS_INTERNAL_ERROR);
+}
+
+#define RTEMS_ATA_LOCK_ATTRIBS \
+ (RTEMS_PRIORITY | RTEMS_BINARY_SEMAPHORE | \
+ RTEMS_INHERIT_PRIORITY | RTEMS_NO_PRIORITY_CEILING | RTEMS_LOCAL)
+
+/* FIXME: case if ATA device is FLASH device need more attention */
+#undef ATA_DEV_IS_FLASH_DISK
+
+/* Array indexed by controllers minor number */
+static ata_ide_ctrl_t ata_ide_ctrls[IDE_CTRL_MAX_MINOR_NUMBER];
+
+/*
+ * Mapping from ATA-minor numbers to
+ * controller-minor and device on this controller.
+ */
+static ata_ide_dev_t ata_devs[2 * IDE_CTRL_MAX_MINOR_NUMBER];
+static int ata_devs_number;
+
+/* Flag meaning that ATA driver has already been initialized */
+static bool ata_initialized = false;
+
+
+/* task and queue used for asynchronous I/O operations */
+static rtems_id ata_task_id;
+static rtems_id ata_queue_id;
+
+#if CPU_SIMPLE_VECTORED_INTERRUPTS == TRUE
+/* Mapping of interrupt vectors to devices */
+static rtems_chain_control ata_int_vec[ATA_MAX_RTEMS_INT_VEC_NUMBER + 1];
+#endif
+
+static void
+ata_process_request(rtems_device_minor_number ctrl_minor);
+
+static void
+ata_add_to_controller_queue(rtems_device_minor_number ctrl_minor,
+ ata_req_t *areq);
+
+/*
+ * read/write, open/close and ioctl are provided by general block device
+ * driver. Only initialization and ata-specific ioctl are here.
+ */
+
+/* ata_io_data_request --
+ * Form read/write request for an ATA device and enqueue it to
+ * IDE controller.
+ *
+ * PARAMETERS:
+ * device - device identifier
+ * req - read/write request from block device driver
+ *
+ * RETURNS:
+ * RTEMS_SUCCESSFUL on success, or error code if
+ * error occured
+ */
+static rtems_status_code
+ata_io_data_request(dev_t device, rtems_blkdev_request *req)
+{
+ ata_req_t *areq; /* ATA request */
+ rtems_device_minor_number rel_minor; /* relative minor which indexes
+ * ata_devs array
+ */
+ rtems_device_minor_number ctrl_minor;
+ uint8_t dev;
+
+ rel_minor = (rtems_filesystem_dev_minor_t(device)) /
+ ATA_MINOR_NUM_RESERVED_PER_ATA_DEVICE;
+
+ /* get controller which serves the ATA device */
+ ctrl_minor = ata_devs[rel_minor].ctrl_minor;
+
+ /* get ATA device identifier (0 or 1) */
+ dev = ata_devs[rel_minor].device;
+
+ areq = malloc(sizeof(ata_req_t));
+ if (areq == NULL)
+ {
+ rtems_blkdev_request_done(req, RTEMS_NO_MEMORY);
+ return RTEMS_SUCCESSFUL;
+ }
+
+ areq->breq = req;
+ areq->cnt = req->bufnum;
+ areq->cbuf = 0;
+ areq->pos = 0;
+
+ /* set up registers masks */
+ areq->regs.to_write = ATA_REGISTERS_POSITION;
+ areq->regs.to_read = ATA_REGISTERS_VALUE(IDE_REGISTER_STATUS);
+
+ /* choose device on the controller for which the command will be issued */
+ areq->regs.regs[IDE_REGISTER_DEVICE_HEAD] =
+ (dev << IDE_REGISTER_DEVICE_HEAD_DEV_POS);
+
+ /* Find ATA command and its type */
+ if (ATA_DEV_INFO(ctrl_minor, dev).mode_active & ATA_MODES_DMA)
+ {
+ /* XXX: never has been tested */
+ areq->type = ATA_COMMAND_TYPE_DMA;
+ if (req->req == RTEMS_BLKDEV_REQ_READ)
+ areq->regs.regs[IDE_REGISTER_COMMAND] = ATA_COMMAND_READ_DMA;
+ else
+ areq->regs.regs[IDE_REGISTER_COMMAND] = ATA_COMMAND_WRITE_DMA;
+ }
+ else
+ {
+ if (req->req == RTEMS_BLKDEV_REQ_READ)
+ {
+ areq->type = ATA_COMMAND_TYPE_PIO_IN;
+ areq->regs.regs[IDE_REGISTER_COMMAND] = ATA_COMMAND_READ_SECTORS;
+#if ATA_DEBUG
+ ata_printf("ata_io_data_request: type: READ: %lu, %lu cmd:%02x\n",
+ req->bufs[0].block, req->bufnum,
+ areq->regs.regs[IDE_REGISTER_COMMAND]);
+#endif
+ }
+ else
+ {
+ areq->type = ATA_COMMAND_TYPE_PIO_OUT;
+ areq->regs.regs[IDE_REGISTER_COMMAND] = ATA_COMMAND_WRITE_SECTORS;
+#if ATA_DEBUG
+ ata_printf("ata_io_data_request: type: WRITE: %lu, %lu cmd:%02x\n",
+ req->bufs[0].block, req->bufnum,
+ areq->regs.regs[IDE_REGISTER_COMMAND]);
+#endif
+ }
+ }
+
+ /*
+ * Fill position registers
+ */
+ if (ATA_DEV_INFO(ctrl_minor, dev).lba_avaible)
+ {
+ uint32_t start = req->bufs[0].block;
+ areq->regs.regs[IDE_REGISTER_LBA0] = (uint8_t)start;
+ areq->regs.regs[IDE_REGISTER_LBA1] = (uint8_t)(start >> 8);
+ areq->regs.regs[IDE_REGISTER_LBA2] = (uint8_t)(start >> 16);
+ /* Set as the head register write above */
+ areq->regs.regs[IDE_REGISTER_LBA3] |= (uint8_t) (start >> 24);
+ areq->regs.regs[IDE_REGISTER_LBA3] |= IDE_REGISTER_LBA3_L;
+ }
+ else
+ {
+ uint32_t count = req->bufs[0].block;
+
+ areq->regs.regs[IDE_REGISTER_SECTOR_NUMBER] =
+ (count % ATA_DEV_INFO(ctrl_minor, dev).sectors) + 1;
+
+ /* now count = number of tracks: */
+ count /= ATA_DEV_INFO(ctrl_minor, dev).sectors;
+ areq->regs.regs[IDE_REGISTER_DEVICE_HEAD] |=
+ (count / ATA_DEV_INFO(ctrl_minor, dev).cylinders);
+
+ /* now count = number of cylinders */
+ count %= ATA_DEV_INFO(ctrl_minor, dev).cylinders;
+ areq->regs.regs[IDE_REGISTER_CYLINDER_LOW] = (uint8_t)count;
+ areq->regs.regs[IDE_REGISTER_CYLINDER_HIGH] = (uint8_t)(count >> 8);
+ areq->regs.regs[IDE_REGISTER_DEVICE_HEAD] &=
+ ~IDE_REGISTER_DEVICE_HEAD_L;
+ }
+
+ /*
+ * Fill sector count register. We have a number of buffers (bufnum) which
+ * can be of a specific length (bufs[0].length / ATA_SECTOR_SIZE).
+ */
+ areq->regs.regs[IDE_REGISTER_SECTOR_COUNT] =
+ areq->breq->bufnum * (areq->breq->bufs[0].length / ATA_SECTOR_SIZE);
+
+ /* add request to the queue of awaiting requests to the controller */
+ ata_add_to_controller_queue(ctrl_minor, areq);
+
+ return RTEMS_SUCCESSFUL;
+}
+
+/* ata_non_data_request --
+ * Form and serve request of NON DATA type for an ATA device.
+ * Processing of NON DATA request is SYNChronous operation.
+ *
+ * PARAMETERS:
+ * device - device identifier
+ * cmd - command
+ * argp - arguments for command
+ *
+ * RETURNS:
+ * RTEMS_SUCCESSFUL on success, or error code if
+ * error occured
+ */
+static rtems_status_code
+ata_non_data_request(dev_t device, uint32_t cmd, void *argp)
+{
+ rtems_status_code rc;
+ ata_req_t *areq; /* ATA request */
+ rtems_device_minor_number rel_minor; /* relative minor which indexes
+ * ata_devs array
+ */
+ rtems_device_minor_number ctrl_minor;
+ uint8_t dev;
+ ata_queue_msg_t msg;
+
+ rel_minor = (rtems_filesystem_dev_minor_t(device)) /
+ ATA_MINOR_NUM_RESERVED_PER_ATA_DEVICE;
+
+ /* get controller which serves the ATA device */
+ ctrl_minor = ata_devs[rel_minor].ctrl_minor;
+
+ /* get ATA device identifier (0 or 1) */
+ dev = ata_devs[rel_minor].device;
+
+ /* form the request */
+ areq = malloc(sizeof(ata_req_t));
+ if (areq == NULL)
+ {
+ return RTEMS_NO_MEMORY;
+ }
+ memset(areq, 0, sizeof(ata_req_t));
+
+ areq->type = ATA_COMMAND_TYPE_NON_DATA;
+ areq->regs.to_write = ATA_REGISTERS_VALUE(IDE_REGISTER_COMMAND);
+ areq->regs.regs[IDE_REGISTER_DEVICE_HEAD] |=
+ (dev << IDE_REGISTER_DEVICE_HEAD_DEV_POS);
+ areq->breq = NULL;
+ areq->regs.to_read = ATA_REGISTERS_VALUE(IDE_REGISTER_ERROR);
+
+ /*
+ * depending on command fill command register and additional registers
+ * which are needed for command execution
+ */
+ switch(cmd)
+ {
+ case ATAIO_SET_MULTIPLE_MODE:
+ areq->regs.regs[IDE_REGISTER_COMMAND] =
+ ATA_COMMAND_SET_MULTIPLE_MODE;
+ areq->regs.to_write |=
+ ATA_REGISTERS_VALUE(IDE_REGISTER_SECTOR_COUNT);
+ areq->regs.regs[IDE_REGISTER_SECTOR_COUNT] = *(uint8_t*)argp;
+ break;
+
+ default:
+ free(areq);
+ return RTEMS_INVALID_NUMBER;
+ break;
+ }
+
+ rc = rtems_semaphore_create(rtems_build_name('I', 'D', 'E', 'S'),
+ 0,
+ RTEMS_FIFO | RTEMS_COUNTING_SEMAPHORE |
+ RTEMS_NO_INHERIT_PRIORITY |
+ RTEMS_NO_PRIORITY_CEILING | RTEMS_LOCAL,
+ 0,
+ &(areq->sema));
+ if (rc != RTEMS_SUCCESSFUL)
+ {
+ free(areq);
+ return rc;
+ }
+
+ ata_add_to_controller_queue(ctrl_minor, areq);
+
+ /* wait for request processing... */
+ rc = rtems_semaphore_obtain(areq->sema, RTEMS_WAIT, RTEMS_NO_TIMEOUT);
+ if (rc != RTEMS_SUCCESSFUL)
+ {
+ free(areq);
+ return rc;
+ }
+
+ rtems_semaphore_delete(areq->sema);
+
+ /*
+ * if no error occurred and if necessary, update internal ata driver data
+ * structures to reflect changes (in device configuration, for example)
+ */
+ if (areq->status == RTEMS_SUCCESSFUL)
+ {
+ switch(cmd)
+ {
+ case ATAIO_SET_MULTIPLE_MODE:
+ /* invalid operation now */
+ default:
+ rc = RTEMS_INVALID_NUMBER;
+ break;
+ }
+ }
+ else
+ {
+ /* XXX: should be correct error processing: for ex, may be
+ * ABRT and then we should return RTEMS_NOT_IMPLEMENTED
+ */
+ rc = RTEMS_IO_ERROR;
+ }
+
+ /* tell ata driver that controller ready to serve next request */
+ ATA_SEND_EVT(msg, ATA_MSG_SUCCESS_EVT, ctrl_minor, 0);
+
+ return rc;
+}
+
+/* ata_process_request --
+ * Get first request from controller's queue and process it.
+ *
+ * PARAMETERS:
+ * ctrl_minor - controller identifier
+ *
+ * RETURNS:
+ * NONE
+ */
+static void
+ata_process_request(rtems_device_minor_number ctrl_minor)
+{
+ ata_req_t *areq;
+ uint16_t byte; /* emphasize that only 8 low bits is meaningful */
+ ata_queue_msg_t msg;
+ uint8_t i;
+#if 0
+ uint8_t dev;
+#endif
+ uint16_t val;
+ ISR_Level level;
+
+ /* if no requests to controller then do nothing */
+ if (rtems_chain_is_empty(&ata_ide_ctrls[ctrl_minor].reqs))
+ return;
+
+ /* get first request in the controller's queue */
+ _ISR_Local_disable(level);
+ areq = (ata_req_t *)rtems_chain_first(&ata_ide_ctrls[ctrl_minor].reqs);
+ _ISR_Local_enable(level);
+
+#if 0
+ /* get ATA device identifier (0 or 1) */
+ dev = areq->regs.regs[IDE_REGISTER_DEVICE_HEAD] &
+ IDE_REGISTER_DEVICE_HEAD_DEV;
+#endif
+
+ /* execute device select protocol */
+ ide_controller_write_register(ctrl_minor, IDE_REGISTER_DEVICE_HEAD,
+ areq->regs.regs[IDE_REGISTER_DEVICE_HEAD]);
+
+ do {
+ ide_controller_read_register(ctrl_minor, IDE_REGISTER_STATUS, &byte);
+ } while ((byte & IDE_REGISTER_STATUS_BSY) ||
+ (!(byte & IDE_REGISTER_STATUS_DRDY)));
+
+ /* fill in all necessary registers on the controller */
+ for (i=0; i< ATA_MAX_CMD_REG_OFFSET; i++)
+ {
+ uint32_t reg = (1 << i);
+ if (areq->regs.to_write & reg)
+ ide_controller_write_register(ctrl_minor, i, areq->regs.regs[i]);
+ }
+
+#if ATA_DEBUG
+ ata_printf("ata_process_request: type: %d\n", areq->type);
+#endif
+
+ /* continue to execute ATA protocols depending on type of request */
+ if (areq->type == ATA_COMMAND_TYPE_PIO_OUT)
+ {
+ do {
+ ide_controller_read_register(ctrl_minor, IDE_REGISTER_STATUS,
+ &byte);
+ } while (byte & IDE_REGISTER_STATUS_BSY);
+
+ if (byte & IDE_REGISTER_STATUS_DRQ)
+ {
+ if (areq->cnt)
+ {
+ int ccbuf = areq->cbuf;
+ ide_controller_write_data_block(ctrl_minor,
+ areq->breq->bufs[0].length * areq->cnt,
+ areq->breq->bufs, &areq->cbuf,
+ &areq->pos);
+ ccbuf = areq->cbuf - ccbuf;
+ areq->cnt -= ccbuf;
+ }
+ }
+ else
+ {
+ if (IDE_Controller_Table[ctrl_minor].int_driven == false)
+ {
+ ide_controller_read_register(
+ ctrl_minor,
+ IDE_REGISTER_ALTERNATE_STATUS_OFFSET,
+ &val);
+ ide_controller_read_register(ctrl_minor, IDE_REGISTER_STATUS,
+ &val);
+
+ ATA_SEND_EVT(msg, ATA_MSG_ERROR_EVT, ctrl_minor,
+ RTEMS_IO_ERROR);
+ }
+ }
+ }
+
+ if (IDE_Controller_Table[ctrl_minor].int_driven == false)
+ {
+ do {
+ ide_controller_read_register(ctrl_minor, IDE_REGISTER_STATUS,
+ &byte);
+ } while (byte & IDE_REGISTER_STATUS_BSY);
+
+ ATA_SEND_EVT(msg, ATA_MSG_GEN_EVT, ctrl_minor, 0);
+ }
+}
+
+/* ata_request_done --
+ * Extract request from controller queue, execute callback if necessary
+ * and process next request for the controller.
+ *
+ * PARAMETERS:
+ * areq - ATA request
+ * ctrl_minor - controller identifier
+ * status - status with which request has been done
+ * error - error, if status != RTEMS_SUCCESSFUL
+ *
+ * RETURNS:
+ * NONE
+ */
+static inline void
+ata_request_done(ata_req_t *areq, rtems_device_minor_number ctrl_minor,
+ rtems_status_code status)
+{
+ assert(areq);
+
+#if ATA_DEBUG
+ ata_printf("ata_request_done: entry\n");
+#endif
+
+ ATA_EXEC_CALLBACK(areq, status);
+ rtems_chain_extract(&areq->link);
+
+ if (!rtems_chain_is_empty(&ata_ide_ctrls[ctrl_minor].reqs))
+ {
+ free(areq);
+ ata_process_request(ctrl_minor);
+ return;
+ }
+
+ free(areq);
+
+#if ATA_DEBUG
+ ata_printf("ata_request_done: exit\n");
+#endif
+}
+
+/* ata_non_data_request_done --
+ * Set up request status and release request's semaphore.
+ *
+ * PARAMETERS:
+ * areq - ATA request
+ * ctrl_minor - controller identifier
+ * status - status with which request has been done
+ * error - error, if status != RTEMS_SUCCESSFUL
+ *
+ * RETURNS:
+ * NONE
+ */
+static inline void
+ata_non_data_request_done(ata_req_t *areq,
+ rtems_device_minor_number ctrl_minor,
+ rtems_status_code status, int info)
+{
+#if ATA_DEBUG
+ ata_printf("ata_non_data_request_done: entry\n");
+#endif
+
+ areq->status = status;
+ areq->info = info;
+ rtems_semaphore_release(areq->sema);
+}
+
+
+/* ata_add_to_controller_queue --
+ * Add request to the controller's queue.
+ *
+ * PARAMETERS:
+ * ctrl_minor - controller identifier
+ * areq - ATA request
+ *
+ * RETURNS:
+ * NONE
+ */
+static void
+ata_add_to_controller_queue(rtems_device_minor_number ctrl_minor,
+ ata_req_t *areq)
+{
+ rtems_ata_lock();
+
+ rtems_chain_append(&ata_ide_ctrls[ctrl_minor].reqs, &areq->link);
+ if (rtems_chain_has_only_one_node(&ata_ide_ctrls[ctrl_minor].reqs))
+ {
+
+ ata_queue_msg_t msg;
+
+#if ATA_DEBUG_DOES_NOT_WORK_WITH_QEMU
+ uint16_t val;
+ /*
+ * read IDE_REGISTER_ALTERNATE_STATUS instead IDE_REGISTER_STATUS
+ * to prevent clearing of pending interrupt
+ */
+ ide_controller_read_register(ctrl_minor,
+ IDE_REGISTER_ALTERNATE_STATUS,
+ &val);
+ if (val & IDE_REGISTER_STATUS_BSY)
+ return;
+#endif
+ ATA_SEND_EVT(msg, ATA_MSG_PROCESS_NEXT_EVT, ctrl_minor, 0);
+ }
+
+ rtems_ata_unlock();
+}
+
+
+/* ata_interrupt_handler --
+ * ATA driver interrrupt handler. If interrrupt happend it mapped it to
+ * controller (controllerS, if a number of controllers share one int line)
+ * and generates ATA event(s).
+ *
+ * PARAMETERS:
+ * vec - interrupt vector
+ *
+ * RETURNS:
+ * NONE
+ */
+#if CPU_SIMPLE_VECTORED_INTERRUPTS == TRUE
+static rtems_isr ata_interrupt_handler(rtems_vector_number vec)
+{
+ rtems_chain_node *the_node = rtems_chain_first(&ata_int_vec[vec]);
+ ata_queue_msg_t msg;
+ uint16_t byte; /* emphasize that only 8 low bits is meaningful */
+
+ for ( ; !rtems_chain_is_tail(&ata_int_vec[vec], the_node) ; )
+ {
+ /* if (1) - is temporary hack - currently I don't know how to identify
+ * controller which asserted interrupt if few controllers share one
+ * interrupt line
+ */
+ if (1)
+ {
+ msg.ctrl_minor = ((ata_int_st_t *)the_node)->ctrl_minor;
+ ide_controller_read_register(msg.ctrl_minor, IDE_REGISTER_STATUS,
+ &byte);
+ ATA_SEND_EVT(msg, ATA_MSG_GEN_EVT, msg.ctrl_minor, 0);
+ }
+ the_node = the_node->next;
+ }
+}
+#else
+static void ata_interrupt_handler(rtems_irq_hdl_param handle)
+{
+ uintptr_t ata_irq_chain_index = (uintptr_t) handle;
+ rtems_chain_node *the_node =
+ rtems_chain_last(&ata_irq_chain[ata_irq_chain_index].irq_chain);
+ ata_queue_msg_t msg;
+ uint16_t byte; /* emphasize that only 8 low bits is meaningful */
+
+
+ for ( ; !rtems_chain_is_tail(&ata_irq_chain[ata_irq_chain_index].irq_chain,
+ the_node) ; )
+ {
+ /* if (1) - is temporary hack - currently I don't know how to identify
+ * controller which asserted interrupt if few controllers share one
+ * interrupt line
+ */
+ if (1)
+ {
+ msg.ctrl_minor = ((ata_int_st_t *)the_node)->ctrl_minor;
+ ide_controller_read_register(msg.ctrl_minor, IDE_REGISTER_STATUS,
+ &byte);
+ ATA_SEND_EVT(msg, ATA_MSG_GEN_EVT, msg.ctrl_minor, 0);
+ }
+ the_node = the_node->next;
+ }
+}
+
+static void ata_interrupt_on(const rtems_irq_connect_data *ptr)
+ {
+
+ /* enable ATA device interrupt */
+ ide_controller_write_register(0,
+ IDE_REGISTER_DEVICE_CONTROL_OFFSET,
+ 0x00
+ );
+ }
+
+
+static void ata_interrupt_off(const rtems_irq_connect_data *ptr)
+ {
+
+ /* disable ATA device interrupt */
+ ide_controller_write_register(0,
+ IDE_REGISTER_DEVICE_CONTROL_OFFSET,
+ IDE_REGISTER_DEVICE_CONTROL_nIEN
+ );
+ }
+
+
+static int ata_interrupt_isOn(const rtems_irq_connect_data *ptr)
+ {
+ uint16_t byte; /* emphasize that only 8 low bits is meaningful */
+
+ /* return int. status od ATA device */
+ ide_controller_read_register(0,
+ IDE_REGISTER_DEVICE_CONTROL_OFFSET,
+ &byte
+ );
+
+ return !(byte & IDE_REGISTER_DEVICE_CONTROL_nIEN);
+ }
+
+
+static rtems_irq_connect_data ata_irq_data =
+ {
+
+ 0, /* filled out before use... */
+ ata_interrupt_handler,/* filled out before use... */
+ NULL,
+ ata_interrupt_on,
+ ata_interrupt_off,
+ ata_interrupt_isOn
+ };
+#endif
+
+/* ata_pio_in_protocol --
+ * ATA PIO_IN protocol implementation, see specification
+ *
+ * PARAMETERS:
+ * ctrl_minor - controller identifier
+ * areq - ATA request
+ *
+ * RETURNS:
+ * NONE
+ */
+static inline void
+ata_pio_in_protocol(rtems_device_minor_number ctrl_minor, ata_req_t *areq)
+{
+ uint16_t val;
+#if 0
+ uint8_t dev;
+#endif
+ ata_queue_msg_t msg;
+
+#if 0
+ dev = areq->regs.regs[IDE_REGISTER_DEVICE_HEAD] &
+ IDE_REGISTER_DEVICE_HEAD_DEV;
+#endif
+
+ if (areq->cnt)
+ {
+ int ccbuf = areq->cbuf;
+ ide_controller_read_data_block(ctrl_minor,
+ areq->breq->bufs[0].length * areq->cnt,
+ areq->breq->bufs, &areq->cbuf, &areq->pos);
+ ccbuf = areq->cbuf - ccbuf;
+ areq->cnt -= ccbuf;
+ }
+
+ if (areq->cnt == 0)
+ {
+ ata_request_done(areq, ctrl_minor, RTEMS_SUCCESSFUL);
+ }
+ else if (IDE_Controller_Table[ctrl_minor].int_driven == false)
+ {
+ do {
+ ide_controller_read_register(ctrl_minor, IDE_REGISTER_STATUS, &val);
+ } while (val & IDE_REGISTER_STATUS_BSY);
+
+ ATA_SEND_EVT(msg, ATA_MSG_GEN_EVT, ctrl_minor, 0);
+ }
+}
+
+/* ata_pio_out_protocol --
+ * ATA PIO_OUT protocol implementation, see specification
+ *
+ * PARAMETERS:
+ * ctrl_minor - controller identifier
+ * areq - ATA request
+ *
+ * RETURNS:
+ * NONE
+ */
+static inline void
+ata_pio_out_protocol(rtems_device_minor_number ctrl_minor, ata_req_t *areq)
+{
+ uint16_t val;
+#if 0
+ uint8_t dev;
+#endif
+ ata_queue_msg_t msg;
+
+#if ATA_DEBUG
+ ata_printf("ata_pio_out_protocol:\n");
+#endif
+
+#if 0
+ dev = areq->regs.regs[IDE_REGISTER_DEVICE_HEAD] &
+ IDE_REGISTER_DEVICE_HEAD_DEV;
+#endif
+
+ if (areq->cnt == 0)
+ {
+ ata_request_done(areq, ctrl_minor, RTEMS_SUCCESSFUL);
+ }
+ else
+ {
+ if (areq->cnt)
+ {
+ int ccbuf = areq->cbuf;
+ ide_controller_write_data_block(ctrl_minor,
+ areq->breq->bufs[0].length * areq->cnt,
+ areq->breq->bufs, &areq->cbuf,
+ &areq->pos);
+ ccbuf = areq->cbuf - ccbuf;
+ areq->cnt -= ccbuf;
+ }
+ if (IDE_Controller_Table[ctrl_minor].int_driven == false)
+ {
+ do {
+ ide_controller_read_register(ctrl_minor, IDE_REGISTER_STATUS,
+ &val);
+ } while (val & IDE_REGISTER_STATUS_BSY);
+
+ ATA_SEND_EVT(msg, ATA_MSG_GEN_EVT, ctrl_minor, 0);
+ }
+ }
+}
+
+/* ata_queue_task --
+ * Task which manages ATA driver events queue.
+ *
+ * PARAMETERS:
+ * arg - ignored
+ *
+ * RETURNS:
+ * NONE
+ *
+ * NOTES:
+ * should be non-preemptive
+ */
+static rtems_task
+ata_queue_task(rtems_task_argument arg)
+{
+ ata_queue_msg_t msg;
+ size_t size;
+ ata_req_t *areq;
+ rtems_device_minor_number ctrl_minor;
+ uint16_t val;
+ uint16_t val1;
+ rtems_status_code rc;
+ ISR_Level level;
+
+ rtems_ata_lock();
+
+ while (1)
+ {
+ rtems_ata_unlock();
+
+ /* get event which has happend */
+ rc = rtems_message_queue_receive(ata_queue_id, &msg, &size, RTEMS_WAIT,
+ RTEMS_NO_TIMEOUT);
+ if (rc != RTEMS_SUCCESSFUL)
+ rtems_fatal_error_occurred(RTEMS_INTERNAL_ERROR);
+
+ /* get controller on which event has happend */
+ ctrl_minor = msg.ctrl_minor;
+
+ rtems_ata_lock();
+
+ /* get current request to the controller */
+ _ISR_Local_disable(level);
+ areq = (ata_req_t *)rtems_chain_first(&ata_ide_ctrls[ctrl_minor].reqs);
+ _ISR_Local_enable(level);
+
+ switch(msg.type)
+ {
+ case ATA_MSG_PROCESS_NEXT_EVT:
+ /* process next request in the controller queue */
+ ata_process_request(ctrl_minor);
+ break;
+
+ case ATA_MSG_SUCCESS_EVT:
+ /*
+ * finish processing of current request with successful
+ * status and start processing of the next request in the
+ * controller queue
+ */
+ ata_request_done(areq, ctrl_minor, RTEMS_SUCCESSFUL);
+ break;
+
+ case ATA_MSG_ERROR_EVT:
+ /*
+ * finish processing of current request with error
+ * status and start processing of the next request in the
+ * controller queue
+ */
+ ata_request_done(areq, ctrl_minor, RTEMS_IO_ERROR);
+ break;
+
+ case ATA_MSG_GEN_EVT:
+ /*
+ * continue processing of the current request to the
+ * controller according to current request state and
+ * ATA protocol
+ */
+ ide_controller_read_register(ctrl_minor, IDE_REGISTER_STATUS,
+ &val);
+ /* process error case */
+ if (val & IDE_REGISTER_STATUS_ERR)
+ {
+ ide_controller_read_register(ctrl_minor,
+ IDE_REGISTER_ERROR,
+ &val);
+ if (val & (IDE_REGISTER_ERROR_UNC |
+ IDE_REGISTER_ERROR_ICRC |
+ IDE_REGISTER_ERROR_IDNF |
+ IDE_REGISTER_ERROR_NM |
+ IDE_REGISTER_ERROR_MED))
+ {
+ if (areq->type == ATA_COMMAND_TYPE_NON_DATA)
+ ata_non_data_request_done(areq, ctrl_minor,
+ RTEMS_UNSATISFIED,
+ RTEMS_IO_ERROR);
+ else
+ ata_request_done(areq, ctrl_minor, RTEMS_IO_ERROR);
+ break;
+ }
+ }
+
+ switch(areq->type)
+ {
+ case ATA_COMMAND_TYPE_PIO_IN:
+ ata_pio_in_protocol(ctrl_minor, areq);
+ break;
+
+ case ATA_COMMAND_TYPE_PIO_OUT:
+ ata_pio_out_protocol(ctrl_minor, areq);
+ break;
+
+ case ATA_COMMAND_TYPE_NON_DATA:
+ ide_controller_read_register(ctrl_minor,
+ IDE_REGISTER_ERROR,
+ &val1);
+ ata_non_data_request_done(areq, ctrl_minor,
+ RTEMS_SUCCESSFUL,
+ val1);
+ break;
+
+ default:
+#if ATA_DEBUG
+ ata_printf("ata_queue_task: non-supported command type\n");
+#endif
+ ata_request_done(areq, ctrl_minor, RTEMS_IO_ERROR);
+ break;
+ }
+ break;
+
+ default:
+#if ATA_DEBUG
+ ata_printf("ata_queue_task: internal error\n");
+ rtems_task_delete (RTEMS_SELF);
+#endif
+ rtems_fatal_error_occurred(RTEMS_INTERNAL_ERROR);
+ break;
+ }
+ }
+}
+
+/* ata_ioctl --
+ * ATA driver ioctl interface.
+ *
+ * PARAMETERS:
+ * device - device identifier
+ * cmd - command
+ * argp - arguments
+ *
+ * RETURNS:
+ * depend on 'cmd'
+ */
+static int
+ata_ioctl(rtems_disk_device *dd, uint32_t cmd, void *argp)
+{
+ dev_t device = rtems_disk_get_device_identifier(dd);
+ rtems_status_code status;
+ rtems_device_minor_number rel_minor;
+
+ rel_minor = (rtems_filesystem_dev_minor_t(device)) /
+ ATA_MINOR_NUM_RESERVED_PER_ATA_DEVICE;
+
+ /*
+ * in most cases this means that device 'device' is not an registred ATA
+ * device
+ */
+ if (ata_devs[rel_minor].device == ATA_UNDEFINED_VALUE)
+ {
+ errno = ENODEV;
+ return -1;
+ }
+
+ switch (cmd)
+ {
+ case RTEMS_BLKIO_REQUEST:
+ status = ata_io_data_request(device, (rtems_blkdev_request *)argp);
+ break;
+
+ case ATAIO_SET_MULTIPLE_MODE:
+ status = ata_non_data_request(device, cmd, argp);
+ break;
+
+ case RTEMS_BLKIO_CAPABILITIES:
+ *((uint32_t*) argp) = RTEMS_BLKDEV_CAP_MULTISECTOR_CONT;
+ status = RTEMS_SUCCESSFUL;
+ break;
+
+ default:
+ return rtems_blkdev_ioctl (dd, cmd, argp);
+ break;
+ }
+
+ if (status != RTEMS_SUCCESSFUL)
+ {
+ errno = EIO;
+ return -1;
+ }
+ return 0;
+}
+
+static void ata_execute_device_diagnostic(
+ rtems_device_minor_number ctrl_minor,
+ uint16_t *sector_buffer
+)
+{
+#if ATA_EXEC_DEVICE_DIAGNOSTIC
+ ata_req_t areq;
+ blkdev_request1 breq;
+
+ ata_breq_init(&breq, sector_buffer);
+
+ /*
+ * Issue EXECUTE DEVICE DIAGNOSTIC ATA command for explore is
+ * there any ATA device on the controller.
+ *
+ * This command may fail and it assumes we have a master device and may
+ * be a slave device. I think the identify command will handle
+ * detection better than this method.
+ */
+ memset(&areq, 0, sizeof(ata_req_t));
+ areq.type = ATA_COMMAND_TYPE_NON_DATA;
+ areq.regs.to_write = ATA_REGISTERS_VALUE(IDE_REGISTER_COMMAND);
+ areq.regs.regs[IDE_REGISTER_COMMAND] =
+ ATA_COMMAND_EXECUTE_DEVICE_DIAGNOSTIC;
+ areq.regs.to_read = ATA_REGISTERS_VALUE(IDE_REGISTER_ERROR);
+
+ areq.breq = (rtems_blkdev_request *)&breq;
+
+ /*
+ * Process the request. Special processing of requests on
+ * initialization phase is needed because at this moment there
+ * is no multitasking enviroment
+ */
+ ata_process_request_on_init_phase(ctrl_minor, &areq);
+
+ /*
+ * check status of I/O operation
+ */
+ if (breq.req.status == RTEMS_SUCCESSFUL)
+ {
+ /* disassemble returned diagnostic codes */
+ if (areq.info == ATA_DEV0_PASSED_DEV1_PASSED_OR_NOT_PRSNT)
+ {
+ printk("ATA: ctrl:%d: primary, secondary\n", ctrl_minor);
+ ATA_DEV_INFO(ctrl_minor,0).present = true;
+ ATA_DEV_INFO(ctrl_minor,1).present = true;
+ }
+ else if (areq.info == ATA_DEV0_PASSED_DEV1_FAILED)
+ {
+ printk("ATA: ctrl:%d: primary\n", ctrl_minor);
+ ATA_DEV_INFO(ctrl_minor,0).present = true;
+ ATA_DEV_INFO(ctrl_minor,1).present = false;
+ }
+ else if (areq.info < ATA_DEV1_PASSED_DEV0_FAILED)
+ {
+ printk("ATA: ctrl:%d: secondary\n", ctrl_minor);
+ ATA_DEV_INFO(ctrl_minor,0).present = false;
+ ATA_DEV_INFO(ctrl_minor,1).present = true;
+ }
+ else
+ {
+ printk("ATA: ctrl:%d: none\n", ctrl_minor);
+ ATA_DEV_INFO(ctrl_minor, 0).present = false;
+ ATA_DEV_INFO(ctrl_minor, 1).present = false;
+ }
+
+ /* refine the returned codes */
+ if (ATA_DEV_INFO(ctrl_minor, 1).present)
+ {
+ uint16_t ec = 0;
+ ide_controller_read_register(ctrl_minor, IDE_REGISTER_ERROR, &ec);
+ if (ec & ATA_DEV1_PASSED_DEV0_FAILED)
+ {
+ printk("ATA: ctrl:%d: secondary inforced\n", ctrl_minor);
+ ATA_DEV_INFO(ctrl_minor, 1).present = true;
+ }
+ else
+ {
+ printk("ATA: ctrl:%d: secondary removed\n", ctrl_minor);
+ ATA_DEV_INFO(ctrl_minor, 1).present = false;
+ }
+ }
+ }
+ else
+#endif
+ {
+ ATA_DEV_INFO(ctrl_minor, 0).present = true;
+ ATA_DEV_INFO(ctrl_minor,1).present = true;
+ }
+}
+
+/*
+ * ata_initialize --
+ * Initializes all ATA devices found on initialized IDE controllers.
+ *
+ * PARAMETERS:
+ * major - device major number
+ * minor - device minor number
+ * args - arguments
+ *
+ * RETURNS:
+ * RTEMS_SUCCESSFUL on success, or error code if
+ * error occured
+ */
+rtems_device_driver
+rtems_ata_initialize(rtems_device_major_number major,
+ rtems_device_minor_number minor_arg,
+ void *args)
+{
+ uint32_t ctrl_minor;
+ rtems_status_code status;
+ uint16_t *buffer;
+ int i, dev = 0;
+ char name[ATA_MAX_NAME_LENGTH];
+ dev_t device;
+ ata_int_st_t *int_st;
+
+#if CPU_SIMPLE_VECTORED_INTERRUPTS == TRUE
+ rtems_isr_entry old_isr;
+#else
+ int ata_irq_chain_use;
+#endif
+
+ if (ata_initialized)
+ return RTEMS_SUCCESSFUL;
+
+ /* initialization of disk devices library */
+ status = rtems_disk_io_initialize();
+ if (status != RTEMS_SUCCESSFUL)
+ return status;
+
+ status = rtems_semaphore_create (rtems_build_name ('A', 'T', 'A', 'L'),
+ 1, RTEMS_ATA_LOCK_ATTRIBS, 0,
+ &ata_lock);
+ if (status != RTEMS_SUCCESSFUL)
+ return status;
+
+ /* create queue for asynchronous requests handling */
+ status = rtems_message_queue_create(
+ rtems_build_name('A', 'T', 'A', 'Q'),
+ ATA_DRIVER_MESSAGE_QUEUE_SIZE,
+ sizeof(ata_queue_msg_t),
+ RTEMS_FIFO | RTEMS_LOCAL,
+ &ata_queue_id);
+ if (status != RTEMS_SUCCESSFUL)
+ {
+ rtems_disk_io_done();
+ return status;
+ }
+
+ /*
+ * create ATA driver task, see comments for task implementation for
+ * details
+ */
+ status = rtems_task_create(
+ rtems_build_name ('A', 'T', 'A', 'T'),
+ ((rtems_ata_driver_task_priority > 0)
+ ? rtems_ata_driver_task_priority
+ : ATA_DRIVER_TASK_DEFAULT_PRIORITY),
+ ATA_DRIVER_TASK_STACK_SIZE,
+ RTEMS_PREEMPT | RTEMS_NO_TIMESLICE | RTEMS_NO_ASR |
+ RTEMS_INTERRUPT_LEVEL(0),
+ RTEMS_NO_FLOATING_POINT | RTEMS_LOCAL,
+ &ata_task_id);
+ if (status != RTEMS_SUCCESSFUL)
+ {
+ rtems_message_queue_delete(ata_queue_id);
+ rtems_disk_io_done();
+ return status;
+ }
+
+ /*
+ * start ATA driver task. Actually the task will not start immediately -
+ * it will start only after multitasking support will be started
+ */
+ status = rtems_task_start(ata_task_id, ata_queue_task, 0);
+ if (status != RTEMS_SUCCESSFUL)
+ {
+ rtems_task_delete(ata_task_id);
+ rtems_message_queue_delete(ata_queue_id);
+ rtems_disk_io_done();
+ return status;
+ }
+
+ buffer = (uint16_t*)malloc(ATA_SECTOR_SIZE);
+ if (buffer == NULL)
+ {
+ rtems_task_delete(ata_task_id);
+ rtems_message_queue_delete(ata_queue_id);
+ rtems_disk_io_done();
+ return RTEMS_NO_MEMORY;
+ }
+
+ ata_devs_number = 0;
+
+ for (i = 0; i < (2 * IDE_CTRL_MAX_MINOR_NUMBER); i++)
+ ata_devs[i].device = ATA_UNDEFINED_VALUE;
+
+#if CPU_SIMPLE_VECTORED_INTERRUPTS == TRUE
+ /* prepare ATA driver for handling interrupt driven devices */
+ for (i = 0; i < ATA_MAX_RTEMS_INT_VEC_NUMBER; i++)
+ rtems_chain_initialize_empty(&ata_int_vec[i]);
+#else
+ for (i = 0; i < ATA_IRQ_CHAIN_MAX_CNT; i++) {
+ rtems_chain_initialize_empty(&(ata_irq_chain[i].irq_chain));
+ }
+#endif
+
+ /*
+ * during ATA driver initialization EXECUTE DEVICE DIAGNOSTIC and
+ * IDENTIFY DEVICE ATA command should be issued; for these purposes ATA
+ * requests should be formed; ATA requests contain block device request,
+ * so form block device request first
+ */
+
+ /*
+ * for each presented IDE controller execute EXECUTE DEVICE DIAGNOSTIC
+ * ATA command; for each found device execute IDENTIFY DEVICE ATA
+ * command
+ */
+ for (ctrl_minor = 0; ctrl_minor < IDE_Controller_Count; ctrl_minor++)
+ if (IDE_Controller_Table[ctrl_minor].status == IDE_CTRL_INITIALIZED)
+ {
+ rtems_chain_initialize_empty(&ata_ide_ctrls[ctrl_minor].reqs);
+
+ if (IDE_Controller_Table[ctrl_minor].int_driven == true)
+ {
+ int_st = malloc(sizeof(ata_int_st_t));
+ if (int_st == NULL)
+ {
+ free(buffer);
+ rtems_task_delete(ata_task_id);
+ rtems_message_queue_delete(ata_queue_id);
+ rtems_disk_io_done();
+ return RTEMS_NO_MEMORY;
+ }
+
+ int_st->ctrl_minor = ctrl_minor;
+#if CPU_SIMPLE_VECTORED_INTERRUPTS == TRUE
+ status = rtems_interrupt_catch(
+ ata_interrupt_handler,
+ IDE_Controller_Table[ctrl_minor].int_vec,
+ &old_isr);
+#else
+ /*
+ * FIXME: check existing entries. if they use the same
+ * IRQ name, then append int_st to respective chain
+ * otherwise, use new ata_irq_chain entry
+ */
+ ata_irq_chain_use = -1;
+ for (i = 0;
+ ((i < ata_irq_chain_cnt) &&
+ (ata_irq_chain_use < 0));i++) {
+ if (ata_irq_chain[i].name ==
+ IDE_Controller_Table[ctrl_minor].int_vec) {
+ ata_irq_chain_use = i;
+ }
+ }
+ if (ata_irq_chain_use < 0) {
+ /*
+ * no match found, try to use new channel entry
+ */
+ if (ata_irq_chain_cnt < ATA_IRQ_CHAIN_MAX_CNT) {
+ ata_irq_chain_use = ata_irq_chain_cnt++;
+
+ ata_irq_chain[ata_irq_chain_use].name =
+ IDE_Controller_Table[ctrl_minor].int_vec;
+ ata_irq_data.name =
+ IDE_Controller_Table[ctrl_minor].int_vec;
+ ata_irq_data.hdl = ata_interrupt_handler;
+ ata_irq_data.handle = (rtems_irq_hdl_param) (uintptr_t) ctrl_minor;
+
+ status = ((0 == BSP_install_rtems_irq_handler(&ata_irq_data))
+ ? RTEMS_INVALID_NUMBER
+ : RTEMS_SUCCESSFUL);
+ }
+ else {
+ status = RTEMS_TOO_MANY;
+ }
+ }
+#endif
+ if (status != RTEMS_SUCCESSFUL)
+ {
+ free(int_st);
+ free(buffer);
+ rtems_task_delete(ata_task_id);
+ rtems_message_queue_delete(ata_queue_id);
+ rtems_disk_io_done();
+ return status;
+ }
+#if CPU_SIMPLE_VECTORED_INTERRUPTS == TRUE
+ rtems_chain_append(
+ &ata_int_vec[IDE_Controller_Table[ctrl_minor].int_vec],
+ &int_st->link);
+#else
+ rtems_chain_append(
+ &(ata_irq_chain[ata_irq_chain_use].irq_chain),
+ &int_st->link);
+#endif
+
+ /* disable interrupts */
+ ide_controller_write_register(ctrl_minor,
+ IDE_REGISTER_DEVICE_CONTROL_OFFSET,
+ IDE_REGISTER_DEVICE_CONTROL_nIEN);
+ }
+
+ ata_execute_device_diagnostic(ctrl_minor, buffer);
+
+ /* for each found ATA device obtain it configuration */
+ for (dev = 0; dev < 2; dev++)
+ if (ATA_DEV_INFO(ctrl_minor, dev).present)
+ {
+ status = ata_identify_device(
+ ctrl_minor,
+ dev,
+ buffer,
+ &ATA_DEV_INFO(ctrl_minor, dev));
+ if (status != RTEMS_SUCCESSFUL)
+ continue;
+
+ /*
+ * choose most appropriate ATA device data I/O speed supported
+ * by the controller
+ */
+ status = ide_controller_config_io_speed(
+ ctrl_minor,
+ ATA_DEV_INFO(ctrl_minor, dev).modes_available);
+ if (status != RTEMS_SUCCESSFUL)
+ continue;
+
+ /*
+ * Ok, let register new ATA device in the system
+ */
+ ata_devs[ata_devs_number].ctrl_minor = ctrl_minor;
+ ata_devs[ata_devs_number].device = dev;
+
+ /* The space leaves a hole for the character. */
+ strcpy(name, "/dev/hd ");
+ name[7] = 'a' + 2 * ctrl_minor + dev;
+
+ device = rtems_filesystem_make_dev_t(
+ major,
+ (ata_devs_number *
+ ATA_MINOR_NUM_RESERVED_PER_ATA_DEVICE));
+ status = rtems_disk_create_phys(device, ATA_SECTOR_SIZE,
+ ATA_DEV_INFO(ctrl_minor, dev).lba_avaible ?
+ ATA_DEV_INFO(ctrl_minor, dev).lba_sectors :
+ (ATA_DEV_INFO(ctrl_minor, dev).heads *
+ ATA_DEV_INFO(ctrl_minor, dev).cylinders *
+ ATA_DEV_INFO(ctrl_minor, dev).sectors),
+ ata_ioctl, NULL, name);
+ if (status != RTEMS_SUCCESSFUL)
+ {
+ ata_devs[ata_devs_number].device = ATA_UNDEFINED_VALUE;
+ continue;
+ }
+ ata_devs_number++;
+ }
+ if (IDE_Controller_Table[ctrl_minor].int_driven == true)
+ {
+ ide_controller_write_register(ctrl_minor,
+ IDE_REGISTER_DEVICE_CONTROL_OFFSET,
+ 0x00);
+ }
+ }
+
+ free(buffer);
+ ata_initialized = true;
+ return RTEMS_SUCCESSFUL;
+}
diff --git a/bsps/shared/dev/ide/ata_util.c b/bsps/shared/dev/ide/ata_util.c
new file mode 100644
index 0000000000..68e0f0bbe5
--- /dev/null
+++ b/bsps/shared/dev/ide/ata_util.c
@@ -0,0 +1,215 @@
+/*
+ * Copyright (c) 2010 embedded brains GmbH.
+ *
+ * Copyright (C) 2001 OKTET Ltd., St.-Petersburg, Russia
+ * Authors: Eugeny S. Mints <Eugeny.Mints@oktet.ru>
+ *
+ * The license and distribution terms for this file may be
+ * found in the file LICENSE in this distribution or at
+ * http://www.rtems.org/license/LICENSE.
+ */
+
+#include <assert.h>
+#include <string.h>
+
+#include <libchip/ide_ctrl_io.h>
+#include <libchip/ide_ctrl_cfg.h>
+#include <libchip/ata_internal.h>
+
+/* ata_process_request_on_init_phase --
+ * Process the ATA request during system initialization. Request
+ * processing is syncronous and doesn't use multiprocessing enviroment.
+ *
+ * PARAMETERS:
+ * ctrl_minor - controller identifier
+ * areq - ATA request
+ *
+ * RETURNS:
+ * NONE
+ */
+void
+ata_process_request_on_init_phase(rtems_device_minor_number ctrl_minor,
+ ata_req_t *areq)
+{
+ uint16_t byte;/* emphasize that only 8 low bits is meaningful */
+ uint8_t i;
+#if 0
+ uint8_t dev;
+#endif
+ uint16_t val, val1;
+ volatile unsigned retries;
+
+ assert(areq);
+
+#if 0
+ dev = areq->regs.regs[IDE_REGISTER_DEVICE_HEAD] &
+ IDE_REGISTER_DEVICE_HEAD_DEV;
+#endif
+
+ ide_controller_write_register(ctrl_minor, IDE_REGISTER_DEVICE_HEAD,
+ areq->regs.regs[IDE_REGISTER_DEVICE_HEAD]);
+
+ retries = 0;
+ do {
+ ide_controller_read_register(ctrl_minor, IDE_REGISTER_STATUS, &byte);
+ /* If device (on INIT, i.e. it should be idle) is neither
+ * busy nor ready something's fishy, i.e., there is probably
+ * no device present.
+ * I'd like to do a proper timeout but don't know of a portable
+ * timeout routine (w/o using multitasking / rtems_task_wake_after())
+ */
+ if ( ! (byte & (IDE_REGISTER_STATUS_BSY | IDE_REGISTER_STATUS_DRDY))) {
+ retries++;
+ if ( 10000 == retries ) {
+ /* probably no drive connected */
+ areq->breq->status = RTEMS_UNSATISFIED;
+ return;
+ }
+ }
+ } while ((byte & IDE_REGISTER_STATUS_BSY) ||
+ (!(byte & IDE_REGISTER_STATUS_DRDY)));
+
+ for (i=0; i< ATA_MAX_CMD_REG_OFFSET; i++)
+ {
+ uint32_t reg = (1 << i);
+ if (areq->regs.to_write & reg)
+ ide_controller_write_register(ctrl_minor, i,
+ areq->regs.regs[i]);
+ }
+
+ do {
+ ide_controller_read_register(ctrl_minor, IDE_REGISTER_STATUS, &byte);
+ } while (byte & IDE_REGISTER_STATUS_BSY);
+
+ ide_controller_read_register(ctrl_minor, IDE_REGISTER_STATUS, &val);
+ ide_controller_read_register(ctrl_minor, IDE_REGISTER_ERROR, &val1);
+
+ if (val & IDE_REGISTER_STATUS_ERR)
+ {
+ areq->breq->status = RTEMS_IO_ERROR;
+ return;
+ }
+
+ switch(areq->type)
+ {
+ case ATA_COMMAND_TYPE_PIO_IN:
+ if (areq->cnt)
+ {
+ int ccbuf = areq->cbuf;
+ ide_controller_read_data_block(ctrl_minor,
+ areq->breq->bufs[0].length * areq->cnt,
+ areq->breq->bufs, &areq->cbuf,
+ &areq->pos);
+ ccbuf = areq->cbuf - ccbuf;
+ areq->cnt -= ccbuf;
+ }
+ if (areq->cnt == 0)
+ {
+ areq->breq->status = RTEMS_SUCCESSFUL;
+ }
+ else
+ {
+ /*
+ * this shouldn't happend on the initialization
+ * phase!
+ */
+ rtems_fatal_error_occurred(RTEMS_INTERNAL_ERROR);
+ }
+ break;
+
+ case ATA_COMMAND_TYPE_NON_DATA:
+ areq->breq->status = RTEMS_SUCCESSFUL;
+ areq->info = val1;
+ break;
+
+ default:
+ areq->breq->status = RTEMS_IO_ERROR;
+ break;
+ }
+}
+
+void ata_breq_init(blkdev_request1 *breq, uint16_t *sector_buffer)
+{
+ memset(breq, 0, sizeof(*breq));
+
+ breq->req.done_arg = breq;
+ breq->req.bufnum = 1;
+ breq->req.bufs [0].length = ATA_SECTOR_SIZE;
+ breq->req.bufs [0].buffer = sector_buffer;
+}
+
+rtems_status_code ata_identify_device(
+ rtems_device_minor_number ctrl_minor,
+ int dev,
+ uint16_t *sector_buffer,
+ ata_dev_t *device_entry
+)
+{
+ ata_req_t areq;
+ blkdev_request1 breq;
+
+ ata_breq_init(&breq, sector_buffer);
+
+ /*
+ * Issue DEVICE IDENTIFY ATA command and get device
+ * configuration
+ */
+ memset(&areq, 0, sizeof(ata_req_t));
+ areq.type = ATA_COMMAND_TYPE_PIO_IN;
+ areq.regs.to_write = ATA_REGISTERS_VALUE(IDE_REGISTER_COMMAND);
+ areq.regs.regs [IDE_REGISTER_COMMAND] = ATA_COMMAND_IDENTIFY_DEVICE;
+ areq.regs.to_read = ATA_REGISTERS_VALUE(IDE_REGISTER_STATUS);
+ areq.breq = (rtems_blkdev_request *)&breq;
+ areq.cnt = breq.req.bufnum;
+ areq.regs.regs [IDE_REGISTER_DEVICE_HEAD] |=
+ dev << IDE_REGISTER_DEVICE_HEAD_DEV_POS;
+
+ /*
+ * Process the request. Special processing of requests on
+ * initialization phase is needed because at this moment there
+ * is no multitasking enviroment
+ */
+ ata_process_request_on_init_phase(ctrl_minor, &areq);
+
+ /* check status of I/O operation */
+ if (breq.req.status != RTEMS_SUCCESSFUL) {
+ return RTEMS_IO_ERROR;
+ }
+
+ /*
+ * Parse returned device configuration and fill in ATA internal
+ * device info structure
+ */
+ device_entry->cylinders =
+ CF_LE_W(sector_buffer[ATA_IDENT_WORD_NUM_OF_CURR_LOG_CLNDS]);
+ device_entry->heads =
+ CF_LE_W(sector_buffer[ATA_IDENT_WORD_NUM_OF_CURR_LOG_HEADS]);
+ device_entry->sectors =
+ CF_LE_W(sector_buffer[ATA_IDENT_WORD_NUM_OF_CURR_LOG_SECS]);
+ device_entry->lba_sectors =
+ CF_LE_W(sector_buffer[ATA_IDENT_WORD_NUM_OF_USR_SECS1]);
+ device_entry->lba_sectors <<= 16;
+ device_entry->lba_sectors += CF_LE_W(sector_buffer[ATA_IDENT_WORD_NUM_OF_USR_SECS0]);
+ device_entry->lba_avaible =
+ (CF_LE_W(sector_buffer[ATA_IDENT_WORD_CAPABILITIES]) >> 9) & 0x1;
+
+ if ((CF_LE_W(sector_buffer[ATA_IDENT_WORD_FIELD_VALIDITY]) &
+ ATA_IDENT_BIT_VALID) == 0) {
+ /* no "supported modes" info -> use default */
+ device_entry->mode_active = ATA_MODES_PIO3;
+ } else {
+ device_entry->modes_available =
+ ((CF_LE_W(sector_buffer[64]) & 0x1) ? ATA_MODES_PIO3 : 0) |
+ ((CF_LE_W(sector_buffer[64]) & 0x2) ? ATA_MODES_PIO4 : 0) |
+ ((CF_LE_W(sector_buffer[63]) & 0x1) ? ATA_MODES_DMA0 : 0) |
+ ((CF_LE_W(sector_buffer[63]) & 0x2) ?
+ ATA_MODES_DMA0 | ATA_MODES_DMA1 : 0) |
+ ((CF_LE_W(sector_buffer[63]) & 0x4) ?
+ ATA_MODES_DMA0 | ATA_MODES_DMA1 | ATA_MODES_DMA2 : 0);
+ if (device_entry->modes_available == 0) {
+ return RTEMS_IO_ERROR;
+ }
+ }
+
+ return RTEMS_SUCCESSFUL;
+}
diff --git a/bsps/shared/dev/ide/ide_controller.c b/bsps/shared/dev/ide/ide_controller.c
new file mode 100644
index 0000000000..912f9e3157
--- /dev/null
+++ b/bsps/shared/dev/ide/ide_controller.c
@@ -0,0 +1,200 @@
+/*
+ * ide_controller.c
+ *
+ * This is generic rtems driver for IDE controllers.
+ *
+ * Copyright (C) 2001 OKTET Ltd., St.-Petersburg, Russia
+ * Authors: Alexandra Kossovsky <sasha@oktet.ru>
+ * Eugeny S. Mints <Eugeny.Mints@oktet.ru>
+ *
+ * The license and distribution terms for this file may be
+ * found in the file LICENSE in this distribution or at
+ * http://www.rtems.org/license/LICENSE.
+ *
+ */
+
+#define IDE_CONTROLLER_TRACE 0
+
+#include <rtems/chain.h>
+#include <errno.h>
+#include <rtems/blkdev.h>
+
+#include <libchip/ide_ctrl.h>
+#include <libchip/ide_ctrl_cfg.h>
+#include <libchip/ide_ctrl_io.h>
+
+#if IDE_CONTROLLER_TRACE
+int ide_controller_trace = 1;
+#endif
+
+/*
+ * ide_controller_initialize --
+ * Initializes all configured IDE controllers. Controllers configuration
+ * table is provided by BSP
+ *
+ * PARAMETERS:
+ * major - device major number
+ * minor_arg - device minor number
+ * args - arguments
+ *
+ * RETURNS:
+ * RTEMS_SUCCESSFUL on success, or error code if
+ * error occured
+ */
+rtems_device_driver
+ide_controller_initialize(rtems_device_major_number major,
+ rtems_device_minor_number minor_arg,
+ void *args)
+{
+ unsigned long minor;
+
+ /* FIXME: may be it should be done on compilation phase */
+ if (IDE_Controller_Count > IDE_CTRL_MAX_MINOR_NUMBER)
+ rtems_fatal_error_occurred(RTEMS_TOO_MANY);
+
+ for (minor=0; minor < IDE_Controller_Count; minor++)
+ {
+ IDE_Controller_Table[minor].status = IDE_CTRL_NON_INITIALIZED;
+
+ if ((IDE_Controller_Table[minor].probe == NULL ||
+ IDE_Controller_Table[minor].probe(minor)) &&
+ (IDE_Controller_Table[minor].fns->ctrl_probe == NULL ||
+ IDE_Controller_Table[minor].fns->ctrl_probe(minor)))
+ {
+ dev_t dev;
+ dev = rtems_filesystem_make_dev_t( major, minor );
+ if (mknod(IDE_Controller_Table[minor].name,
+ 0777 | S_IFBLK, dev ) < 0)
+ rtems_fatal_error_occurred(errno);
+ IDE_Controller_Table[minor].fns->ctrl_initialize(minor);
+ IDE_Controller_Table[minor].status = IDE_CTRL_INITIALIZED;
+ }
+ }
+ return RTEMS_SUCCESSFUL;
+}
+
+/*
+ * ide_controller_read_data_block --
+ * Read data block via controller's data register
+ *
+ * PARAMETERS:
+ * minor - minor number of controller
+ * block_size - number of bytes to read
+ * bufs - set of buffers to store data
+ * cbuf - number of current buffer from the set
+ * pos - position inside current buffer 'cbuf'
+ *
+ * RETURNS:
+ * NONE
+ */
+void
+ide_controller_read_data_block(rtems_device_minor_number minor,
+ uint32_t block_size,
+ rtems_blkdev_sg_buffer *bufs,
+ uint32_t *cbuf,
+ uint32_t *pos)
+{
+#if IDE_CONTROLLER_TRACE
+ if (ide_controller_trace)
+ printk ("IDE data block read: %d:%d\n", *cbuf, bufs[*cbuf].block);
+#endif
+ IDE_Controller_Table[minor].fns->ctrl_read_block(minor, block_size, bufs,
+ cbuf, pos);
+}
+
+/*
+ * ide_controller_write_data_block --
+ * Write data block via controller's data register
+ *
+ * PARAMETERS:
+ * minor - minor number of controller
+ * block_size - number of bytes to write
+ * bufs - set of buffers which store data
+ * cbuf - number of current buffer from the set
+ * pos - position inside current buffer 'cbuf'
+ *
+ * RETURNS:
+ * NONE
+ */
+void
+ide_controller_write_data_block(rtems_device_minor_number minor,
+ uint32_t block_size,
+ rtems_blkdev_sg_buffer *bufs,
+ uint32_t *cbuf,
+ uint32_t *pos)
+
+{
+#if IDE_CONTROLLER_TRACE
+ if (ide_controller_trace)
+ printk ("IDE data block write: %d:%d\n", *cbuf, bufs[*cbuf].block);
+#endif
+ IDE_Controller_Table[minor].fns->ctrl_write_block(minor, block_size, bufs,
+ cbuf, pos);
+}
+
+/*
+ * ide_controller_read_register --
+ * Read controller's register
+ *
+ * PARAMETERS:
+ * minor - minor number of controller
+ * reg - register to read
+ * value - placeholder for result
+ *
+ * RETURNS
+ * NONE
+ */
+void
+ide_controller_read_register(rtems_device_minor_number minor,
+ int reg,
+ uint16_t *value)
+{
+ IDE_Controller_Table[minor].fns->ctrl_reg_read(minor, reg, value);
+#if IDE_CONTROLLER_TRACE
+ if (ide_controller_trace)
+ printk ("IDE read reg: %d => %04x\n", reg, *value);
+#endif
+}
+
+/*
+ * ide_controller_write_register --
+ * Write controller's register
+ *
+ * PARAMETERS:
+ * minor - minor number of controller
+ * reg - register to write
+ * value - value to write
+ *
+ * RETURNS:
+ * NONE
+ */
+void
+ide_controller_write_register(rtems_device_minor_number minor, int reg,
+ uint16_t value)
+{
+#if IDE_CONTROLLER_TRACE
+ if (ide_controller_trace)
+ printk ("IDE write reg: %d => %04x\n", reg, value);
+#endif
+ IDE_Controller_Table[minor].fns->ctrl_reg_write(minor, reg, value);
+}
+
+/*
+ * ide_controller_config_io_speed --
+ * Set controller's speed of IO operations
+ *
+ * PARAMETERS:
+ * minor - minor number of controller
+ * modes_available - speeds available
+ *
+ * RETURNS:
+ * RTEMS_SUCCESSFUL on success, or error code if
+ * error occured
+ */
+rtems_status_code
+ide_controller_config_io_speed(int minor, uint16_t modes_available)
+{
+ return IDE_Controller_Table[minor].fns->ctrl_config_io_speed(
+ minor,
+ modes_available);
+}
diff --git a/bsps/shared/dev/rtc/README.ds1643 b/bsps/shared/dev/rtc/README.ds1643
new file mode 100644
index 0000000000..a3a38605c8
--- /dev/null
+++ b/bsps/shared/dev/rtc/README.ds1643
@@ -0,0 +1,3 @@
+The Mostek M48T08 is compatible with the Dallas Semiconductor DS1643. Please
+use that driver.
+
diff --git a/bsps/shared/dev/rtc/README.icm7170 b/bsps/shared/dev/rtc/README.icm7170
new file mode 100644
index 0000000000..d4ecff570f
--- /dev/null
+++ b/bsps/shared/dev/rtc/README.icm7170
@@ -0,0 +1,48 @@
+
+Configuration Table Use
+=======================
+
+sDeviceName
+
+ The name of this device.
+
+deviceType
+
+ This field must be RTC_ICM7170.
+
+pDeviceFns
+
+ The device interface control table. This must be icm7170_fns.
+
+deviceProbe
+
+ This is the address of the routine which probes to see if the device
+ is present.
+
+pDeviceParams
+
+ This field specifies the clock frequency. It may be one of the
+ following:
+ ICM7170_AT_32_KHZ
+ ICM7170_AT_1_MHZ
+ ICM7170_AT_2_MHZ
+ ICM7170_AT_4_MHZ
+
+ulCtrlPort1
+
+ This field is the base address of the RTC area of the chip.
+
+ulCtrlPort2
+
+ This field is ignored.
+
+ulDataPort
+
+ This field is ignored.
+
+
+getRegister
+setRegister
+
+ These follow standard conventions.
+
diff --git a/bsps/shared/dev/rtc/README.m48t08 b/bsps/shared/dev/rtc/README.m48t08
new file mode 100644
index 0000000000..25c032e85e
--- /dev/null
+++ b/bsps/shared/dev/rtc/README.m48t08
@@ -0,0 +1,44 @@
+
+Configuration Table Use
+=======================
+
+sDeviceName
+
+ The name of this device.
+
+deviceType
+
+ This field must be RTC_M48T08.
+
+pDeviceFns
+
+ The device interface control table. This must be m48t08_fns.
+
+deviceProbe
+
+ This is the address of the routine which probes to see if the device
+ is present.
+
+pDeviceParams
+
+ This is ignored and should be NULL.
+
+ulCtrlPort1
+
+ This field is the base address of the RTC area of the chip. The
+ NVRAM portion of the chip is ignored.
+
+ulCtrlPort2
+
+ This field is ignored.
+
+ulDataPort
+
+ This field is ignored.
+
+
+getRegister
+setRegister
+
+ These follow standard conventions.
+
diff --git a/bsps/shared/dev/rtc/README.m48t18 b/bsps/shared/dev/rtc/README.m48t18
new file mode 100644
index 0000000000..0925c62115
--- /dev/null
+++ b/bsps/shared/dev/rtc/README.m48t18
@@ -0,0 +1 @@
+This is supported by the m48t08 driver.
diff --git a/bsps/shared/dev/rtc/README.mc146818a b/bsps/shared/dev/rtc/README.mc146818a
new file mode 100644
index 0000000000..e9a9c86447
--- /dev/null
+++ b/bsps/shared/dev/rtc/README.mc146818a
@@ -0,0 +1 @@
+This is supported by the mc146818a driver.
diff --git a/bsps/shared/dev/rtc/STATUS b/bsps/shared/dev/rtc/STATUS
new file mode 100644
index 0000000000..a6d5c41cd5
--- /dev/null
+++ b/bsps/shared/dev/rtc/STATUS
@@ -0,0 +1,33 @@
+General
+=======
+
++ It would be nice to utilize the interrupt capabilities of some
+ RTC parts. This could be used to trigger checking the software
+ clock against the hardware clock.
+
++ The periodic capability of most RTCs is not suitable for use
+ as a general purpose flexible clock tick source. For example,
+ many RTCs generate only a handful of periods with 100 Hz being the
+ most frequent.
+
++ The tick field is not set on get. Anything smaller than a second
+ is ignored on set and get operations.
+
++ Day of week is ignored since RTEMS does not set it internally.
+
++ There is no attempt in RTEMS to know about time zones.
+
+Harris ICM7170
+==============
+
++ Tested on a DMV177.
+
++ Interrupt capabilities are ignored.
+
+Mostek 48T08
+============
+
++ Untested.
+
++ NVRAM is ignored.
+
diff --git a/bsps/shared/dev/rtc/ds1375.c b/bsps/shared/dev/rtc/ds1375.c
new file mode 100644
index 0000000000..4a23a0044b
--- /dev/null
+++ b/bsps/shared/dev/rtc/ds1375.c
@@ -0,0 +1,461 @@
+/* Driver for the Maxim 1375 i2c RTC (TOD only; very simple...) */
+
+/*
+ * Authorship
+ * ----------
+ * This software was created by
+ *
+ * Till Straumann <strauman@slac.stanford.edu>, 2005-2007,
+ * Stanford Linear Accelerator Center, Stanford University.
+ *
+ * Acknowledgement of sponsorship
+ * ------------------------------
+ * The software was produced by
+ * the Stanford Linear Accelerator Center, Stanford University,
+ * under Contract DE-AC03-76SFO0515 with the Department of Energy.
+ *
+ * Government disclaimer of liability
+ * ----------------------------------
+ * Neither the United States nor the United States Department of Energy,
+ * nor any of their employees, makes any warranty, express or implied, or
+ * assumes any legal liability or responsibility for the accuracy,
+ * completeness, or usefulness of any data, apparatus, product, or process
+ * disclosed, or represents that its use would not infringe privately owned
+ * rights.
+ *
+ * Stanford disclaimer of liability
+ * --------------------------------
+ * Stanford University makes no representations or warranties, express or
+ * implied, nor assumes any liability for the use of this software.
+ *
+ * Stanford disclaimer of copyright
+ * --------------------------------
+ * Stanford University, owner of the copyright, hereby disclaims its
+ * copyright and all other rights in this software. Hence, anyone may
+ * freely use it for any purpose without restriction.
+ *
+ * Maintenance of notices
+ * ----------------------
+ * In the interest of clarity regarding the origin and status of this
+ * SLAC software, this and all the preceding Stanford University notices
+ * are to remain affixed to any copy or derivative of this software made
+ * or distributed by the recipient and are to be affixed to any copy of
+ * software made or distributed by the recipient that contains a copy or
+ * derivative of this software.
+ *
+ * ------------------ SLAC Software Notices, Set 4 OTT.002a, 2004 FEB 03
+ */
+
+/* This driver uses the file-system interface to the i2c bus */
+
+#include <unistd.h> /* write, read, close */
+
+#include <rtems.h>
+#include <rtems/bspIo.h>
+#include <rtems/rtc.h>
+#include <rtems/score/sysstate.h>
+#include <libchip/rtc.h>
+#include <libchip/ds1375-rtc.h>
+
+#include <sys/fcntl.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <inttypes.h>
+
+
+#define STATIC static
+#undef DEBUG
+
+/* The RTC driver routines are possibly called during
+ * system initialization -- that would be prior to opening
+ * the console. At this point it is not safe to use stdio
+ * (printf, perror etc.).
+ * Our file descriptors may even be 0..2
+ */
+#define STDIOSAFE(fmt,args...) \
+ do { \
+ if ( _System_state_Is_up( _System_state_Get() ) ) { \
+ fprintf(stderr,fmt,args); \
+ } else { \
+ printk(fmt,args); \
+ } \
+ } while (0)
+
+
+STATIC uint8_t ds1375_bcd2bin(uint8_t x)
+{
+ uint8_t h = x & 0xf0;
+
+ /* 8*hi + 2*hi + lo */
+ return ( h >> 1 ) + ( h >> 3 ) + ( x & 0xf );
+}
+
+STATIC uint8_t ds1375_bin2bcd(uint8_t x)
+{
+ uint8_t h = x/10;
+
+ return ( h << 4 ) + ( x - ( ( h << 3 ) + ( h << 1 ) ) );
+}
+
+/*
+ * Register Definitions and Access Macros
+ *
+ * The xxx_REG macros are offsets into the register files
+ * The xxx_OFF macros are offsets into a in-memory buffer
+ * starting at the seconds (for the 1375 both,
+ * _REG and _OFF happen to be identical).
+ */
+#define DS1375_SEC_REG 0x0
+#define DS1375_SEC_OFF (DS1375_SEC_REG-DS1375_SEC_REG)
+/* Extract seconds and convert to binary */
+#define DS1375_SEC(x) ds1375_bcd2bin( ((x)[DS1375_SEC_OFF]) & 0x7f )
+
+#define DS1375_MIN_REG 0x1
+#define DS1375_MIN_OFF (DS1375_MIN_REG-DS1375_SEC_REG)
+/* Extract minutes and convert to binary */
+#define DS1375_MIN(x) ds1375_bcd2bin( ((x)[DS1375_MIN_OFF]) & 0x7f )
+
+#define DS1375_HR_REG 0x2
+#define DS1375_HR_OFF (DS1375_HR_REG-DS1375_SEC_REG)
+#define DS1375_HR_1224 (1<<6)
+#define DS1375_HR_AMPM (1<<5)
+/* Are hours in AM/PM representation ? */
+#define DS1375_IS_AMPM(x) (DS1375_HR_1224 & ((x)[DS1375_HR_OFF]))
+/* Are we PM ? */
+#define DS1375_IS_PM(x) (DS1375_HR_AMPM & ((x)[DS1375_HR_OFF]))
+/* Extract hours (12h mode) and convert to binary */
+#define DS1375_HR_12(x) ds1375_bcd2bin( ((x)[DS1375_HR_OFF]) & 0x1f )
+/* Extract hours (24h mode) and convert to binary */
+#define DS1375_HR_24(x) ds1375_bcd2bin( ((x)[DS1375_HR_OFF]) & 0x3f )
+
+#define DS1375_DAY_REG 0x3
+#define DS1375_DAY_OFF (DS1375_DAY_REG-DS1375_SEC_REG)
+#define DS1375_DAT_REG 0x4
+#define DS1375_DAT_OFF (DS1375_DAT_REG-DS1375_SEC_REG)
+/* Extract date and convert to binary */
+#define DS1375_DAT(x) ds1375_bcd2bin( ((x)[DS1375_DAT_OFF]) & 0x3f )
+#define DS1375_MON_REG 0x5
+#define DS1375_MON_OFF (DS1375_MON_REG-DS1375_SEC_REG)
+#define DS1375_MON_CTRY (1<<7)
+/* Is century bit set ? */
+#define DS1375_IS_CTRY(x) (((x)[DS1375_MON_OFF]) & DS1375_MON_CTRY)
+/* Extract month and convert to binary */
+#define DS1375_MON(x) ds1375_bcd2bin( ((x)[DS1375_MON_OFF]) & 0x1f )
+
+#define DS1375_YR_REG 0x6
+#define DS1375_YR_OFF (DS1375_YR_REG-DS1375_SEC_REG)
+/* Extract year and convert to binary */
+#define DS1375_YR(x) ds1375_bcd2bin( ((x)[DS1375_YR_OFF]) & 0xff )
+
+/* CR Register and bit definitions */
+#define DS1375_CR_REG 0xe
+#define DS1375_CR_ECLK (1<<7)
+#define DS1375_CR_CLKSEL1 (1<<6)
+#define DS1375_CR_CLKSEL0 (1<<5)
+#define DS1375_CR_RS2 (1<<4)
+#define DS1375_CR_RS1 (1<<3)
+#define DS1375_CR_INTCN (1<<2)
+#define DS1375_CR_A2IE (1<<1)
+#define DS1375_CR_A1IE (1<<0)
+
+#define DS1375_CSR_REG 0xf
+
+/* User SRAM (8 bytes) */
+#define DS1375_RAM 0x10 /* start of 8 bytes user ram */
+
+/* Access Primitives */
+
+STATIC int rd_bytes(
+ int fd,
+ uint32_t off,
+ uint8_t *buf,
+ int len
+)
+{
+ uint8_t ptr = off;
+
+ return 1 == write( fd, &ptr, 1 ) && len == read( fd, buf, len ) ? 0 : -1;
+}
+
+STATIC int wr_bytes(
+ int fd,
+ uint32_t off,
+ uint8_t *buf,
+ int len
+)
+{
+ uint8_t d[ len + 1 ];
+
+ /* Must not break up writing of the register pointer and
+ * the data to-be-written into multiple write() calls
+ * because every 'write()' operation sends START and
+ * the chip interprets the first byte after START as
+ * the register pointer.
+ */
+
+ d[0] = off;
+ memcpy( d + 1, buf, len );
+
+ return len + 1 == write( fd, d, len + 1 ) ? 0 : -1;
+}
+
+/* Helpers */
+
+static int getfd(
+ int minor
+)
+{
+ return open( (const char *)RTC_Table[minor].ulCtrlPort1, O_RDWR );
+}
+
+/* Driver Access Functions */
+
+STATIC void ds1375_initialize(
+ int minor
+)
+{
+ int fd;
+ uint8_t cr;
+
+ if ( ( fd = getfd( minor ) ) >= 0 ) {
+ if ( 0 == rd_bytes( fd, DS1375_CR_REG, &cr, 1 ) ) {
+ /* make sure clock is enabled */
+ if ( ! ( DS1375_CR_ECLK & cr ) ) {
+ cr |= DS1375_CR_ECLK;
+ wr_bytes( fd, DS1375_CR_REG, &cr, 1 );
+ }
+ }
+ close( fd );
+ }
+
+}
+
+STATIC int ds1375_get_time(
+ int minor,
+ rtems_time_of_day *time
+)
+{
+ int rval = -1;
+ int fd;
+ uint8_t buf[DS1375_YR_REG + 1 - DS1375_SEC_REG];
+
+ if ( time && ( ( fd = getfd( minor ) ) >= 0 ) ) {
+ if ( 0 == rd_bytes( fd, DS1375_SEC_REG, buf, sizeof(buf) ) ) {
+ time->year = DS1375_IS_CTRY( buf ) ? 2000 : 1900;
+ time->year += DS1375_YR ( buf );
+ time->month = DS1375_MON( buf );
+ time->day = DS1375_DAT( buf ); /* DAY is weekday */
+
+ if ( DS1375_IS_AMPM( buf ) ) {
+ time->hour = DS1375_HR_12 ( buf );
+ if ( DS1375_IS_PM( buf ) )
+ time->hour += 12;
+ } else {
+ time->hour = DS1375_HR_24 ( buf );
+ }
+
+ time->minute = DS1375_MIN( buf );
+ time->second = DS1375_SEC( buf );
+ time->ticks = 0;
+ rval = 0;
+ }
+ close( fd );
+ }
+ return rval;
+}
+
+STATIC int ds1375_set_time(
+ int minor,
+ const rtems_time_of_day *time
+)
+{
+ int rval = -1;
+ int fd = -1;
+ time_t secs;
+ struct tm tm;
+ uint8_t buf[DS1375_YR_REG + 1 - DS1375_SEC_REG];
+ uint8_t cr = 0xff;
+
+ /*
+ * The clock hardware maintains the day-of-week as a separate counter
+ * so we must compute it ourselves (rtems_time_of_day doesn't come
+ * with a day of week).
+ */
+ secs = _TOD_To_seconds( time );
+ /* we're only interested in tm_wday... */
+ gmtime_r( &secs, &tm );
+
+ buf[DS1375_SEC_OFF] = ds1375_bin2bcd( time->second );
+ buf[DS1375_MIN_OFF] = ds1375_bin2bcd( time->minute );
+ /* bin2bcd(hour) implicitly selects 24h mode since ms-bit is clear */
+ buf[DS1375_HR_OFF] = ds1375_bin2bcd( time->hour );
+ buf[DS1375_DAY_OFF] = tm.tm_wday + 1;
+ buf[DS1375_DAT_OFF] = ds1375_bin2bcd( time->day );
+ buf[DS1375_MON_OFF] = ds1375_bin2bcd( time->month );
+
+ if ( time->year >= 2000 ) {
+ buf[DS1375_YR_OFF] = ds1375_bin2bcd( time->year - 2000 );
+ buf[DS1375_MON_OFF] |= DS1375_MON_CTRY;
+ } else {
+ buf[DS1375_YR_OFF] = ds1375_bin2bcd( time->year - 1900 );
+ }
+
+ /*
+ * Stop clock; update registers and restart. This is slightly
+ * slower than just writing everyting but if we did that we
+ * could get inconsistent registers if this routine would not
+ * complete in less than 1s (says the datasheet) and we don't
+ * know if we are going to be pre-empted for some time...
+ */
+ if ( ( fd = getfd( minor ) ) < 0 ) {
+ goto cleanup;
+ }
+
+ if ( rd_bytes( fd, DS1375_CR_REG, &cr, 1 ) )
+ goto cleanup;
+
+ cr &= ~DS1375_CR_ECLK;
+
+ /* This stops the clock */
+ if ( wr_bytes( fd, DS1375_CR_REG, &cr, 1 ) )
+ goto cleanup;
+
+ /* write new contents */
+ if ( wr_bytes( fd, DS1375_SEC_REG, buf, sizeof(buf) ) )
+ goto cleanup;
+
+ rval = 0;
+
+cleanup:
+ if ( fd >= 0 ) {
+ if ( ! ( DS1375_CR_ECLK & cr ) ) {
+ /* start clock; this handles some cases of failure
+ * after stopping the clock by restarting it again
+ */
+ cr |= DS1375_CR_ECLK;
+ if ( wr_bytes( fd, DS1375_CR_REG, &cr, 1 ) )
+ rval = -1;
+ }
+ close( fd );
+ }
+ return rval;
+}
+
+/* Debugging / Testing */
+
+#ifdef DEBUG
+
+/* Don't forget to set "TZ" when using these test routines */
+
+/* What is rtems_time_of_day good for ? Why not use std types ? */
+
+uint32_t
+ds1375_get_time_tst()
+{
+rtems_time_of_day rtod;
+time_t secs;
+
+ ds1375_get_time( 0, &rtod );
+ secs = _TOD_To_seconds( &rtod );
+ printf( "%s\n", ctime( &secs ) );
+ return secs;
+}
+
+int
+ds1375_set_time_tst( const char *datstr, rtems_time_of_day *prt )
+{
+struct tm tm;
+time_t secs;
+rtems_time_of_day rt;
+
+ if ( !datstr )
+ return -1;
+
+ if ( ! strptime( datstr, "%Y-%m-%d/%T", &tm ) )
+ return -2;
+
+ if ( ! prt )
+ prt = &rt;
+
+ secs = mktime( &tm );
+
+ /* convert to UTC */
+ gmtime_r( &secs, &tm );
+
+ printf("Y: %"PRIu32" ", (prt->year = tm.tm_year + 1900) );
+ printf("M: %"PRIu32" ", (prt->month = tm.tm_mon + 1) );
+ printf("D: %"PRIu32" ", (prt->day = tm.tm_mday ) );
+ printf("h: %"PRIu32" ", (prt->hour = tm.tm_hour ) );
+ printf("m: %"PRIu32" ", (prt->minute = tm.tm_min ) );
+ printf("s: %"PRIu32"\n", (prt->second = tm.tm_sec ) );
+ prt->ticks = 0;
+
+ return ( prt == &rt ) ? ds1375_set_time( 0, &rt ) : 0;
+}
+
+#endif
+
+
+uint32_t
+rtc_ds1375_get_register( uintptr_t port, uint8_t reg )
+{
+int fd;
+uint8_t v;
+uint32_t rval = -1;
+
+ if ( ( fd = open( (const char*)port, O_RDWR ) ) >= 0 ) {
+
+ if ( 0 == rd_bytes( fd, reg, &v, 1 ) ) {
+ rval = v;
+ }
+ close( fd );
+ }
+
+ return rval;
+}
+
+void
+rtc_ds1375_set_register( uintptr_t port, uint8_t reg, uint32_t value )
+{
+int fd;
+uint8_t v = value;
+
+ if ( ( fd = open( (const char*)port, O_RDWR ) ) >= 0 ) {
+ wr_bytes( fd, reg, &v, 1 );
+ close( fd );
+ }
+
+}
+
+bool rtc_ds1375_device_probe(
+ int minor
+)
+{
+ int fd;
+
+ if ( ( fd = getfd( minor ) ) < 0 ) {
+ STDIOSAFE( "ds1375_probe (open): %s\n", strerror( errno ) );
+ return false;
+ }
+
+ /* Try to set file pointer */
+ if ( 0 != wr_bytes( fd, DS1375_SEC_REG, 0, 0 ) ) {
+ STDIOSAFE( "ds1375_probe (wr_bytes): %s\n", strerror( errno ) );
+ close( fd );
+ return false;
+ }
+
+ if ( close( fd ) ) {
+ STDIOSAFE( "ds1375_probe (close): %s\n", strerror( errno ) );
+ return false;
+ }
+
+ return true;
+}
+
+rtc_fns rtc_ds1375_fns = {
+ .deviceInitialize = ds1375_initialize,
+ .deviceGetTime = ds1375_get_time,
+ .deviceSetTime = ds1375_set_time,
+};
diff --git a/bsps/shared/dev/rtc/icm7170.c b/bsps/shared/dev/rtc/icm7170.c
new file mode 100644
index 0000000000..1cc9e980f7
--- /dev/null
+++ b/bsps/shared/dev/rtc/icm7170.c
@@ -0,0 +1,168 @@
+/*
+ * This file interfaces with the real-time clock found in
+ * a Harris ICM7170
+ *
+ * Year 2K Notes:
+ *
+ * This chip only uses a two digit field to store the year. This
+ * code uses the RTEMS Epoch as a pivot year. This lets us map the
+ * two digit year field as follows:
+ *
+ * + two digit years 0-87 are mapped to 2000-2087.
+ * + two digit years 88-99 are mapped to 1988-1999.
+ *
+ * This is less than the time span supported by RTEMS.
+ *
+ * COPYRIGHT (c) 1989-1999.
+ * On-Line Applications Research Corporation (OAR).
+ *
+ * The license and distribution terms for this file may be
+ * found in the file LICENSE in this distribution or at
+ * http://www.rtems.org/license/LICENSE.
+ */
+
+#include <rtems.h>
+#include <libchip/rtc.h>
+#include <libchip/icm7170.h>
+
+/*
+ * Control register bits
+ */
+
+/* XXX */
+
+/*
+ * icm7170_initialize
+ */
+
+static void icm7170_initialize(
+ int minor
+)
+{
+ uintptr_t icm7170;
+ setRegister_f setReg;
+ uintptr_t clock;
+
+ icm7170 = RTC_Table[ minor ].ulCtrlPort1;
+ setReg = RTC_Table[ minor ].setRegister;
+
+ /*
+ * Initialize the RTC with the proper clock frequency
+ */
+
+ clock = (uintptr_t) RTC_Table[ minor ].pDeviceParams;
+ (*setReg)( icm7170, ICM7170_CONTROL, 0x0c | clock );
+}
+
+/*
+ * icm7170_get_time
+ */
+
+static int icm7170_get_time(
+ int minor,
+ rtems_time_of_day *time
+)
+{
+ uint32_t icm7170;
+ getRegister_f getReg;
+ uint32_t year;
+
+ icm7170 = RTC_Table[ minor ].ulCtrlPort1;
+ getReg = RTC_Table[ minor ].getRegister;
+
+ /*
+ * Put the RTC into read mode
+ */
+
+ (void) (*getReg)( icm7170, ICM7170_COUNTER_HUNDREDTHS );
+
+ /*
+ * Now get the time
+ */
+
+
+ year = (*getReg)( icm7170, ICM7170_YEAR );
+ if ( year < 88 )
+ year += 2000;
+ else
+ year += 1900;
+
+ time->year = year;
+ time->month = (*getReg)( icm7170, ICM7170_MONTH );
+ time->day = (*getReg)( icm7170, ICM7170_DATE );
+ time->hour = (*getReg)( icm7170, ICM7170_HOUR );
+ time->minute = (*getReg)( icm7170, ICM7170_MINUTE );
+ time->second = (*getReg)( icm7170, ICM7170_SECOND );
+
+ time->ticks = 0;
+
+ /*
+ * Put the RTC back into normal mode.
+ */
+
+ (void) (*getReg)( icm7170, ICM7170_COUNTER_HUNDREDTHS );
+
+ return 0;
+}
+
+/*
+ * icm7170_set_time
+ */
+
+static int icm7170_set_time(
+ int minor,
+ const rtems_time_of_day *time
+)
+{
+ uintptr_t icm7170;
+ setRegister_f setReg;
+ uint32_t year;
+ uintptr_t clock;
+
+ icm7170 = RTC_Table[ minor ].ulCtrlPort1;
+ setReg = RTC_Table[ minor ].setRegister;
+ clock = (uintptr_t) RTC_Table[ minor ].pDeviceParams;
+
+ year = time->year;
+
+ if ( year >= 2088 )
+ rtems_fatal_error_occurred( RTEMS_INVALID_NUMBER );
+
+ if ( year >= 2000 )
+ year -= 2000;
+ else
+ year -= 1900;
+
+ (*setReg)( icm7170, ICM7170_CONTROL, 0x04 | clock );
+
+ (*setReg)( icm7170, ICM7170_YEAR, year );
+ (*setReg)( icm7170, ICM7170_MONTH, time->month );
+ (*setReg)( icm7170, ICM7170_DATE, time->day );
+ (*setReg)( icm7170, ICM7170_HOUR, time->hour );
+ (*setReg)( icm7170, ICM7170_MINUTE, time->minute );
+ (*setReg)( icm7170, ICM7170_SECOND, time->second );
+
+ /*
+ * This is not really right.
+ */
+
+ (*setReg)( icm7170, ICM7170_DAY_OF_WEEK, 1 );
+
+ /*
+ * Put the RTC back into normal mode.
+ */
+
+ (*setReg)( icm7170, ICM7170_CONTROL, 0x0c | clock );
+
+ return 0;
+}
+
+/*
+ * Driver function table
+ */
+
+rtc_fns icm7170_fns = {
+ icm7170_initialize,
+ icm7170_get_time,
+ icm7170_set_time
+};
diff --git a/bsps/shared/dev/rtc/icm7170_reg.c b/bsps/shared/dev/rtc/icm7170_reg.c
new file mode 100644
index 0000000000..747f1f218d
--- /dev/null
+++ b/bsps/shared/dev/rtc/icm7170_reg.c
@@ -0,0 +1,60 @@
+/*
+ * This file contains a typical set of register access routines which may be
+ * used with the icm7170 chip if accesses to the chip are as follows:
+ *
+ * + registers are accessed as bytes
+ * + registers are only byte-aligned (no address gaps)
+ *
+ * COPYRIGHT (c) 1989-1997.
+ * On-Line Applications Research Corporation (OAR).
+ *
+ * The license and distribution terms for this file may be
+ * found in the file LICENSE in this distribution or at
+ * http://www.rtems.org/license/LICENSE.
+ */
+
+#include <rtems.h>
+#include <libchip/rtc.h>
+#include <libchip/icm7170.h>
+
+#ifndef _ICM7170_MULTIPLIER
+#define _ICM7170_MULTIPLIER 1
+#define _ICM7170_NAME(_X) _X
+#define _ICM7170_TYPE uint8_t
+#endif
+
+#define CALCULATE_REGISTER_ADDRESS( _base, _reg ) \
+ (_ICM7170_TYPE *)((_base) + ((_reg) * _ICM7170_MULTIPLIER ))
+
+/*
+ * ICM7170 Get Register Routine
+ */
+
+uint32_t _ICM7170_NAME(icm7170_get_register)(
+ uintptr_t ulCtrlPort,
+ uint8_t ucRegNum
+)
+{
+ _ICM7170_TYPE *port;
+
+ port = CALCULATE_REGISTER_ADDRESS( ulCtrlPort, ucRegNum );
+
+ return *port;
+}
+
+/*
+ * ICM7170 Set Register Routine
+ */
+
+void _ICM7170_NAME(icm7170_set_register)(
+ uintptr_t ulCtrlPort,
+ uint8_t ucRegNum,
+ uint32_t ucData
+)
+{
+ _ICM7170_TYPE *port;
+
+ port = CALCULATE_REGISTER_ADDRESS( ulCtrlPort, ucRegNum );
+
+ *port = ucData;
+}
diff --git a/bsps/shared/dev/rtc/icm7170_reg2.c b/bsps/shared/dev/rtc/icm7170_reg2.c
new file mode 100644
index 0000000000..179d76c6f5
--- /dev/null
+++ b/bsps/shared/dev/rtc/icm7170_reg2.c
@@ -0,0 +1,20 @@
+/*
+ * This file contains a typical set of register access routines which may be
+ * used with the icm7170 chip if accesses to the chip are as follows:
+ *
+ * + registers are accessed as bytes
+ * + registers are on 16-bit boundaries
+ *
+ * COPYRIGHT (c) 1989-1997.
+ * On-Line Applications Research Corporation (OAR).
+ *
+ * The license and distribution terms for this file may be
+ * found in the file LICENSE in this distribution or at
+ * http://www.rtems.org/license/LICENSE.
+ */
+
+#define _ICM7170_MULTIPLIER 2
+#define _ICM7170_NAME(_X) _X##_2
+#define _ICM7170_TYPE uint8_t
+
+#include "icm7170_reg.c"
diff --git a/bsps/shared/dev/rtc/icm7170_reg4.c b/bsps/shared/dev/rtc/icm7170_reg4.c
new file mode 100644
index 0000000000..dada40961c
--- /dev/null
+++ b/bsps/shared/dev/rtc/icm7170_reg4.c
@@ -0,0 +1,20 @@
+/*
+ * This file contains a typical set of register access routines which may be
+ * used with the icm7170 chip if accesses to the chip are as follows:
+ *
+ * + registers are accessed as bytes
+ * + registers are on 32-bit boundaries
+ *
+ * COPYRIGHT (c) 1989-1997.
+ * On-Line Applications Research Corporation (OAR).
+ *
+ * The license and distribution terms for this file may be
+ * found in the file LICENSE in this distribution or at
+ * http://www.rtems.org/license/LICENSE.
+ */
+
+#define _ICM7170_MULTIPLIER 4
+#define _ICM7170_NAME(_X) _X##_4
+#define _ICM7170_TYPE uint8_t
+
+#include "icm7170_reg.c"
diff --git a/bsps/shared/dev/rtc/icm7170_reg8.c b/bsps/shared/dev/rtc/icm7170_reg8.c
new file mode 100644
index 0000000000..a1fb1a5ea2
--- /dev/null
+++ b/bsps/shared/dev/rtc/icm7170_reg8.c
@@ -0,0 +1,20 @@
+/*
+ * This file contains a typical set of register access routines which may be
+ * used with the icm7170 chip if accesses to the chip are as follows:
+ *
+ * + registers are accessed as bytes
+ * + registers are on 64-bit boundaries
+ *
+ * COPYRIGHT (c) 1989-1997.
+ * On-Line Applications Research Corporation (OAR).
+ *
+ * The license and distribution terms for this file may be
+ * found in the file LICENSE in this distribution or at
+ * http://www.rtems.org/license/LICENSE.
+ */
+
+#define _ICM7170_MULTIPLIER 8
+#define _ICM7170_NAME(_X) _X##_8
+#define _ICM7170_TYPE uint8_t
+
+#include "icm7170_reg.c"
diff --git a/bsps/shared/dev/rtc/m48t08.c b/bsps/shared/dev/rtc/m48t08.c
new file mode 100644
index 0000000000..3b600bd995
--- /dev/null
+++ b/bsps/shared/dev/rtc/m48t08.c
@@ -0,0 +1,161 @@
+/*
+ * This file interfaces with the real-time clock found in
+ * a Mostek M48T08 or M48T18 or compatibles.
+ *
+ * Year 2K Notes:
+ *
+ * This chip only uses a two digit field to store the year. This
+ * code uses the RTEMS Epoch as a pivot year. This lets us map the
+ * two digit year field as follows:
+ *
+ * + two digit years 0-87 are mapped to 2000-2087.
+ * + two digit years 88-99 are mapped to 1988-1999.
+ *
+ * This is less than the time span supported by RTEMS.
+ *
+ * COPYRIGHT (c) 1989-1999.
+ * On-Line Applications Research Corporation (OAR).
+ *
+ * The license and distribution terms for this file may be
+ * found in the file LICENSE in this distribution or at
+ * http://www.rtems.org/license/LICENSE.
+ */
+
+#include <rtems.h>
+#include <libchip/rtc.h>
+#include <libchip/m48t08.h>
+
+/*
+ * Control register bits
+ */
+
+#define M48T08_CONTROL_WRITE 0x80
+#define M48T08_CONTROL_READ 0x40
+#define M48T08_CONTROL_SIGN 0x20
+
+/*
+ * m48t08_initialize
+ */
+
+static void m48t08_initialize(
+ int minor
+)
+{
+}
+
+/*
+ * m48t08_get_time
+ */
+
+#define From_BCD( _x ) ((((_x) >> 4) * 10) + ((_x) & 0x0F))
+#define To_BCD( _x ) ((((_x) / 10) << 4) + ((_x) % 10))
+
+static int m48t08_get_time(
+ int minor,
+ rtems_time_of_day *time
+)
+{
+ uint32_t m48t08;
+ getRegister_f getReg;
+ setRegister_f setReg;
+ uint8_t controlReg;
+ uint32_t value1;
+ uint32_t value2;
+
+ m48t08 = RTC_Table[ minor ].ulCtrlPort1;
+ getReg = RTC_Table[ minor ].getRegister;
+ setReg = RTC_Table[ minor ].setRegister;
+
+ /*
+ * Put the RTC into read mode
+ */
+
+ controlReg = (*getReg)( m48t08, M48T08_CONTROL );
+ (*setReg)( m48t08, M48T08_CONTROL, controlReg | M48T08_CONTROL_READ );
+
+ value1 = (*getReg)( m48t08, M48T08_YEAR );
+ value2 = From_BCD( value1 );
+ if ( value2 < 88 )
+ time->year = 2000 + value2;
+ else
+ time->year = 1900 + value2;
+
+ value1 = (*getReg)( m48t08, M48T08_MONTH );
+ time->month = From_BCD( value1 );
+
+ value1 = (*getReg)( m48t08, M48T08_DATE );
+ time->day = From_BCD( value1 );
+
+ value1 = (*getReg)( m48t08, M48T08_HOUR );
+ time->hour = From_BCD( value1 );
+
+ value1 = (*getReg)( m48t08, M48T08_MINUTE );
+ time->minute = From_BCD( value1 );
+
+ value1 = (*getReg)( m48t08, M48T08_SECOND );
+ time->second = From_BCD( value1 );
+
+ time->ticks = 0;
+
+ /*
+ * Put the RTC back into normal mode.
+ */
+
+ (*setReg)( m48t08, M48T08_CONTROL, controlReg );
+
+ return 0;
+}
+
+/*
+ * m48t08_set_time
+ */
+
+static int m48t08_set_time(
+ int minor,
+ const rtems_time_of_day *time
+)
+{
+ uint32_t m48t08;
+ getRegister_f getReg;
+ setRegister_f setReg;
+ uint8_t controlReg;
+
+ m48t08 = RTC_Table[ minor ].ulCtrlPort1;
+ getReg = RTC_Table[ minor ].getRegister;
+ setReg = RTC_Table[ minor ].setRegister;
+
+ /*
+ * Put the RTC into read mode
+ */
+
+ controlReg = (*getReg)( m48t08, M48T08_CONTROL );
+ (*setReg)( m48t08, M48T08_CONTROL, controlReg | M48T08_CONTROL_WRITE );
+
+ if ( time->year >= 2088 )
+ rtems_fatal_error_occurred( RTEMS_INVALID_NUMBER );
+
+ (*setReg)( m48t08, M48T08_YEAR, To_BCD(time->year % 100) );
+ (*setReg)( m48t08, M48T08_MONTH, To_BCD(time->month) );
+ (*setReg)( m48t08, M48T08_DATE, To_BCD(time->day) );
+ (*setReg)( m48t08, M48T08_HOUR, To_BCD(time->hour) );
+ (*setReg)( m48t08, M48T08_MINUTE, To_BCD(time->minute) );
+ (*setReg)( m48t08, M48T08_SECOND, To_BCD(time->second) );
+
+ /*
+ * Put the RTC back into normal mode.
+ */
+
+ (*setReg)( m48t08, M48T08_CONTROL, controlReg );
+
+ return 0;
+}
+
+/*
+ * Driver function table
+ */
+
+rtc_fns m48t08_fns = {
+ m48t08_initialize,
+ m48t08_get_time,
+ m48t08_set_time
+};
diff --git a/bsps/shared/dev/rtc/m48t08_reg.c b/bsps/shared/dev/rtc/m48t08_reg.c
new file mode 100644
index 0000000000..2174496fda
--- /dev/null
+++ b/bsps/shared/dev/rtc/m48t08_reg.c
@@ -0,0 +1,60 @@
+/*
+ * This file contains a typical set of register access routines which may be
+ * used with the m48t08 chip if accesses to the chip are as follows:
+ *
+ * + registers are accessed as bytes
+ * + registers are only byte-aligned (no address gaps)
+ *
+ * COPYRIGHT (c) 1989-1997.
+ * On-Line Applications Research Corporation (OAR).
+ *
+ * The license and distribution terms for this file may be
+ * found in the file LICENSE in this distribution or at
+ * http://www.rtems.org/license/LICENSE.
+ */
+
+#include <rtems.h>
+#include <libchip/rtc.h>
+#include <libchip/m48t08.h>
+
+#ifndef _M48T08_MULTIPLIER
+#define _M48T08_MULTIPLIER 1
+#define _M48T08_NAME(_X) _X
+#define _M48T08_TYPE uint8_t
+#endif
+
+#define CALCULATE_REGISTER_ADDRESS( _base, _reg ) \
+ (_M48T08_TYPE *)((_base) + ((_reg) * _M48T08_MULTIPLIER ))
+
+/*
+ * M48T08 Get Register Routine
+ */
+
+uint32_t _M48T08_NAME(m48t08_get_register)(
+ uintptr_t ulCtrlPort,
+ uint8_t ucRegNum
+)
+{
+ _M48T08_TYPE *port;
+
+ port = CALCULATE_REGISTER_ADDRESS( ulCtrlPort, ucRegNum );
+
+ return *port;
+}
+
+/*
+ * M48T08 Set Register Routine
+ */
+
+void _M48T08_NAME(m48t08_set_register)(
+ uintptr_t ulCtrlPort,
+ uint8_t ucRegNum,
+ uint32_t ucData
+)
+{
+ _M48T08_TYPE *port;
+
+ port = CALCULATE_REGISTER_ADDRESS( ulCtrlPort, ucRegNum );
+
+ *port = ucData;
+}
diff --git a/bsps/shared/dev/rtc/m48t08_reg2.c b/bsps/shared/dev/rtc/m48t08_reg2.c
new file mode 100644
index 0000000000..87d2041946
--- /dev/null
+++ b/bsps/shared/dev/rtc/m48t08_reg2.c
@@ -0,0 +1,20 @@
+/*
+ * This file contains a typical set of register access routines which may be
+ * used with the m48t08 chip if accesses to the chip are as follows:
+ *
+ * + registers are accessed as bytes
+ * + registers are on 16-bit boundaries
+ *
+ * COPYRIGHT (c) 1989-1997.
+ * On-Line Applications Research Corporation (OAR).
+ *
+ * The license and distribution terms for this file may be
+ * found in the file LICENSE in this distribution or at
+ * http://www.rtems.org/license/LICENSE.
+ */
+
+#define _M48T08_MULTIPLIER 2
+#define _M48T08_NAME(_X) _X##_2
+#define _M48T08_TYPE uint8_t
+
+#include "m48t08_reg.c"
diff --git a/bsps/shared/dev/rtc/m48t08_reg4.c b/bsps/shared/dev/rtc/m48t08_reg4.c
new file mode 100644
index 0000000000..2203249503
--- /dev/null
+++ b/bsps/shared/dev/rtc/m48t08_reg4.c
@@ -0,0 +1,20 @@
+/*
+ * This file contains a typical set of register access routines which may be
+ * used with the m48t08 chip if accesses to the chip are as follows:
+ *
+ * + registers are accessed as bytes
+ * + registers are on 32-bit boundaries
+ *
+ * COPYRIGHT (c) 1989-1997.
+ * On-Line Applications Research Corporation (OAR).
+ *
+ * The license and distribution terms for this file may be
+ * found in the file LICENSE in this distribution or at
+ * http://www.rtems.org/license/LICENSE.
+ */
+
+#define _M48T08_MULTIPLIER 4
+#define _M48T08_NAME(_X) _X##_4
+#define _M48T08_TYPE uint8_t
+
+#include "m48t08_reg.c"
diff --git a/bsps/shared/dev/rtc/m48t08_reg8.c b/bsps/shared/dev/rtc/m48t08_reg8.c
new file mode 100644
index 0000000000..83044d752b
--- /dev/null
+++ b/bsps/shared/dev/rtc/m48t08_reg8.c
@@ -0,0 +1,20 @@
+/*
+ * This file contains a typical set of register access routines which may be
+ * used with the m48t08 chip if accesses to the chip are as follows:
+ *
+ * + registers are accessed as bytes
+ * + registers are on 64-bit boundaries
+ *
+ * COPYRIGHT (c) 1989-1997.
+ * On-Line Applications Research Corporation (OAR).
+ *
+ * The license and distribution terms for this file may be
+ * found in the file LICENSE in this distribution or at
+ * http://www.rtems.org/license/LICENSE.
+ */
+
+#define _M48T08_MULTIPLIER 8
+#define _M48T08_NAME(_X) _X##_8
+#define _M48T08_TYPE uint8_t
+
+#include "m48t08_reg.c"
diff --git a/bsps/shared/dev/rtc/mc146818a.c b/bsps/shared/dev/rtc/mc146818a.c
new file mode 100644
index 0000000000..2720ce5e8a
--- /dev/null
+++ b/bsps/shared/dev/rtc/mc146818a.c
@@ -0,0 +1,180 @@
+/*
+ * This file interfaces with the real-time clock found in
+ * a Motorola MC146818A (common on PC hardware)
+ *
+ * Year 2K Notes:
+ *
+ * This chip only uses a two digit field to store the year. This
+ * code uses the RTEMS Epoch as a pivot year. This lets us map the
+ * two digit year field as follows:
+ *
+ * + two digit years 0-87 are mapped to 2000-2087.
+ * + two digit years 88-99 are mapped to 1988-1999.
+ *
+ * This is less than the time span supported by RTEMS.
+ *
+ * COPYRIGHT (c) 1989-1999.
+ * On-Line Applications Research Corporation (OAR).
+ *
+ * The license and distribution terms for this file may be
+ * found in the file LICENSE in this distribution or at
+ * http://www.rtems.org/license/LICENSE.
+ */
+#include <rtems.h>
+#include <libchip/rtc.h>
+#include <libchip/mc146818a.h>
+
+#define From_BCD( _x ) ((((_x) >> 4) * 10) + ((_x) & 0x0F))
+#define To_BCD( _x ) ((((_x) / 10) << 4) + ((_x) % 10))
+
+/*
+ * See if chip is present
+ */
+bool mc146818a_probe(
+ int minor
+)
+{
+ uint32_t mc146818a;
+ getRegister_f getReg;
+ uint32_t value;
+
+ /*
+ * Verify that chip is present and that time is valid
+ */
+ mc146818a = RTC_Table[ minor ].ulCtrlPort1;
+ getReg = RTC_Table[ minor ].getRegister;
+ value = (*getReg)( mc146818a, MC146818A_STATUSD );
+ if ((value == 0) || (value == 0xFF))
+ return false;
+ return true;
+}
+
+/*
+ * Initialize chip
+ */
+static void mc146818a_initialize(
+ int minor
+)
+{
+ uintptr_t mc146818a;
+ setRegister_f setReg;
+
+ mc146818a = RTC_Table[ minor ].ulCtrlPort1;
+ setReg = RTC_Table[ minor ].setRegister;
+
+ (*setReg)(
+ mc146818a,
+ MC146818A_STATUSA,
+ MC146818ASA_DIVIDER|MC146818ASA_1024
+ );
+ (*setReg)(
+ mc146818a,
+ MC146818A_STATUSB,
+ MC146818ASB_24HR
+ );
+}
+
+/*
+ * Read time from chip
+ */
+static int mc146818a_get_time(
+ int minor,
+ rtems_time_of_day *time
+)
+{
+ uintptr_t mc146818a;
+ getRegister_f getReg;
+ uint32_t value;
+ rtems_interrupt_level level;
+
+ mc146818a = RTC_Table[ minor ].ulCtrlPort1;
+ getReg = RTC_Table[ minor ].getRegister;
+
+ /*
+ * No time if power failed
+ */
+ if (((*getReg)( mc146818a, MC146818A_STATUSD ) & MC146818ASD_PWR) == 0)
+ return -1;
+
+ /*
+ * Wait for time update to complete
+ */
+ rtems_interrupt_disable( level );
+ while (((*getReg)( mc146818a, MC146818A_STATUSA ) & MC146818ASA_TUP) != 0) {
+ rtems_interrupt_flash( level );
+ }
+
+ /*
+ * Read the time (we have at least 244 usec to do this)
+ */
+ value = (*getReg)( mc146818a, MC146818A_YEAR );
+ value = From_BCD( value );
+ if ( value < 88 )
+ time->year = 2000 + value;
+ else
+ time->year = 1900 + value;
+
+ value = (*getReg)( mc146818a, MC146818A_MONTH );
+ time->month = From_BCD( value );
+
+ value = (*getReg)( mc146818a, MC146818A_DAY );
+ time->day = From_BCD( value );
+
+ value = (*getReg)( mc146818a, MC146818A_HRS );
+ time->hour = From_BCD( value );
+
+ value = (*getReg)( mc146818a, MC146818A_MIN );
+ time->minute = From_BCD( value );
+
+ value = (*getReg)( mc146818a, MC146818A_SEC );
+ rtems_interrupt_enable( level );
+ time->second = From_BCD( value );
+ time->ticks = 0;
+
+ return 0;
+}
+
+/*
+ * Set time into chip
+ */
+static int mc146818a_set_time(
+ int minor,
+ const rtems_time_of_day *time
+)
+{
+ uint32_t mc146818a;
+ setRegister_f setReg;
+
+ mc146818a = RTC_Table[ minor ].ulCtrlPort1;
+ setReg = RTC_Table[ minor ].setRegister;
+
+ /*
+ * Stop the RTC
+ */
+ (*setReg)( mc146818a, MC146818A_STATUSB, MC146818ASB_HALT|MC146818ASB_24HR );
+
+ if ( time->year >= 2088 )
+ rtems_fatal_error_occurred( RTEMS_INVALID_NUMBER );
+
+ (*setReg)( mc146818a, MC146818A_YEAR, To_BCD(time->year % 100) );
+ (*setReg)( mc146818a, MC146818A_MONTH, To_BCD(time->month) );
+ (*setReg)( mc146818a, MC146818A_DAY, To_BCD(time->day) );
+ (*setReg)( mc146818a, MC146818A_HRS, To_BCD(time->hour) );
+ (*setReg)( mc146818a, MC146818A_MIN, To_BCD(time->minute) );
+ (*setReg)( mc146818a, MC146818A_SEC, To_BCD(time->second) );
+
+ /*
+ * Restart the RTC
+ */
+ (*setReg)( mc146818a, MC146818A_STATUSB, MC146818ASB_24HR );
+ return 0;
+}
+
+/*
+ * Driver function table
+ */
+rtc_fns mc146818a_fns = {
+ mc146818a_initialize,
+ mc146818a_get_time,
+ mc146818a_set_time
+};
diff --git a/bsps/shared/dev/rtc/mc146818a_ioreg.c b/bsps/shared/dev/rtc/mc146818a_ioreg.c
new file mode 100644
index 0000000000..4c438a516a
--- /dev/null
+++ b/bsps/shared/dev/rtc/mc146818a_ioreg.c
@@ -0,0 +1,56 @@
+/*
+ * This file contains a typical set of register access routines which may be
+ * used with the MC146818A chip if accesses to the chip are as follows:
+ *
+ * + registers are in I/O space
+ * + registers are accessed as bytes
+ * + registers are only byte-aligned (no address gaps)
+ */
+
+/*
+ * COPYRIGHT (c) 1989-1997.
+ * On-Line Applications Research Corporation (OAR).
+ *
+ * The license and distribution terms for this file may be
+ * found in the file LICENSE in this distribution or at
+ * http://www.rtems.org/license/LICENSE.
+ */
+
+#include <rtems.h>
+#include <bsp.h>
+#include <libchip/rtc.h>
+#include <libchip/mc146818a.h>
+
+/*
+ * At this point, not all CPUs or BSPs have defined in/out port routines.
+ */
+#if defined(__i386__) || defined(__PPC__)
+#if defined(inport_byte)
+uint32_t mc146818a_get_register(
+ uintptr_t ulCtrlPort,
+ uint8_t ucRegNum
+)
+{
+ uint8_t val;
+ uint8_t tmp;
+
+ (void) tmp; /* eliminate warning for set but not used */
+
+ outport_byte( ulCtrlPort, ucRegNum );
+ inport_byte( 0x84, tmp ); /* Hack a delay to give chip time to settle */
+ inport_byte( ulCtrlPort+1, val );
+ inport_byte( 0x84, tmp ); /* Hack a delay to give chip time to settle */
+ return val;
+}
+
+void mc146818a_set_register(
+ uintptr_t ulCtrlPort,
+ uint8_t ucRegNum,
+ uint32_t ucData
+)
+{
+ outport_byte( ulCtrlPort, ucRegNum );
+ outport_byte( ulCtrlPort+1, (uint8_t)ucData );
+}
+#endif
+#endif
diff --git a/bsps/shared/dev/rtc/rtcprobe.c b/bsps/shared/dev/rtc/rtcprobe.c
new file mode 100644
index 0000000000..71472ffd7c
--- /dev/null
+++ b/bsps/shared/dev/rtc/rtcprobe.c
@@ -0,0 +1,21 @@
+/*
+ * This file contains the default Real-Time Clock probe routine.
+ *
+ * COPYRIGHT (c) 1989-1999.
+ * On-Line Applications Research Corporation (OAR).
+ *
+ * The license and distribution terms for this file may be
+ * found in the file LICENSE in this distribution or at
+ * http://www.rtems.org/license/LICENSE.
+ */
+
+#include <rtems.h>
+#include <libchip/rtc.h>
+
+
+bool rtc_probe(
+ int minor
+)
+{
+ return true;
+}
diff --git a/bsps/shared/dev/serial/README b/bsps/shared/dev/serial/README
new file mode 100644
index 0000000000..59bb9e90fa
--- /dev/null
+++ b/bsps/shared/dev/serial/README
@@ -0,0 +1,13 @@
+This is the serial controller portion of the libchip library. This
+directory contains the source code for reusable console driver
+support code. Each individual driver is configured using the
+console_tbl data structure. This structure is defined and explained
+in the console.h file.
+
+The reusable chip drivers do not directly access the serial controller.
+They access the registers on the controller via a set of up to four
+functions which are provided by the BSP. These functins set and get
+general registers and data buffers. Some chips can access the data
+buffers as general registers and thus the driver may not require
+those interface routines.
+
diff --git a/bsps/shared/dev/serial/README.mc68681 b/bsps/shared/dev/serial/README.mc68681
new file mode 100644
index 0000000000..e0966d0e10
--- /dev/null
+++ b/bsps/shared/dev/serial/README.mc68681
@@ -0,0 +1,83 @@
+Configuration Table Use
+=======================
+
+sDeviceName
+
+ The name of this device.
+
+deviceType
+
+ This field must be SERIAL_MC68681.
+
+pDeviceFns
+
+ The device interface control table. This may be:
+ + mc68681_fns for interrupt driven IO
+ + mc68681_fns_polled for polled IO
+
+deviceProbe
+
+ This is the address of the routine which probes to see if the device
+ is present.
+
+pDeviceFlow
+
+ This field is ignored as hardware flow control is not currently supported.
+
+ulMargin
+
+ This is currently unused.
+
+ulHysteresis
+
+ This is currently unused.
+
+pDeviceParams
+
+ This is set to the default settings.
+
+ulCtrlPort1
+
+ This field is the base address of the entire DUART.
+
+ulCtrlPort2
+
+ This field is the base address of the port specific registers.
+
+ulDataPort
+
+ This field is bit mapped as follows:
+ bit 0: baud rate set a or b
+ bit 1-2: BRG selection ("Select Extend bit")
+
+ Note: If both ports on single DUART are not configured for the same
+ baud rate set, then unexpected results will occur.
+
+ Note: On the Exar 88c681, if a standard clock of 3.6864 Mhz is used
+ and the "Select Extend bit" is 0 (disabled), then the default
+ MC68681 baud rate table is selected.
+
+getRegister
+setRegister
+
+ These follow standard conventions.
+
+getData
+setData
+
+ These are unused since the TX and RX data registers can be accessed
+ as regular registers.
+
+ulClock
+
+ This is a pointer to a baud rate mapping table. If set to
+ mc68681_baud_rate_table, then the CSR/ACR/X bit mappings shown
+ in the 68681 and 88681 manuals are used. Otherwise, the board
+ specific baud rate mapping is used.
+
+ NULL is not a valid value.
+
+ulIntVector
+
+ This is the interrupt vector number associated with this chip.
+
diff --git a/bsps/shared/dev/serial/README.ns16550 b/bsps/shared/dev/serial/README.ns16550
new file mode 100644
index 0000000000..a0c31b5506
--- /dev/null
+++ b/bsps/shared/dev/serial/README.ns16550
@@ -0,0 +1,82 @@
+Status
+======
+
+There are no known problems with this driver.
+
+Configuration Table Use
+=======================
+
+sDeviceName
+
+ The name of this device.
+
+deviceType
+
+ This field must be SERIAL_NS16550.
+
+pDeviceFns
+
+ The device interface control table. This may be:
+ + ns16550_fns for interrupt driven IO
+ + ns16550_fns_polled for polled IO
+
+deviceProbe
+
+ This is the address of the routine which probes to see if the device
+ is present.
+
+pDeviceFlow
+
+ This field is ignored as hardware flow control is not currently supported.
+
+ulMargin
+
+ This is currently unused.
+
+ulHysteresis
+
+ This is currently unused.
+
+pDeviceParams
+
+ This is set to the default settings. At this point, it is the default
+ baud rate cast as a (void *).
+
+ulCtrlPort1
+
+ This field is the base address of this port on the UART.
+
+ulCtrlPort2
+
+ This field is unused for the NS16550.
+
+ulDataPort
+
+ This field is the base address of this port on the UART.
+
+getRegister
+setRegister
+
+ These follow standard conventions.
+
+getData
+setData
+
+ These are unused since the TX and RX data registers can be accessed
+ as regular registers.
+
+ulClock
+
+ This is the clock constant which is divided by the desired baud
+ to get the value programmed into the part. The formula for this
+ for 9600 baud is:
+
+ chip_divisor_value = ulClock / 9600.
+
+ NOTE: When ulClock is 0, the correct value for a PC (115,200) is
+ used.
+
+ulIntVector
+
+ This is the interrupt vector number associated with this chip.
+
diff --git a/bsps/shared/dev/serial/README.xr88681 b/bsps/shared/dev/serial/README.xr88681
new file mode 100644
index 0000000000..89b661143f
--- /dev/null
+++ b/bsps/shared/dev/serial/README.xr88681
@@ -0,0 +1,2 @@
+The Exar XR88681 is an enhanced version of the Motorola MC68681 and is
+supported by the mc68681 driver.
diff --git a/bsps/shared/dev/serial/README.z85c30 b/bsps/shared/dev/serial/README.z85c30
new file mode 100644
index 0000000000..f6e0b8cb11
--- /dev/null
+++ b/bsps/shared/dev/serial/README.z85c30
@@ -0,0 +1,74 @@
+Configuration Table Use
+=======================
+
+sDeviceName
+
+ The name of this device.
+
+deviceType
+
+ This field must be SERIAL_Z85C30.
+
+pDeviceFns
+
+ The device interface control table. This may be:
+ + z85c30_fns for interrupt driven IO
+ + z85c30_fns_polled for polled IO
+
+deviceProbe
+
+ This is the address of the routine which probes to see if the device
+ is present.
+
+pDeviceFlow
+
+ This field is set to one of the following values:
+ + NULL for no hardware flow control
+ + z85c30_flow_RTSCTS for RTS/CTS based flow control
+ + z85c30_flow_DTRCTS for DTR/CTS based flow control
+
+ulMargin
+
+ This is currently unused.
+
+ulHysteresis
+
+ This is currently unused.
+
+pDeviceParams
+
+ This is set to the default settings.
+
+ulCtrlPort1
+
+ This field is the address of the control register for this port.
+
+ulCtrlPort2
+
+ This field is the address of the control register for chip.
+
+ulDataPort
+
+ This field is the address of the data register for this port.
+
+getRegister
+setRegister
+
+ These follow standard conventions.
+
+getData
+setData
+
+ These follow standard conventions.
+
+ulClock
+
+ This is the clock speed of the baud rate clock.
+ NULL, then the CSR/ACR/X bit mappings shown in the 68681 and 88681
+ manuals are used. Otherwise, the board specific baud rate mapping
+ is used.
+
+ulIntVector
+
+ This is the interrupt vector number associated with this chip.
+
diff --git a/bsps/shared/dev/serial/STATUS b/bsps/shared/dev/serial/STATUS
new file mode 100644
index 0000000000..243b1a9de5
--- /dev/null
+++ b/bsps/shared/dev/serial/STATUS
@@ -0,0 +1,48 @@
+General
+=======
+
++ Hardware flow control is not currently supported. Some of the chip
+ drivers (in particular the z8530) have support for hardware flow control
+ but this has not been tested in the libchip context. There will need
+ to be a way to totally disabled hardware flow control which is not
+ currently in this.
+
++ "ulClockSpeed" configuration item field to become a pointer to a table
+ of chip specific information. For example, the z8530 should specify
+ clock speed and clock divisor setting.
+
++ A termios structure should be included to specify the initial settings.
+ Right now all drivers default to 9600, 8N1.
+
++ Need to switch to passing pointers rather than a minor number to
+ functions which are strictly internal to each chip driver. This
+ should be a performance win.
+
++ Need a test which prompts you for termios settings and tests them. Until
+ this happens, testing for the variety of settings possible will be limited.
+ This test should be able to test any serial port while prompts come to the
+ console.
+
+MC68681
+=======
+
++ Works interrupt and polled.
+
++ Hardware flow control not included.
+
+NS16650
+=======
+
++ ns16550_set-attributes function is untested.
+
++ Hardware flow control included but is currently disabled in ISR.
+
+Z85C30
+======
+
++ Works polled and interrupt.
+
++ Hardware flow control included but is currently disabled in ISR.
+
++ Needs to support mode where more specific vectors are generated.
+
diff --git a/bsps/shared/dev/serial/mc68681.c b/bsps/shared/dev/serial/mc68681.c
new file mode 100644
index 0000000000..f4ddbd6a50
--- /dev/null
+++ b/bsps/shared/dev/serial/mc68681.c
@@ -0,0 +1,776 @@
+/*
+ * This file contains the termios TTY driver for the Motorola MC68681.
+ *
+ * This part is available from a number of secondary sources.
+ * In particular, we know about the following:
+ *
+ * + Exar 88c681 and 68c681
+ *
+ * COPYRIGHT (c) 1989-1999.
+ * On-Line Applications Research Corporation (OAR).
+ *
+ * The license and distribution terms for this file may be
+ * found in the file LICENSE in this distribution or at
+ * http://www.rtems.org/license/LICENSE.
+ */
+
+#include <rtems.h>
+#include <rtems/libio.h>
+#include <rtems/score/sysstate.h>
+#include <stdlib.h>
+
+#include <libchip/serial.h>
+#include <libchip/mc68681.h>
+#include <libchip/sersupp.h>
+#include "mc68681_p.h"
+
+/*
+ * Flow control is only supported when using interrupts
+ */
+
+const console_fns mc68681_fns =
+{
+ libchip_serial_default_probe, /* deviceProbe */
+ mc68681_open, /* deviceFirstOpen */
+ NULL, /* deviceLastClose */
+ NULL, /* deviceRead */
+ mc68681_write_support_int, /* deviceWrite */
+ mc68681_initialize_interrupts, /* deviceInitialize */
+ mc68681_write_polled, /* deviceWritePolled */
+ mc68681_set_attributes, /* deviceSetAttributes */
+ true /* deviceOutputUsesInterrupts */
+};
+
+const console_fns mc68681_fns_polled =
+{
+ libchip_serial_default_probe, /* deviceProbe */
+ mc68681_open, /* deviceFirstOpen */
+ mc68681_close, /* deviceLastClose */
+ mc68681_inbyte_nonblocking_polled, /* deviceRead */
+ mc68681_write_support_polled, /* deviceWrite */
+ mc68681_init, /* deviceInitialize */
+ mc68681_write_polled, /* deviceWritePolled */
+ mc68681_set_attributes, /* deviceSetAttributes */
+ false, /* deviceOutputUsesInterrupts */
+};
+
+
+#if (CPU_SIMPLE_VECTORED_INTERRUPTS == TRUE)
+ extern void set_vector( rtems_isr_entry, rtems_vector_number, int );
+#endif
+
+/*
+ * Console Device Driver Entry Points
+ */
+
+/*
+ * mc68681_baud_rate
+ *
+ * This routine returns the proper ACR bit and baud rate field values
+ * based on the requested baud rate. The baud rate set to be used
+ * must be configured by the user.
+ */
+
+MC68681_STATIC int mc68681_baud_rate(
+ int minor,
+ int baud,
+ unsigned int *baud_mask_p,
+ unsigned int *acr_bit_p,
+ unsigned int *command
+);
+
+/*
+ * mc68681_set_attributes
+ *
+ * This function sets the DUART channel to reflect the requested termios
+ * port settings.
+ */
+
+MC68681_STATIC int mc68681_set_attributes(
+ int minor,
+ const struct termios *t
+)
+{
+ uint32_t pMC68681_port;
+ uint32_t pMC68681;
+ unsigned int mode1;
+ unsigned int mode2;
+ unsigned int baud_mask;
+ unsigned int acr_bit;
+ unsigned int cmd = 0;
+ setRegister_f setReg;
+ rtems_interrupt_level Irql;
+
+ pMC68681 = Console_Port_Tbl[minor]->ulCtrlPort1;
+ pMC68681_port = Console_Port_Tbl[minor]->ulCtrlPort2;
+ setReg = Console_Port_Tbl[minor]->setRegister;
+
+ /*
+ * Set the baud rate
+ */
+
+ if (mc68681_baud_rate( minor, t->c_cflag, &baud_mask, &acr_bit, &cmd ) == -1)
+ return -1;
+
+ baud_mask |= baud_mask << 4;
+ acr_bit <<= 7;
+
+ /*
+ * Parity
+ */
+
+ mode1 = 0;
+ mode2 = 0;
+
+ if (t->c_cflag & PARENB) {
+ if (t->c_cflag & PARODD)
+ mode1 |= 0x04;
+ /* else
+ mode1 |= 0x04; */
+ } else {
+ mode1 |= 0x10;
+ }
+
+ /*
+ * Character Size
+ */
+
+ if (t->c_cflag & CSIZE) {
+ switch (t->c_cflag & CSIZE) {
+ case CS5: break;
+ case CS6: mode1 |= 0x01; break;
+ case CS7: mode1 |= 0x02; break;
+ case CS8: mode1 |= 0x03; break;
+ }
+ } else {
+ mode1 |= 0x03; /* default to 9600,8,N,1 */
+ }
+
+ /*
+ * Stop Bits
+ */
+
+ if (t->c_cflag & CSTOPB) {
+ mode2 |= 0x0F; /* 2 stop bits */
+ } else {
+ if ((t->c_cflag & CSIZE) == CS5) /* CS5 and 1 stop bits not supported */
+ return -1;
+ mode2 |= 0x07; /* 1 stop bit */
+ }
+
+ /*
+ * Hardware Flow Control
+ */
+
+ if(t->c_cflag & CRTSCTS) {
+ mode1 |= 0x80; /* Enable Rx RTS Control */
+ mode2 |= 0x10; /* Enable CTS Enable Tx */
+ }
+
+
+ rtems_interrupt_disable(Irql);
+ (*setReg)( pMC68681, MC68681_AUX_CTRL_REG, acr_bit );
+ (*setReg)( pMC68681_port, MC68681_CLOCK_SELECT, baud_mask );
+ if ( cmd ) {
+ (*setReg)( pMC68681_port, MC68681_COMMAND, cmd ); /* RX */
+ (*setReg)( pMC68681_port, MC68681_COMMAND, cmd | 0x20 ); /* TX */
+ }
+ (*setReg)( pMC68681_port, MC68681_COMMAND, MC68681_MODE_REG_RESET_MR_PTR );
+ (*setReg)( pMC68681_port, MC68681_MODE, mode1 );
+ (*setReg)( pMC68681_port, MC68681_MODE, mode2 );
+ rtems_interrupt_enable(Irql);
+ return 0;
+}
+
+/*
+ * mc68681_initialize_context
+ *
+ * This function sets the default values of the per port context structure.
+ */
+
+MC68681_STATIC void mc68681_initialize_context(
+ int minor,
+ mc68681_context *pmc68681Context
+)
+{
+ int port;
+ unsigned int pMC68681;
+ unsigned int pMC68681_port;
+
+ pMC68681 = Console_Port_Tbl[minor]->ulCtrlPort1;
+ pMC68681_port = Console_Port_Tbl[minor]->ulCtrlPort2;
+
+ pmc68681Context->mate = -1;
+
+ for (port=0 ; port<Console_Port_Count ; port++ ) {
+ if ( Console_Port_Tbl[port]->ulCtrlPort1 == pMC68681 &&
+ Console_Port_Tbl[port]->ulCtrlPort2 != pMC68681_port ) {
+ pmc68681Context->mate = port;
+ pmc68681Context->imr = 0;
+ break;
+ }
+ }
+
+}
+
+/*
+ * mc68681_init
+ *
+ * This function initializes the DUART to a quiecsent state.
+ */
+
+MC68681_STATIC void mc68681_init(int minor)
+{
+ uint32_t pMC68681_port;
+ mc68681_context *pmc68681Context;
+ setRegister_f setReg;
+
+ pmc68681Context = (mc68681_context *) malloc(sizeof(mc68681_context));
+
+ Console_Port_Data[minor].pDeviceContext = (void *)pmc68681Context;
+
+ mc68681_initialize_context( minor, pmc68681Context );
+
+ pMC68681_port = Console_Port_Tbl[minor]->ulCtrlPort2;
+ setReg = Console_Port_Tbl[minor]->setRegister;
+
+ /*
+ * Reset everything and leave this port disabled.
+ */
+
+ (*setReg)( pMC68681_port, MC68681_COMMAND, MC68681_MODE_REG_RESET_RX );
+ (*setReg)( pMC68681_port, MC68681_COMMAND, MC68681_MODE_REG_RESET_TX );
+ (*setReg)( pMC68681_port, MC68681_COMMAND, MC68681_MODE_REG_RESET_ERROR );
+ (*setReg)( pMC68681_port, MC68681_COMMAND, MC68681_MODE_REG_RESET_BREAK );
+ (*setReg)( pMC68681_port, MC68681_COMMAND, MC68681_MODE_REG_STOP_BREAK );
+ (*setReg)( pMC68681_port, MC68681_COMMAND, MC68681_MODE_REG_DISABLE_TX );
+ (*setReg)( pMC68681_port, MC68681_COMMAND, MC68681_MODE_REG_DISABLE_RX );
+
+
+ (*setReg)( pMC68681_port, MC68681_MODE_REG_1A, 0x00 );
+ (*setReg)( pMC68681_port, MC68681_MODE_REG_2A, 0x02 );
+
+ /*
+ * Disable interrupts on RX and TX for this port
+ */
+
+ mc68681_enable_interrupts( minor, MC68681_IMR_DISABLE_ALL );
+}
+
+/*
+ * mc68681_open
+ *
+ * This function opens a port for communication.
+ *
+ * Default state is 9600 baud, 8 bits, No parity, and 1 stop bit.
+ */
+
+MC68681_STATIC int mc68681_open(
+ int major,
+ int minor,
+ void *arg
+)
+{
+ uint32_t pMC68681;
+ uint32_t pMC68681_port;
+ unsigned int baud;
+ unsigned int acr_bit;
+ unsigned int vector;
+ unsigned int command = 0;
+ rtems_interrupt_level Irql;
+ setRegister_f setReg;
+ int status;
+
+
+ pMC68681 = Console_Port_Tbl[minor]->ulCtrlPort1;
+ pMC68681_port = Console_Port_Tbl[minor]->ulCtrlPort2;
+ setReg = Console_Port_Tbl[minor]->setRegister;
+ vector = Console_Port_Tbl[minor]->ulIntVector;
+
+ /* XXX default baud rate should be from configuration table */
+
+ status = mc68681_baud_rate( minor, B9600, &baud, &acr_bit, &command );
+ if (status < 0) rtems_fatal_error_occurred (RTEMS_NOT_DEFINED);
+
+ /*
+ * Set the DUART channel to a default useable state
+ */
+
+ rtems_interrupt_disable(Irql);
+ (*setReg)( pMC68681, MC68681_AUX_CTRL_REG, acr_bit << 7 );
+ (*setReg)( pMC68681_port, MC68681_CLOCK_SELECT, baud );
+ if ( command ) {
+ (*setReg)( pMC68681_port, MC68681_COMMAND, command ); /* RX */
+ (*setReg)( pMC68681_port, MC68681_COMMAND, command | 0x20 ); /* TX */
+ }
+ (*setReg)( pMC68681_port, MC68681_COMMAND, MC68681_MODE_REG_RESET_MR_PTR );
+ (*setReg)( pMC68681_port, MC68681_MODE, 0x13 );
+ (*setReg)( pMC68681_port, MC68681_MODE, 0x07 );
+ rtems_interrupt_enable(Irql);
+
+ (*setReg)( pMC68681_port, MC68681_COMMAND, MC68681_MODE_REG_ENABLE_TX );
+ (*setReg)( pMC68681_port, MC68681_COMMAND, MC68681_MODE_REG_ENABLE_RX );
+
+ (*setReg)( pMC68681, MC68681_INTERRUPT_VECTOR_REG, vector );
+
+ return RTEMS_SUCCESSFUL;
+}
+
+/*
+ * mc68681_close
+ *
+ * This function shuts down the requested port.
+ */
+
+MC68681_STATIC int mc68681_close(
+ int major,
+ int minor,
+ void *arg
+)
+{
+ uint32_t pMC68681_port;
+ setRegister_f setReg;
+
+ pMC68681_port = Console_Port_Tbl[minor]->ulCtrlPort2;
+ setReg = Console_Port_Tbl[minor]->setRegister;
+
+ /*
+ * Disable interrupts from this channel and then disable it totally.
+ */
+ (*setReg)( pMC68681_port, MC68681_COMMAND, MC68681_MODE_REG_DISABLE_TX );
+ (*setReg)( pMC68681_port, MC68681_COMMAND, MC68681_MODE_REG_DISABLE_RX );
+
+ return(RTEMS_SUCCESSFUL);
+}
+
+/*
+ * mc68681_write_polled
+ *
+ * This routine polls out the requested character.
+ */
+
+MC68681_STATIC void mc68681_write_polled(
+ int minor,
+ char cChar
+)
+{
+ uint32_t pMC68681_port;
+ unsigned char ucLineStatus;
+ int iTimeout;
+ getRegister_f getReg;
+ setRegister_f setReg;
+
+ pMC68681_port = Console_Port_Tbl[minor]->ulCtrlPort2;
+ getReg = Console_Port_Tbl[minor]->getRegister;
+ setReg = Console_Port_Tbl[minor]->setRegister;
+
+ /*
+ * wait for transmitter holding register to be empty
+ */
+ iTimeout = 1000;
+ ucLineStatus = (*getReg)(pMC68681_port, MC68681_STATUS);
+ while ((ucLineStatus & (MC68681_TX_READY|MC68681_TX_EMPTY)) == 0) {
+
+ if ((ucLineStatus & 0xF0))
+ (*setReg)( pMC68681_port, MC68681_COMMAND, MC68681_MODE_REG_RESET_ERROR );
+
+ /*
+ * Yield while we wait
+ */
+
+#if 0
+ if(_System_state_Is_up(_System_state_Get())) {
+ rtems_task_wake_after(RTEMS_YIELD_PROCESSOR);
+ }
+#endif
+ ucLineStatus = (*getReg)(pMC68681_port, MC68681_STATUS);
+ if(!--iTimeout) {
+ break;
+ }
+ }
+
+ /*
+ * transmit character
+ */
+
+ (*setReg)(pMC68681_port, MC68681_TX_BUFFER, cChar);
+}
+
+/*
+ * mc68681_isr
+ *
+ * This is the single interrupt entry point which parcels interrupts
+ * out to the various ports.
+ */
+
+MC68681_STATIC rtems_isr mc68681_isr(
+ rtems_vector_number vector
+)
+{
+ int minor;
+
+ for(minor=0 ; minor<Console_Port_Count ; minor++) {
+ if(Console_Port_Tbl[minor]->ulIntVector == vector &&
+ Console_Port_Tbl[minor]->deviceType == SERIAL_MC68681 ) {
+ mc68681_process(minor);
+ }
+ }
+}
+
+/*
+ * mc68681_initialize_interrupts
+ *
+ * This routine initializes the console's receive and transmit
+ * ring buffers and loads the appropriate vectors to handle the interrupts.
+ */
+
+MC68681_STATIC void mc68681_initialize_interrupts(int minor)
+{
+ mc68681_init(minor);
+
+ Console_Port_Data[minor].bActive = FALSE;
+
+#if (CPU_SIMPLE_VECTORED_INTERRUPTS == TRUE)
+ set_vector(mc68681_isr, Console_Port_Tbl[minor]->ulIntVector, 1);
+#endif
+
+ mc68681_enable_interrupts(minor,MC68681_IMR_ENABLE_ALL_EXCEPT_TX);
+}
+
+/*
+ * mc68681_write_support_int
+ *
+ * Console Termios output entry point when using interrupt driven output.
+ */
+
+MC68681_STATIC ssize_t mc68681_write_support_int(
+ int minor,
+ const char *buf,
+ size_t len
+)
+{
+ uint32_t Irql;
+ uint32_t pMC68681_port;
+ setRegister_f setReg;
+
+ pMC68681_port = Console_Port_Tbl[minor]->ulCtrlPort2;
+ setReg = Console_Port_Tbl[minor]->setRegister;
+
+ /*
+ * We are using interrupt driven output and termios only sends us
+ * one character at a time.
+ */
+
+ if ( !len )
+ return 0;
+
+ /*
+ * Put the character out and enable interrupts if necessary.
+ */
+
+ rtems_interrupt_disable(Irql);
+ if ( Console_Port_Data[minor].bActive == FALSE ) {
+ Console_Port_Data[minor].bActive = TRUE;
+ mc68681_enable_interrupts(minor, MC68681_IMR_ENABLE_ALL);
+ }
+ (*setReg)(pMC68681_port, MC68681_TX_BUFFER, *buf);
+ rtems_interrupt_enable(Irql);
+
+ return 0;
+}
+
+/*
+ * mc68681_write_support_polled
+ *
+ * Console Termios output entry point when using polled output.
+ *
+ */
+
+MC68681_STATIC ssize_t mc68681_write_support_polled(
+ int minor,
+ const char *buf,
+ size_t len
+)
+{
+ int nwrite = 0;
+
+ /*
+ * poll each byte in the string out of the port.
+ */
+ while (nwrite < len) {
+ /*
+ * transmit character
+ */
+ mc68681_write_polled(minor, *buf++);
+ nwrite++;
+ }
+
+ /*
+ * return the number of bytes written.
+ */
+ return nwrite;
+}
+
+/*
+ * mc68681_inbyte_nonblocking_polled
+ *
+ * Console Termios polling input entry point.
+ */
+
+MC68681_STATIC int mc68681_inbyte_nonblocking_polled(
+ int minor
+)
+{
+ uint32_t pMC68681_port;
+ unsigned char ucLineStatus;
+ unsigned char cChar;
+ getRegister_f getReg;
+
+ pMC68681_port = Console_Port_Tbl[minor]->ulCtrlPort2;
+ getReg = Console_Port_Tbl[minor]->getRegister;
+
+ ucLineStatus = (*getReg)(pMC68681_port, MC68681_STATUS);
+ if(ucLineStatus & MC68681_RX_READY) {
+ cChar = (*getReg)(pMC68681_port, MC68681_RX_BUFFER);
+ return (int)cChar;
+ } else {
+ return -1;
+ }
+}
+
+/*
+ * mc68681_baud_rate
+ */
+
+MC68681_STATIC int mc68681_baud_rate(
+ int minor,
+ int baud,
+ unsigned int *baud_mask_p,
+ unsigned int *acr_bit_p,
+ unsigned int *command
+)
+{
+ unsigned int baud_mask;
+ unsigned int acr_bit;
+ int status;
+ int is_extended;
+ int baud_requested;
+ mc68681_baud_table_t *baud_tbl;
+
+ baud_mask = 0;
+ acr_bit = 0;
+ status = 0;
+
+ if (Console_Port_Tbl[minor]->ulDataPort & MC68681_DATA_BAUD_RATE_SET_2)
+ {
+ acr_bit = 1;
+ }
+
+ is_extended = 0;
+
+ switch (Console_Port_Tbl[minor]->ulDataPort & MC68681_XBRG_MASK) {
+ case MC68681_XBRG_IGNORED:
+ *command = 0x00;
+ break;
+ case MC68681_XBRG_ENABLED:
+ *command = 0x80;
+ is_extended = 1;
+ break;
+ case MC68681_XBRG_DISABLED:
+ *command = 0x90;
+ break;
+ }
+
+ baud_requested = baud;
+ if (!baud_requested)
+ baud_requested = B9600; /* default to 9600 baud */
+
+ baud_requested = rtems_termios_baud_to_index( baud_requested );
+ if (baud_requested == -1)
+ return -1;
+
+ baud_tbl = (mc68681_baud_table_t *)
+ ((uintptr_t)Console_Port_Tbl[minor]->ulClock);
+ if (!baud_tbl)
+ rtems_fatal_error_occurred(RTEMS_INVALID_ADDRESS);
+
+ if ( is_extended )
+ baud_mask = (unsigned int)baud_tbl[ acr_bit + 2 ][ baud_requested ];
+ else
+ baud_mask = baud_tbl[ acr_bit ][ baud_requested ];
+
+ if ( baud_mask == MC68681_BAUD_NOT_VALID )
+ status = -1;
+
+ /*
+ * upper nibble is receiver and lower nibble is transmitter
+ */
+
+ *baud_mask_p = (baud_mask << 4) | baud_mask;
+ *acr_bit_p = acr_bit;
+ return status;
+}
+
+/*
+ * mc68681_process
+ *
+ * This routine is the per port console interrupt handler.
+ */
+
+MC68681_STATIC void mc68681_process(
+ int minor
+)
+{
+ uint32_t pMC68681;
+ uint32_t pMC68681_port;
+ volatile uint8_t ucLineStatus;
+ volatile uint8_t ucISRStatus;
+ char cChar;
+ getRegister_f getReg;
+
+ pMC68681 = Console_Port_Tbl[minor]->ulCtrlPort1;
+ pMC68681_port = Console_Port_Tbl[minor]->ulCtrlPort2;
+ getReg = Console_Port_Tbl[minor]->getRegister;
+
+ /* Get ISR at the beginning of the IT routine */
+ ucISRStatus = (*getReg)(pMC68681, MC68681_INTERRUPT_STATUS_REG);
+
+ /* Get good ISR a or b channel */
+ if (pMC68681 != pMC68681_port){
+ ucISRStatus >>= 4;
+ }
+
+ /* See if is usefull to call rtems_termios_dequeue */
+ if(Console_Port_Data[minor].bActive == FALSE) {
+ ucISRStatus = ucISRStatus & ~MC68681_IR_TX_READY;
+ }
+
+ /*
+ * Deal with any received characters
+ */
+ while(true) {
+ ucLineStatus = (*getReg)(pMC68681_port, MC68681_STATUS);
+ if(!(ucLineStatus & MC68681_RX_READY)) {
+ break;
+ }
+ /*
+ * If there is a RX error, then dump all the data.
+ */
+ if ( ucLineStatus & MC68681_RX_ERRORS ) {
+ do {
+ cChar = (*getReg)(pMC68681_port, MC68681_RX_BUFFER);
+ ucLineStatus = (*getReg)(pMC68681_port, MC68681_STATUS);
+ } while ( ucLineStatus & MC68681_RX_READY );
+ continue;
+ }
+ cChar = (*getReg)(pMC68681_port, MC68681_RX_BUFFER);
+ rtems_termios_enqueue_raw_characters(
+ Console_Port_Data[minor].termios_data,
+ &cChar,
+ 1
+ );
+ }
+
+ /*
+ * Deal with the transmitter
+ */
+
+ if (ucISRStatus & MC68681_IR_TX_READY) {
+ if (!rtems_termios_dequeue_characters(
+ Console_Port_Data[minor].termios_data, 1)) {
+ /* If no more char to send, disable TX interrupt */
+ Console_Port_Data[minor].bActive = FALSE;
+ mc68681_enable_interrupts(minor, MC68681_IMR_ENABLE_ALL_EXCEPT_TX);
+ }
+ }
+}
+
+/*
+ * mc68681_build_imr
+ *
+ * This function returns the value for the interrupt mask register for this
+ * DUART. Since this is a shared register, we must look at the other port
+ * on this chip to determine whether or not it is using interrupts.
+ */
+
+MC68681_STATIC unsigned int mc68681_build_imr(
+ int minor,
+ int enable_flag
+)
+{
+ int mate;
+ int is_a;
+ unsigned int mask;
+ unsigned int mate_mask;
+ unsigned int pMC68681;
+ unsigned int pMC68681_port;
+ mc68681_context *pmc68681Context;
+ mc68681_context *mateContext;
+
+ pMC68681 = Console_Port_Tbl[minor]->ulCtrlPort1;
+ pMC68681_port = Console_Port_Tbl[minor]->ulCtrlPort2;
+ pmc68681Context = (mc68681_context *) Console_Port_Data[minor].pDeviceContext;
+ mate = pmc68681Context->mate;
+
+ mask = 0;
+ mate_mask = 0;
+
+ is_a = (pMC68681 == pMC68681_port);
+
+ /*
+ * If there is a mate for this port, get its IMR mask.
+ */
+
+ if ( mate != -1 ) {
+ mateContext = Console_Port_Data[mate].pDeviceContext;
+
+ if (mateContext)
+ mate_mask = mateContext->imr;
+ }
+
+ /*
+ * Calculate this port's IMR mask and save it in the context area.
+ */
+
+ if ( Console_Port_Tbl[minor]->pDeviceFns->deviceOutputUsesInterrupts )
+ mask = enable_flag;
+
+ pmc68681Context->imr = mask;
+
+ /*
+ * Now return the full IMR value
+ */
+
+ if (is_a)
+ return (mate_mask << 4) | mask;
+
+ return (mask << 4) | mate_mask;
+}
+
+/*
+ * mc68681_enable_interrupts
+ *
+ * This function enables specific interrupt sources on the DUART.
+ */
+
+MC68681_STATIC void mc68681_enable_interrupts(
+ int minor,
+ int imr_mask
+)
+{
+ uint32_t pMC68681;
+ setRegister_f setReg;
+
+ pMC68681 = Console_Port_Tbl[minor]->ulCtrlPort1;
+ setReg = Console_Port_Tbl[minor]->setRegister;
+
+ /*
+ * Enable interrupts on RX and TX -- not break
+ */
+
+ (*setReg)(
+ pMC68681,
+ MC68681_INTERRUPT_MASK_REG,
+ mc68681_build_imr(minor, imr_mask)
+ );
+}
diff --git a/bsps/shared/dev/serial/mc68681_baud.c b/bsps/shared/dev/serial/mc68681_baud.c
new file mode 100644
index 0000000000..0f8e87c2c2
--- /dev/null
+++ b/bsps/shared/dev/serial/mc68681_baud.c
@@ -0,0 +1,124 @@
+/*
+ * MC68681 Default Baud Rate Table
+ */
+
+#include <rtems.h>
+#include <libchip/serial.h>
+#include <libchip/mc68681.h>
+
+/* major index of 0 : ACR[7] = 0, X = 0 -- 68c681 only has these */
+/* major index of 1 : ACR[7] = 1, X = 0 -- 68c681 only has these */
+/* major index of 2 : ACR[7] = 0, X = 1 */
+/* major index of 3 : ACR[7] = 1, X = 1 */
+
+/* mc68681_baud_table_t mc68681_baud_rate_table[4] = { */
+mc68681_baud_t mc68681_baud_rate_table[4][RTEMS_TERMIOS_NUMBER_BAUD_RATES] = {
+ { /* ACR[7] = 0, X = 0 */
+ MC68681_BAUD_NOT_VALID, /* B0 */
+ 0x00, /* B50 */
+ MC68681_BAUD_NOT_VALID, /* B75 */
+ 0x01, /* B110 */
+ 0x02, /* B134 */
+ MC68681_BAUD_NOT_VALID, /* B150 */
+ 0x03, /* B200 */
+ 0x04, /* B300 */
+ 0x05, /* B600 */
+ 0x06, /* B1200 */
+ MC68681_BAUD_NOT_VALID, /* B1800 */
+ 0x08, /* B2400 */
+ 0x09, /* B4800 */
+ 0x0B, /* B9600 */
+ MC68681_BAUD_NOT_VALID, /* B19200 */
+ 0x0C, /* B38400 */
+ MC68681_BAUD_NOT_VALID, /* B7200 */
+ MC68681_BAUD_NOT_VALID, /* B14400 */
+ MC68681_BAUD_NOT_VALID, /* B28800 */
+ MC68681_BAUD_NOT_VALID, /* B57600 */
+ MC68681_BAUD_NOT_VALID, /* B76800 */
+ MC68681_BAUD_NOT_VALID, /* B115200 */
+ MC68681_BAUD_NOT_VALID, /* B230400 */
+ MC68681_BAUD_NOT_VALID, /* B460800 */
+ MC68681_BAUD_NOT_VALID /* B921600 */
+ },
+ { /* ACR[7] = 1, X = 0 */
+ MC68681_BAUD_NOT_VALID, /* B0 */
+ MC68681_BAUD_NOT_VALID, /* B50 */
+ 0x00, /* B75 */
+ 0x01, /* B110 */
+ 0x02, /* B134 */
+ 0x03, /* B150 */
+ MC68681_BAUD_NOT_VALID, /* B200 */
+ 0x04, /* B300 */
+ 0x05, /* B600 */
+ 0x06, /* B1200 */
+ 0x0A, /* B1800 */
+ 0x08, /* B2400 */
+ 0x09, /* B4800 */
+ 0x0B, /* B9600 */
+ 0x0C, /* B19200 */
+ MC68681_BAUD_NOT_VALID, /* B38400 */
+ MC68681_BAUD_NOT_VALID, /* B7200 */
+ MC68681_BAUD_NOT_VALID, /* B14400 */
+ MC68681_BAUD_NOT_VALID, /* B28800 */
+ MC68681_BAUD_NOT_VALID, /* B57600 */
+ MC68681_BAUD_NOT_VALID, /* B76800 */
+ MC68681_BAUD_NOT_VALID, /* B115200 */
+ MC68681_BAUD_NOT_VALID, /* B230400 */
+ MC68681_BAUD_NOT_VALID, /* B460800 */
+ MC68681_BAUD_NOT_VALID /* B921600 */
+ },
+ { /* ACR[7] = 0, X = 1 */
+ MC68681_BAUD_NOT_VALID, /* B0 */
+ MC68681_BAUD_NOT_VALID, /* B50 */
+ 0x00, /* B75 */
+ 0x01, /* B110 */
+ 0x02, /* B134 */
+ 0x03, /* B150 */
+ MC68681_BAUD_NOT_VALID, /* B200 */
+ MC68681_BAUD_NOT_VALID, /* B300 */
+ MC68681_BAUD_NOT_VALID, /* B600 */
+ MC68681_BAUD_NOT_VALID, /* B1200 */
+ 0x0A, /* B1800 */
+ MC68681_BAUD_NOT_VALID, /* B2400 */
+ 0x08, /* B4800 */
+ 0x0B, /* B9600 */
+ 0x0C, /* B19200 */
+ MC68681_BAUD_NOT_VALID, /* B38400 */
+ MC68681_BAUD_NOT_VALID, /* B7200 */
+ MC68681_BAUD_NOT_VALID, /* B14400 */
+ MC68681_BAUD_NOT_VALID, /* B28800 */
+ 0x07, /* B57600 */
+ MC68681_BAUD_NOT_VALID, /* B76800 */
+ 0x08, /* B115200 */
+ MC68681_BAUD_NOT_VALID, /* B230400 */
+ MC68681_BAUD_NOT_VALID, /* B460800 */
+ MC68681_BAUD_NOT_VALID /* B921600 */
+ },
+ { /* ACR[7] = 1, X = 1 */
+ MC68681_BAUD_NOT_VALID, /* B0 */
+ 0x00, /* B50 */
+ MC68681_BAUD_NOT_VALID, /* B75 */
+ 0x01, /* B110 */
+ 0x02, /* B134 */
+ MC68681_BAUD_NOT_VALID, /* B150 */
+ 0x03, /* B200 */
+ MC68681_BAUD_NOT_VALID, /* B300 */
+ MC68681_BAUD_NOT_VALID, /* B600 */
+ MC68681_BAUD_NOT_VALID, /* B1200 */
+ MC68681_BAUD_NOT_VALID, /* B1800 */
+ MC68681_BAUD_NOT_VALID, /* B2400 */
+ 0x09, /* B4800 */
+ 0x0B, /* B9600 */
+ MC68681_BAUD_NOT_VALID, /* B19200 */
+ 0x0C, /* B38400 */
+ MC68681_BAUD_NOT_VALID, /* B7200 */
+ MC68681_BAUD_NOT_VALID, /* B14400 */
+ MC68681_BAUD_NOT_VALID, /* B28800 */
+ 0x07, /* B57600 */
+ MC68681_BAUD_NOT_VALID, /* B76800 */
+ 0x08, /* B115200 */
+ MC68681_BAUD_NOT_VALID, /* B230400 */
+ MC68681_BAUD_NOT_VALID, /* B460800 */
+ MC68681_BAUD_NOT_VALID /* B921600 */
+ },
+};
diff --git a/bsps/shared/dev/serial/mc68681_p.h b/bsps/shared/dev/serial/mc68681_p.h
new file mode 100644
index 0000000000..4623276303
--- /dev/null
+++ b/bsps/shared/dev/serial/mc68681_p.h
@@ -0,0 +1,323 @@
+/*
+ *
+ * COPYRIGHT (c) 1989-1999.
+ * On-Line Applications Research Corporation (OAR).
+ *
+ * The license and distribution terms for this file may be
+ * found in the file LICENSE in this distribution or at
+ * http://www.rtems.org/license/LICENSE.
+ */
+
+#ifndef _MC68681_P_H_
+#define _MC68681_P_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Define MC68681_STATIC to nothing while debugging so the entry points
+ * will show up in the symbol table.
+ */
+
+#define MC68681_STATIC
+
+/* #define MC68681_STATIC static */
+
+/*
+ * mc68681 register offsets Read/Write Addresses
+ */
+
+#define MC68681_MODE_REG_1A 0 /* MR1A-MR Prior to Read */
+#define MC68681_MODE_REG_2A 0 /* MR2A-MR After Read */
+
+#define MC68681_COUNT_MODE_CURRENT_MSB 6 /* CTU */
+#define MC68681_COUNTER_TIMER_UPPER_REG 6 /* CTU */
+#define MC68681_COUNT_MODE_CURRENT_LSB 7 /* CTL */
+#define MC68681_COUNTER_TIMER_LOWER_REG 7 /* CTL */
+#define MC68681_INTERRUPT_VECTOR_REG 12 /* IVR */
+
+#define MC68681_MODE_REG_1B 8 /* MR1B-MR Prior to Read */
+#define MC68681_MODE_REG_2B 8 /* MR2BA-MR After Read */
+
+/*
+ * mc68681 register offsets Read Only Addresses
+ */
+
+#define MC68681_STATUS_REG_A 1 /* SRA */
+#define MC68681_MASK_ISR_REG 2 /* MISR */
+#define MC68681_RECEIVE_BUFFER_A 3 /* RHRA */
+#define MC68681_INPUT_PORT_CHANGE_REG 4 /* IPCR */
+#define MC68681_INTERRUPT_STATUS_REG 5 /* ISR */
+#define MC68681_STATUS_REG_B 9 /* SRB */
+#define MC68681_RECEIVE_BUFFER_B 11 /* RHRB */
+#define MC68681_INPUT_PORT 13 /* IP */
+#define MC68681_START_COUNT_CMD 14 /* SCC */
+#define MC68681_STOP_COUNT_CMD 15 /* STC */
+
+/*
+ * mc68681 register offsets Write Only Addresses
+ */
+
+#define MC68681_CLOCK_SELECT_REG_A 1 /* CSRA */
+#define MC68681_COMMAND_REG_A 2 /* CRA */
+#define MC68681_TRANSMIT_BUFFER_A 3 /* THRA */
+#define MC68681_AUX_CTRL_REG 4 /* ACR */
+#define MC68681_INTERRUPT_MASK_REG 5 /* IMR */
+#define MC68681_CLOCK_SELECT_REG_B 9 /* CSRB */
+#define MC68681_COMMAND_REG_B 10 /* CRB */
+#define MC68681_TRANSMIT_BUFFER_B 11 /* THRB */
+#define MC68681_OUTPUT_PORT_CONFIG_REG 13 /* OPCR */
+#define MC68681_OUTPUT_PORT_SET_REG 14 /* SOPBC */
+#define MC68681_OUTPUT_PORT_RESET_BITS 15 /* COPBC */
+
+/*
+ * DUART Command Register Definitions:
+ *
+ * MC68681_COMMAND_REG_A,MC68681_COMMAND_REG_B
+ */
+
+#define MC68681_MODE_REG_ENABLE_RX 0x01
+#define MC68681_MODE_REG_DISABLE_RX 0x02
+#define MC68681_MODE_REG_ENABLE_TX 0x04
+#define MC68681_MODE_REG_DISABLE_TX 0x08
+#define MC68681_MODE_REG_RESET_MR_PTR 0x10
+#define MC68681_MODE_REG_RESET_RX 0x20
+#define MC68681_MODE_REG_RESET_TX 0x30
+#define MC68681_MODE_REG_RESET_ERROR 0x40
+#define MC68681_MODE_REG_RESET_BREAK 0x50
+#define MC68681_MODE_REG_START_BREAK 0x60
+#define MC68681_MODE_REG_STOP_BREAK 0x70
+#define MC68681_MODE_REG_SET_RX_BRG 0x80
+#define MC68681_MODE_REG_CLEAR_RX_BRG 0x90
+#define MC68681_MODE_REG_SET_TX_BRG 0xa0
+#define MC68681_MODE_REG_CLEAR_TX_BRG 0xb0
+#define MC68681_MODE_REG_SET_STANDBY 0xc0
+#define MC68681_MODE_REG_SET_ACTIVE 0xd0
+
+/*
+ * Mode Register Definitions
+ *
+ * MC68681_MODE_REG_1A
+ * MC68681_MODE_REG_1B
+ */
+
+#define MC68681_5BIT_CHARS 0x00
+#define MC68681_6BIT_CHARS 0x01
+#define MC68681_7BIT_CHARS 0x02
+#define MC68681_8BIT_CHARS 0x03
+
+#define MC68681_ODD_PARITY 0x00
+#define MC68681_EVEN_PARITY 0x04
+
+#define MC68681_WITH_PARITY 0x00
+#define MC68681_FORCE_PARITY 0x08
+#define MC68681_NO_PARITY 0x10
+#define MC68681_MULTI_DROP 0x18
+
+#define MC68681_ERR_MODE_CHAR 0x00
+#define MC68681_ERR_MODE_BLOCK 0x20
+
+#define MC68681_RX_INTR_RX_READY 0x00
+#define MC68681_RX_INTR_FFULL 0x40
+
+#define MC68681_NO_RX_RTS_CTL 0x00
+#define MC68681_RX_RTS_CTRL 0x80
+
+/*
+ * Mode Register Definitions
+ *
+ * MC68681_MODE_REG_2A
+ * MC68681_MODE_REG_2B
+ */
+
+#define MC68681_STOP_BIT_LENGTH__563 0x00
+#define MC68681_STOP_BIT_LENGTH__625 0x01
+#define MC68681_STOP_BIT_LENGTH__688 0x02
+#define MC68681_STOP_BIT_LENGTH__75 0x03
+#define MC68681_STOP_BIT_LENGTH__813 0x04
+#define MC68681_STOP_BIT_LENGTH__875 0x05
+#define MC68681_STOP_BIT_LENGTH__938 0x06
+#define MC68681_STOP_BIT_LENGTH_1 0x07
+#define MC68681_STOP_BIT_LENGTH_1_563 0x08
+#define MC68681_STOP_BIT_LENGTH_1_625 0x09
+#define MC68681_STOP_BIT_LENGTH_1_688 0x0a
+#define MC68681_STOP_BIT_LENGTH_1_75 0x0b
+#define MC68681_STOP_BIT_LENGTH_1_813 0x0c
+#define MC68681_STOP_BIT_LENGTH_1_875 0x0d
+#define MC68681_STOP_BIT_LENGTH_1_938 0x0e
+#define MC68681_STOP_BIT_LENGTH_2 0x0f
+
+#define MC68681_CTS_ENABLE_TX 0x10
+#define MC68681_TX_RTS_CTRL 0x20
+
+#define MC68681_CHANNEL_MODE_NORMAL 0x00
+#define MC68681_CHANNEL_MODE_ECHO 0x40
+#define MC68681_CHANNEL_MODE_LOCAL_LOOP 0x80
+#define MC68681_CHANNEL_MODE_REMOTE_LOOP 0xc0
+
+/*
+ * Status Register Definitions
+ *
+ * MC68681_STATUS_REG_A, MC68681_STATUS_REG_B
+ */
+
+#define MC68681_RX_READY 0x01
+#define MC68681_FFULL 0x02
+#define MC68681_TX_READY 0x04
+#define MC68681_TX_EMPTY 0x08
+#define MC68681_OVERRUN_ERROR 0x10
+#define MC68681_PARITY_ERROR 0x20
+#define MC68681_FRAMING_ERROR 0x40
+#define MC68681_RECEIVED_BREAK 0x80
+
+#define MC68681_RX_ERRORS \
+ (MC68681_OVERRUN_ERROR|MC68681_PARITY_ERROR| \
+ MC68681_FRAMING_ERROR|MC68681_RECEIVED_BREAK)
+
+/*
+ * Interupt Status Register Definitions.
+ *
+ * MC68681_INTERRUPT_STATUS_REG
+ */
+
+/*
+ * Interupt Mask Register Definitions
+ *
+ * MC68681_INTERRUPT_MASK_REG
+ */
+
+/* These are passed to mc68681_build_imr */
+#define MC68681_IR_TX_READY 0x01
+#define MC68681_IR_RX_READY 0x02
+#define MC68681_IR_BREAK 0x04
+#define MC68681_IMR_ENABLE_ALL 0x07
+#define MC68681_IMR_DISABLE_ALL 0x00
+#define MC68681_IMR_ENABLE_ALL_EXCEPT_TX 0x06
+
+#define MC68681_IR_TX_READY_A 0x01
+#define MC68681_IR_RX_READY_A 0x02
+#define MC68681_IR_BREAK_A 0x04
+#define MC68681_IR_COUNTER_READY 0x08
+#define MC68681_IR_TX_READY_B 0x10
+#define MC68681_IR_RX_READY_B 0x20
+#define MC68681_IR_BREAK_B 0x40
+#define MC68681_IR_INPUT_PORT_CHANGE 0x80
+
+/*
+ * Status Register Definitions.
+ *
+ * MC68681_STATUS_REG_A,MC68681_STATUS_REG_B
+ */
+
+#define MC68681_STATUS_RXRDY 0x01
+#define MC68681_STATUS_FFULL 0x02
+#define MC68681_STATUS_TXRDY 0x04
+#define MC68681_STATUS_TXEMT 0x08
+#define MC68681_STATUS_OVERRUN_ERROR 0x10
+#define MC68681_STATUS_PARITY_ERROR 0x20
+#define MC68681_STATUS_FRAMING_ERROR 0x40
+#define MC68681_STATUS_RECEIVED_BREAK 0x80
+
+/*
+ * Definitions for the Interrupt Vector Register:
+ *
+ * MC68681_INTERRUPT_VECTOR_REG
+ */
+
+#define MC68681_INTERRUPT_VECTOR_INIT 0x0f
+
+/*
+ * Definitions for the Auxiliary Control Register
+ *
+ * MC68681_AUX_CTRL_REG
+ */
+
+#define MC68681_AUX_BRG_SET1 0x00
+#define MC68681_AUX_BRG_SET2 0x80
+
+/*
+ * Per chip context control
+ */
+
+typedef struct _mc68681_context
+{
+ int mate;
+ unsigned char imr;
+} mc68681_context;
+
+/*
+ * Driver functions
+ */
+MC68681_STATIC void mc68681_initialize_context(
+ int minor,
+ mc68681_context *pmc68681Context
+);
+
+MC68681_STATIC bool mc68681_probe(int minor);
+
+MC68681_STATIC int mc68681_set_attributes(
+ int minor,
+ const struct termios *t
+);
+
+MC68681_STATIC void mc68681_init(int minor);
+
+MC68681_STATIC int mc68681_open(
+ int major,
+ int minor,
+ void * arg
+);
+
+MC68681_STATIC int mc68681_close(
+ int major,
+ int minor,
+ void * arg
+);
+
+MC68681_STATIC void mc68681_write_polled(
+ int minor,
+ char cChar
+);
+
+MC68681_STATIC void mc68681_initialize_interrupts(int minor);
+
+MC68681_STATIC ssize_t mc68681_write_support_int(
+ int minor,
+ const char *buf,
+ size_t len
+);
+
+MC68681_STATIC ssize_t mc68681_write_support_polled(
+ int minor,
+ const char *buf,
+ size_t len
+ );
+
+MC68681_STATIC int mc68681_inbyte_nonblocking_polled(
+ int minor
+);
+
+MC68681_STATIC unsigned int mc68681_build_imr(
+ int minor,
+ int enable_flag
+);
+
+MC68681_STATIC void mc68681_process(
+ int minor
+);
+
+MC68681_STATIC void mc68681_enable_interrupts(
+ int minor,
+ int imr_mask
+);
+
+MC68681_STATIC rtems_isr mc68681_isr(
+ rtems_vector_number vector
+);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _MC68681_P_H_ */
diff --git a/bsps/shared/dev/serial/mc68681_reg.c b/bsps/shared/dev/serial/mc68681_reg.c
new file mode 100644
index 0000000000..fb92b8fcd3
--- /dev/null
+++ b/bsps/shared/dev/serial/mc68681_reg.c
@@ -0,0 +1,61 @@
+/*
+ * This file contains a typical set of register access routines which may be
+ * used with the mc68681 chip if accesses to the chip are as follows:
+ *
+ * + registers are accessed as bytes
+ * + registers are only byte-aligned (no address gaps)
+ *
+ * COPYRIGHT (c) 1989-1997.
+ * On-Line Applications Research Corporation (OAR).
+ *
+ * The license and distribution terms for this file may be
+ * found in the file LICENSE in this distribution or at
+ * http://www.rtems.org/license/LICENSE.
+ */
+
+#include <rtems.h>
+
+#include <libchip/serial.h>
+#include <libchip/mc68681.h>
+
+#ifndef _MC68681_MULTIPLIER
+#define _MC68681_MULTIPLIER 1
+#define _MC68681_NAME(_X) _X
+#define _MC68681_TYPE uint8_t
+#endif
+
+#define CALCULATE_REGISTER_ADDRESS( _base, _reg ) \
+ (_MC68681_TYPE *)((_base) + ((_reg) * _MC68681_MULTIPLIER ))
+
+/*
+ * MC68681 Get Register Routine
+ */
+
+uint8_t _MC68681_NAME(mc68681_get_register)(
+ uintptr_t ulCtrlPort,
+ uint8_t ucRegNum
+)
+{
+ _MC68681_TYPE *port;
+
+ port = CALCULATE_REGISTER_ADDRESS( ulCtrlPort, ucRegNum );
+
+ return *port;
+}
+
+/*
+ * MC68681 Set Register Routine
+ */
+
+void _MC68681_NAME(mc68681_set_register)(
+ uintptr_t ulCtrlPort,
+ uint8_t ucRegNum,
+ uint8_t ucData
+)
+{
+ _MC68681_TYPE *port;
+
+ port = CALCULATE_REGISTER_ADDRESS( ulCtrlPort, ucRegNum );
+
+ *port = ucData;
+}
diff --git a/bsps/shared/dev/serial/mc68681_reg2.c b/bsps/shared/dev/serial/mc68681_reg2.c
new file mode 100644
index 0000000000..0e0121eb40
--- /dev/null
+++ b/bsps/shared/dev/serial/mc68681_reg2.c
@@ -0,0 +1,20 @@
+/*
+ * This file contains a typical set of register access routines which may be
+ * used with the mc68681 chip if accesses to the chip are as follows:
+ *
+ * + registers are accessed as bytes
+ * + registers are on 16-bit boundaries
+ *
+ * COPYRIGHT (c) 1989-1997.
+ * On-Line Applications Research Corporation (OAR).
+ *
+ * The license and distribution terms for this file may be
+ * found in the file LICENSE in this distribution or at
+ * http://www.rtems.org/license/LICENSE.
+ */
+
+#define _MC68681_MULTIPLIER 2
+#define _MC68681_NAME(_X) _X##_2
+#define _MC68681_TYPE uint8_t
+
+#include "mc68681_reg.c"
diff --git a/bsps/shared/dev/serial/mc68681_reg4.c b/bsps/shared/dev/serial/mc68681_reg4.c
new file mode 100644
index 0000000000..e9dd94ce4b
--- /dev/null
+++ b/bsps/shared/dev/serial/mc68681_reg4.c
@@ -0,0 +1,20 @@
+/*
+ * This file contains a typical set of register access routines which may be
+ * used with the mc68681 chip if accesses to the chip are as follows:
+ *
+ * + registers are accessed as bytes
+ * + registers are on 32-bit boundaries
+ *
+ * COPYRIGHT (c) 1989-1997.
+ * On-Line Applications Research Corporation (OAR).
+ *
+ * The license and distribution terms for this file may be
+ * found in the file LICENSE in this distribution or at
+ * http://www.rtems.org/license/LICENSE.
+ */
+
+#define _MC68681_MULTIPLIER 4
+#define _MC68681_NAME(_X) _X##_4
+#define _MC68681_TYPE uint8_t
+
+#include "mc68681_reg.c"
diff --git a/bsps/shared/dev/serial/mc68681_reg8.c b/bsps/shared/dev/serial/mc68681_reg8.c
new file mode 100644
index 0000000000..402c2ffe1b
--- /dev/null
+++ b/bsps/shared/dev/serial/mc68681_reg8.c
@@ -0,0 +1,20 @@
+/*
+ * This file contains a typical set of register access routines which may be
+ * used with the mc68681 chip if accesses to the chip are as follows:
+ *
+ * + registers are accessed as bytes
+ * + registers are on 64-bit boundaries
+ *
+ * COPYRIGHT (c) 1989-1997.
+ * On-Line Applications Research Corporation (OAR).
+ *
+ * The license and distribution terms for this file may be
+ * found in the file LICENSE in this distribution or at
+ * http://www.rtems.org/license/LICENSE.
+ */
+
+#define _MC68681_MULTIPLIER 8
+#define _MC68681_NAME(_X) _X##_8
+#define _MC68681_TYPE uint8_t
+
+#include "mc68681_reg.c"
diff --git a/bsps/shared/dev/serial/ns16550-context.c b/bsps/shared/dev/serial/ns16550-context.c
new file mode 100644
index 0000000000..b42be96a26
--- /dev/null
+++ b/bsps/shared/dev/serial/ns16550-context.c
@@ -0,0 +1,814 @@
+/**
+ * @file
+ *
+ * This file contains the TTY driver for the National Semiconductor NS16550.
+ *
+ * This part is widely cloned and second sourced. It is found in a number
+ * of "Super IO" controllers.
+ *
+ * This driver uses the termios pseudo driver.
+ */
+
+/*
+ * COPYRIGHT (c) 1998 by Radstone Technology
+ *
+ * THIS FILE IS PROVIDED TO YOU, THE USER, "AS IS", WITHOUT WARRANTY OF ANY
+ * KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTY OF FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK
+ * AS TO THE QUALITY AND PERFORMANCE OF ALL CODE IN THIS FILE IS WITH YOU.
+ *
+ * You are hereby granted permission to use, copy, modify, and distribute
+ * this file, provided that this notice, plus the above copyright notice
+ * and disclaimer, appears in all copies. Radstone Technology will provide
+ * no support for this code.
+ *
+ * COPYRIGHT (c) 1989-2012.
+ * On-Line Applications Research Corporation (OAR).
+ *
+ * The license and distribution terms for this file may be
+ * found in the file LICENSE in this distribution or at
+ * http://www.rtems.org/license/LICENSE.
+ */
+
+#include <stdlib.h>
+
+#include <rtems/bspIo.h>
+
+#include <bsp.h>
+
+#include <libchip/ns16550.h>
+#include <libchip/ns16550_p.h>
+
+#if defined(BSP_FEATURE_IRQ_EXTENSION)
+ #include <bsp/irq.h>
+#elif defined(BSP_FEATURE_IRQ_LEGACY)
+ #include <bsp/irq.h>
+#elif defined(__PPC__) || defined(__i386__)
+ #include <bsp/irq.h>
+ #define BSP_FEATURE_IRQ_LEGACY
+ #ifdef BSP_SHARED_HANDLER_SUPPORT
+ #define BSP_FEATURE_IRQ_LEGACY_SHARED_HANDLER_SUPPORT
+ #endif
+#endif
+
+static uint32_t NS16550_GetBaudDivisor(ns16550_context *ctx, uint32_t baud)
+{
+ uint32_t clock = ctx->clock;
+ uint32_t baudDivisor = (clock != 0 ? clock : 115200) / (baud * 16);
+
+ if (ctx->has_fractional_divider_register) {
+ uint32_t fractionalDivider = 0x10;
+ uint32_t err = baud;
+ uint32_t mulVal;
+ uint32_t divAddVal;
+
+ clock /= 16 * baudDivisor;
+ for (mulVal = 1; mulVal < 16; ++mulVal) {
+ for (divAddVal = 0; divAddVal < mulVal; ++divAddVal) {
+ uint32_t actual = (mulVal * clock) / (mulVal + divAddVal);
+ uint32_t newErr = actual > baud ? actual - baud : baud - actual;
+
+ if (newErr < err) {
+ err = newErr;
+ fractionalDivider = (mulVal << 4) | divAddVal;
+ }
+ }
+ }
+
+ (*ctx->set_reg)(
+ ctx->port,
+ NS16550_FRACTIONAL_DIVIDER,
+ fractionalDivider
+ );
+ }
+
+ return baudDivisor;
+}
+
+/*
+ * ns16550_enable_interrupts
+ *
+ * This routine initializes the port to have the specified interrupts masked.
+ */
+static void ns16550_enable_interrupts(
+ ns16550_context *ctx,
+ int mask
+)
+{
+ (*ctx->set_reg)(ctx->port, NS16550_INTERRUPT_ENABLE, mask);
+}
+
+static void ns16550_clear_and_set_interrupts(
+ ns16550_context *ctx,
+ uint8_t clear,
+ uint8_t set
+)
+{
+ rtems_interrupt_lock_context lock_context;
+ ns16550_get_reg get_reg = ctx->get_reg;
+ ns16550_set_reg set_reg = ctx->set_reg;
+ uintptr_t port = ctx->port;
+ uint8_t val;
+
+ rtems_termios_device_lock_acquire(&ctx->base, &lock_context);
+ val = (*get_reg)(port, NS16550_INTERRUPT_ENABLE);
+ val &= ~clear;
+ val |= set;
+ (*set_reg)(port, NS16550_INTERRUPT_ENABLE, val);
+ rtems_termios_device_lock_release(&ctx->base, &lock_context);
+}
+
+/*
+ * ns16550_probe
+ */
+
+bool ns16550_probe(rtems_termios_device_context *base)
+{
+ ns16550_context *ctx = (ns16550_context *) base;
+ uintptr_t pNS16550;
+ uint8_t ucDataByte;
+ uint32_t ulBaudDivisor;
+ ns16550_set_reg setReg;
+ ns16550_get_reg getReg;
+
+ ctx->modem_control = SP_MODEM_IRQ;
+
+ pNS16550 = ctx->port;
+ setReg = ctx->set_reg;
+ getReg = ctx->get_reg;
+
+ /* Clear the divisor latch, clear all interrupt enables,
+ * and reset and
+ * disable the FIFO's.
+ */
+
+ (*setReg)(pNS16550, NS16550_LINE_CONTROL, 0x0);
+ ns16550_enable_interrupts(ctx, NS16550_DISABLE_ALL_INTR );
+
+ /* Set the divisor latch and set the baud rate. */
+
+ ulBaudDivisor = NS16550_GetBaudDivisor(ctx, ctx->initial_baud);
+ ctx->baud_divisor = ulBaudDivisor;
+ ucDataByte = SP_LINE_DLAB;
+ (*setReg)(pNS16550, NS16550_LINE_CONTROL, ucDataByte);
+
+ /* XXX */
+ (*setReg)(pNS16550,NS16550_TRANSMIT_BUFFER,(uint8_t)(ulBaudDivisor & 0xffU));
+ (*setReg)(
+ pNS16550,NS16550_INTERRUPT_ENABLE,
+ (uint8_t)(( ulBaudDivisor >> 8 ) & 0xffU )
+ );
+
+ /* Clear the divisor latch and set the character size to eight bits */
+ /* with one stop bit and no parity checking. */
+ ucDataByte = EIGHT_BITS;
+ ctx->line_control = ucDataByte;
+ (*setReg)(pNS16550, NS16550_LINE_CONTROL, ucDataByte);
+
+ /* Enable and reset transmit and receive FIFOs. TJA */
+ ucDataByte = SP_FIFO_ENABLE;
+ (*setReg)(pNS16550, NS16550_FIFO_CONTROL, ucDataByte);
+
+ ucDataByte = SP_FIFO_ENABLE | SP_FIFO_RXRST | SP_FIFO_TXRST;
+ (*setReg)(pNS16550, NS16550_FIFO_CONTROL, ucDataByte);
+
+ ns16550_enable_interrupts(ctx, NS16550_DISABLE_ALL_INTR);
+
+ /* Set data terminal ready. */
+ /* And open interrupt tristate line */
+ (*setReg)(pNS16550, NS16550_MODEM_CONTROL,ctx->modem_control);
+
+ (*getReg)(pNS16550, NS16550_LINE_STATUS );
+ (*getReg)(pNS16550, NS16550_RECEIVE_BUFFER );
+
+ return true;
+}
+
+static size_t ns16550_write_to_fifo(
+ const ns16550_context *ctx,
+ const char *buf,
+ size_t len
+)
+{
+ uintptr_t port = ctx->port;
+ ns16550_set_reg set = ctx->set_reg;
+ size_t out = len > SP_FIFO_SIZE ? SP_FIFO_SIZE : len;
+ size_t i;
+
+ for (i = 0; i < out; ++i) {
+ (*set)(port, NS16550_TRANSMIT_BUFFER, buf[i]);
+ }
+
+ return out;
+}
+
+/**
+ * @brief Process interrupt.
+ */
+static void ns16550_isr(void *arg)
+{
+ rtems_termios_tty *tty = arg;
+ ns16550_context *ctx = rtems_termios_get_device_context(tty);
+ uintptr_t port = ctx->port;
+ ns16550_get_reg get = ctx->get_reg;
+ int i = 0;
+ char buf [SP_FIFO_SIZE];
+
+ /* Iterate until no more interrupts are pending */
+ do {
+ /* Fetch received characters */
+ for (i = 0; i < SP_FIFO_SIZE; ++i) {
+ if ((get( port, NS16550_LINE_STATUS) & SP_LSR_RDY) != 0) {
+ buf [i] = (char) get(port, NS16550_RECEIVE_BUFFER);
+ } else {
+ break;
+ }
+ }
+
+ /* Enqueue fetched characters */
+ rtems_termios_enqueue_raw_characters(tty, buf, i);
+
+ /* Do transmit */
+ if (ctx->out_total > 0
+ && (get(port, NS16550_LINE_STATUS) & SP_LSR_THOLD) != 0) {
+ size_t current = ctx->out_current;
+
+ ctx->out_buf += current;
+ ctx->out_remaining -= current;
+
+ if (ctx->out_remaining > 0) {
+ ctx->out_current =
+ ns16550_write_to_fifo(ctx, ctx->out_buf, ctx->out_remaining);
+ } else {
+ rtems_termios_dequeue_characters(tty, ctx->out_total);
+ }
+ }
+ } while ((get( port, NS16550_INTERRUPT_ID) & SP_IID_0) == 0);
+}
+
+static void ns16550_isr_task(void *arg)
+{
+ rtems_termios_tty *tty = arg;
+ ns16550_context *ctx = rtems_termios_get_device_context(tty);
+ uint8_t status = (*ctx->get_reg)(ctx->port, NS16550_LINE_STATUS);
+
+ if ((status & SP_LSR_RDY) != 0) {
+ ns16550_clear_and_set_interrupts(ctx, SP_INT_RX_ENABLE, 0);
+ rtems_termios_rxirq_occured(tty);
+ }
+
+ if (ctx->out_total > 0 && (status & SP_LSR_THOLD) != 0) {
+ size_t current = ctx->out_current;
+
+ ctx->out_buf += current;
+ ctx->out_remaining -= current;
+
+ if (ctx->out_remaining > 0) {
+ ctx->out_current =
+ ns16550_write_to_fifo(ctx, ctx->out_buf, ctx->out_remaining);
+ } else {
+ size_t done = ctx->out_total;
+
+ ctx->out_total = 0;
+ ns16550_clear_and_set_interrupts(ctx, SP_INT_TX_ENABLE, 0);
+ rtems_termios_dequeue_characters(tty, done);
+ }
+ }
+}
+
+static int ns16550_read_task(rtems_termios_device_context *base)
+{
+ ns16550_context *ctx = (ns16550_context *) base;
+ uintptr_t port = ctx->port;
+ ns16550_get_reg get = ctx->get_reg;
+ char buf[SP_FIFO_SIZE];
+ int i;
+
+ for (i = 0; i < SP_FIFO_SIZE; ++i) {
+ if ((get(port, NS16550_LINE_STATUS) & SP_LSR_RDY) != 0) {
+ buf[i] = (char) get(port, NS16550_RECEIVE_BUFFER);
+ } else {
+ break;
+ }
+ }
+
+ rtems_termios_enqueue_raw_characters(ctx->tty, buf, i);
+ ns16550_clear_and_set_interrupts(ctx, 0, SP_INT_RX_ENABLE);
+
+ return -1;
+}
+
+/*
+ * ns16550_initialize_interrupts
+ *
+ * This routine initializes the port to operate in interrupt driver mode.
+ */
+static void ns16550_initialize_interrupts(
+ struct rtems_termios_tty *tty,
+ ns16550_context *ctx,
+ void (*isr)(void *)
+)
+{
+ #ifdef BSP_FEATURE_IRQ_EXTENSION
+ {
+ rtems_status_code sc = RTEMS_SUCCESSFUL;
+ sc = rtems_interrupt_handler_install(
+ ctx->irq,
+ "NS16550",
+ RTEMS_INTERRUPT_SHARED,
+ isr,
+ tty
+ );
+ if (sc != RTEMS_SUCCESSFUL) {
+ /* FIXME */
+ printk( "%s: Error: Install interrupt handler\n", __func__);
+ rtems_fatal_error_occurred( 0xdeadbeef);
+ }
+ }
+ #elif defined(BSP_FEATURE_IRQ_LEGACY)
+ {
+ int rv = 0;
+ #ifdef BSP_FEATURE_IRQ_LEGACY_SHARED_HANDLER_SUPPORT
+ rtems_irq_connect_data cd = {
+ ctx->irq,
+ isr,
+ tty,
+ NULL,
+ NULL,
+ NULL,
+ NULL
+ };
+ rv = BSP_install_rtems_shared_irq_handler( &cd);
+ #else
+ rtems_irq_connect_data cd = {
+ ctx->irq,
+ isr,
+ tty,
+ NULL,
+ NULL,
+ NULL
+ };
+ rv = BSP_install_rtems_irq_handler( &cd);
+ #endif
+ if (rv == 0) {
+ /* FIXME */
+ printk( "%s: Error: Install interrupt handler\n", __func__);
+ rtems_fatal_error_occurred( 0xdeadbeef);
+ }
+ }
+ #endif
+}
+
+/*
+ * ns16550_open
+ */
+
+static bool ns16550_open(
+ struct rtems_termios_tty *tty,
+ rtems_termios_device_context *base,
+ struct termios *term,
+ rtems_libio_open_close_args_t *args
+)
+{
+ ns16550_context *ctx = (ns16550_context *) base;
+
+ ctx->tty = tty;
+
+ /* Set initial baud */
+ rtems_termios_set_initial_baud(tty, ctx->initial_baud);
+
+ if (tty->handler.mode == TERMIOS_IRQ_DRIVEN) {
+ ns16550_initialize_interrupts(tty, ctx, ns16550_isr);
+ ns16550_enable_interrupts(ctx, NS16550_ENABLE_ALL_INTR_EXCEPT_TX);
+ } else if (tty->handler.mode == TERMIOS_TASK_DRIVEN) {
+ ns16550_initialize_interrupts(tty, ctx, ns16550_isr_task);
+ ns16550_enable_interrupts(ctx, NS16550_ENABLE_ALL_INTR_EXCEPT_TX);
+ }
+
+ return true;
+}
+
+static void ns16550_cleanup_interrupts(
+ struct rtems_termios_tty *tty,
+ ns16550_context *ctx,
+ void (*isr)(void *)
+)
+{
+ #if defined(BSP_FEATURE_IRQ_EXTENSION)
+ rtems_status_code sc = RTEMS_SUCCESSFUL;
+ sc = rtems_interrupt_handler_remove(
+ ctx->irq,
+ isr,
+ tty
+ );
+ if (sc != RTEMS_SUCCESSFUL) {
+ /* FIXME */
+ printk("%s: Error: Remove interrupt handler\n", __func__);
+ rtems_fatal_error_occurred(0xdeadbeef);
+ }
+ #elif defined(BSP_FEATURE_IRQ_LEGACY)
+ int rv = 0;
+ rtems_irq_connect_data cd = {
+ .name = ctx->irq,
+ .hdl = isr,
+ .handle = tty
+ };
+ rv = BSP_remove_rtems_irq_handler(&cd);
+ if (rv == 0) {
+ /* FIXME */
+ printk("%s: Error: Remove interrupt handler\n", __func__);
+ rtems_fatal_error_occurred(0xdeadbeef);
+ }
+ #endif
+}
+
+/*
+ * ns16550_close
+ */
+
+static void ns16550_close(
+ struct rtems_termios_tty *tty,
+ rtems_termios_device_context *base,
+ rtems_libio_open_close_args_t *args
+)
+{
+ ns16550_context *ctx = (ns16550_context *) base;
+
+ ns16550_enable_interrupts(ctx, NS16550_DISABLE_ALL_INTR);
+
+ if (tty->handler.mode == TERMIOS_IRQ_DRIVEN) {
+ ns16550_cleanup_interrupts(tty, ctx, ns16550_isr);
+ } else if (tty->handler.mode == TERMIOS_TASK_DRIVEN) {
+ ns16550_cleanup_interrupts(tty, ctx, ns16550_isr_task);
+ }
+}
+
+/**
+ * @brief Polled write for NS16550.
+ */
+void ns16550_polled_putchar(rtems_termios_device_context *base, char out)
+{
+ ns16550_context *ctx = (ns16550_context *) base;
+ uintptr_t port = ctx->port;
+ ns16550_get_reg get = ctx->get_reg;
+ ns16550_set_reg set = ctx->set_reg;
+ uint32_t status = 0;
+ rtems_interrupt_lock_context lock_context;
+
+ /* Save port interrupt mask */
+ uint32_t interrupt_mask = get( port, NS16550_INTERRUPT_ENABLE);
+
+ /* Disable port interrupts */
+ ns16550_enable_interrupts(ctx, NS16550_DISABLE_ALL_INTR);
+
+ while (true) {
+ /* Try to transmit the character in a critical section */
+ rtems_termios_device_lock_acquire(&ctx->base, &lock_context);
+
+ /* Read the transmitter holding register and check it */
+ status = get( port, NS16550_LINE_STATUS);
+ if ((status & SP_LSR_THOLD) != 0) {
+ /* Transmit character */
+ set( port, NS16550_TRANSMIT_BUFFER, out);
+
+ /* Finished */
+ rtems_termios_device_lock_release(&ctx->base, &lock_context);
+ break;
+ } else {
+ rtems_termios_device_lock_release(&ctx->base, &lock_context);
+ }
+
+ /* Wait for transmitter holding register to be empty */
+ do {
+ status = get( port, NS16550_LINE_STATUS);
+ } while ((status & SP_LSR_THOLD) == 0);
+ }
+
+ /* Restore port interrupt mask */
+ set( port, NS16550_INTERRUPT_ENABLE, interrupt_mask);
+}
+
+/*
+ * These routines provide control of the RTS and DTR lines
+ */
+
+/*
+ * ns16550_assert_RTS
+ */
+
+static void ns16550_assert_RTS(rtems_termios_device_context *base)
+{
+ ns16550_context *ctx = (ns16550_context *) base;
+ rtems_interrupt_lock_context lock_context;
+
+ /*
+ * Assert RTS
+ */
+ rtems_termios_device_lock_acquire(base, &lock_context);
+ ctx->modem_control |= SP_MODEM_RTS;
+ (*ctx->set_reg)(ctx->port, NS16550_MODEM_CONTROL, ctx->modem_control);
+ rtems_termios_device_lock_release(base, &lock_context);
+}
+
+/*
+ * ns16550_negate_RTS
+ */
+
+static void ns16550_negate_RTS(rtems_termios_device_context *base)
+{
+ ns16550_context *ctx = (ns16550_context *) base;
+ rtems_interrupt_lock_context lock_context;
+
+ /*
+ * Negate RTS
+ */
+ rtems_termios_device_lock_acquire(base, &lock_context);
+ ctx->modem_control &= ~SP_MODEM_RTS;
+ (*ctx->set_reg)(ctx->port, NS16550_MODEM_CONTROL, ctx->modem_control);
+ rtems_termios_device_lock_release(base, &lock_context);
+}
+
+/*
+ * These flow control routines utilise a connection from the local DTR
+ * line to the remote CTS line
+ */
+
+/*
+ * ns16550_assert_DTR
+ */
+
+static void ns16550_assert_DTR(rtems_termios_device_context *base)
+{
+ ns16550_context *ctx = (ns16550_context *) base;
+ rtems_interrupt_lock_context lock_context;
+
+ /*
+ * Assert DTR
+ */
+ rtems_termios_device_lock_acquire(base, &lock_context);
+ ctx->modem_control |= SP_MODEM_DTR;
+ (*ctx->set_reg)(ctx->port, NS16550_MODEM_CONTROL, ctx->modem_control);
+ rtems_termios_device_lock_release(base, &lock_context);
+}
+
+/*
+ * ns16550_negate_DTR
+ */
+
+static void ns16550_negate_DTR(rtems_termios_device_context *base)
+{
+ ns16550_context *ctx = (ns16550_context *) base;
+ rtems_interrupt_lock_context lock_context;
+
+ /*
+ * Negate DTR
+ */
+ rtems_termios_device_lock_acquire(base, &lock_context);
+ ctx->modem_control &=~SP_MODEM_DTR;
+ (*ctx->set_reg)(ctx->port, NS16550_MODEM_CONTROL,ctx->modem_control);
+ rtems_termios_device_lock_release(base, &lock_context);
+}
+
+/*
+ * ns16550_set_attributes
+ *
+ * This function sets the channel to reflect the requested termios
+ * port settings.
+ */
+
+static bool ns16550_set_attributes(
+ rtems_termios_device_context *base,
+ const struct termios *t
+)
+{
+ ns16550_context *ctx = (ns16550_context *) base;
+ uint32_t pNS16550;
+ uint32_t ulBaudDivisor;
+ uint8_t ucLineControl;
+ uint32_t baud_requested;
+ ns16550_set_reg setReg;
+
+ pNS16550 = ctx->port;
+ setReg = ctx->set_reg;
+
+ /*
+ * Calculate the baud rate divisor
+ *
+ * Assert ensures there is no division by 0.
+ */
+
+ baud_requested = rtems_termios_baud_to_number(t->c_ospeed);
+ _Assert( baud_requested != 0 );
+
+ ulBaudDivisor = NS16550_GetBaudDivisor(ctx, baud_requested);
+
+ ucLineControl = 0;
+
+ /*
+ * Parity
+ */
+
+ if (t->c_cflag & PARENB) {
+ ucLineControl |= SP_LINE_PAR;
+ if (!(t->c_cflag & PARODD))
+ ucLineControl |= SP_LINE_ODD;
+ }
+
+ /*
+ * Character Size
+ */
+
+ if (t->c_cflag & CSIZE) {
+ switch (t->c_cflag & CSIZE) {
+ case CS5: ucLineControl |= FIVE_BITS; break;
+ case CS6: ucLineControl |= SIX_BITS; break;
+ case CS7: ucLineControl |= SEVEN_BITS; break;
+ case CS8: ucLineControl |= EIGHT_BITS; break;
+ }
+ } else {
+ ucLineControl |= EIGHT_BITS; /* default to 9600,8,N,1 */
+ }
+
+ /*
+ * Stop Bits
+ */
+
+ if (t->c_cflag & CSTOPB) {
+ ucLineControl |= SP_LINE_STOP; /* 2 stop bits */
+ } else {
+ ; /* 1 stop bit */
+ }
+
+ /*
+ * Now actually set the chip
+ */
+
+ if (ulBaudDivisor != ctx->baud_divisor || ucLineControl != ctx->line_control) {
+ rtems_interrupt_lock_context lock_context;
+
+ ctx->baud_divisor = ulBaudDivisor;
+ ctx->line_control = ucLineControl;
+
+ rtems_termios_device_lock_acquire(base, &lock_context);
+
+ /*
+ * Set the baud rate
+ *
+ * NOTE: When the Divisor Latch Access Bit (DLAB) is set to 1,
+ * the transmit buffer and interrupt enable registers
+ * turn into the LSB and MSB divisor latch registers.
+ */
+
+ (*setReg)(pNS16550, NS16550_LINE_CONTROL, SP_LINE_DLAB);
+ (*setReg)(pNS16550, NS16550_TRANSMIT_BUFFER, ulBaudDivisor&0xff);
+ (*setReg)(pNS16550, NS16550_INTERRUPT_ENABLE, (ulBaudDivisor>>8)&0xff);
+
+ /*
+ * Now write the line control
+ */
+ (*setReg)(pNS16550, NS16550_LINE_CONTROL, ucLineControl );
+
+ rtems_termios_device_lock_release(base, &lock_context);
+ }
+
+ return true;
+}
+
+/**
+ * @brief Transmits up to @a len characters from @a buf.
+ *
+ * This routine is invoked either from task context with disabled interrupts to
+ * start a new transmission process with exactly one character in case of an
+ * idle output state or from the interrupt handler to refill the transmitter.
+ *
+ * Returns always zero.
+ */
+static void ns16550_write_support_int(
+ rtems_termios_device_context *base,
+ const char *buf,
+ size_t len
+)
+{
+ ns16550_context *ctx = (ns16550_context *) base;
+
+ ctx->out_total = len;
+
+ if (len > 0) {
+ ctx->out_remaining = len;
+ ctx->out_buf = buf;
+ ctx->out_current = ns16550_write_to_fifo(ctx, buf, len);
+
+ ns16550_enable_interrupts(ctx, NS16550_ENABLE_ALL_INTR);
+ } else {
+ ns16550_enable_interrupts(ctx, NS16550_ENABLE_ALL_INTR_EXCEPT_TX);
+ }
+}
+
+static void ns16550_write_support_task(
+ rtems_termios_device_context *base,
+ const char *buf,
+ size_t len
+)
+{
+ ns16550_context *ctx = (ns16550_context *) base;
+
+ ctx->out_total = len;
+
+ if (len > 0) {
+ ctx->out_remaining = len;
+ ctx->out_buf = buf;
+ ctx->out_current = ns16550_write_to_fifo(ctx, buf, len);
+
+ ns16550_clear_and_set_interrupts(ctx, 0, SP_INT_TX_ENABLE);
+ }
+}
+
+/*
+ * ns16550_write_support_polled
+ *
+ * Console Termios output entry point.
+ *
+ */
+
+static void ns16550_write_support_polled(
+ rtems_termios_device_context *base,
+ const char *buf,
+ size_t len
+)
+{
+ size_t nwrite = 0;
+
+ /*
+ * poll each byte in the string out of the port.
+ */
+ while (nwrite < len) {
+ /*
+ * transmit character
+ */
+ ns16550_polled_putchar(base, *buf++);
+ nwrite++;
+ }
+}
+
+/*
+ * Debug gets() support
+ */
+int ns16550_polled_getchar(rtems_termios_device_context *base)
+{
+ ns16550_context *ctx = (ns16550_context *) base;
+ uint32_t pNS16550;
+ unsigned char ucLineStatus;
+ uint8_t cChar;
+ ns16550_get_reg getReg;
+
+ pNS16550 = ctx->port;
+ getReg = ctx->get_reg;
+
+ ucLineStatus = (*getReg)(pNS16550, NS16550_LINE_STATUS);
+ if (ucLineStatus & SP_LSR_RDY) {
+ cChar = (*getReg)(pNS16550, NS16550_RECEIVE_BUFFER);
+ return (int)cChar;
+ }
+ return -1;
+}
+
+/*
+ * Flow control is only supported when using interrupts
+ */
+
+const rtems_termios_device_flow ns16550_flow_rtscts = {
+ .stop_remote_tx = ns16550_negate_RTS,
+ .start_remote_tx = ns16550_assert_RTS
+};
+
+const rtems_termios_device_flow ns16550_flow_dtrcts = {
+ .stop_remote_tx = ns16550_negate_DTR,
+ .start_remote_tx = ns16550_assert_DTR
+};
+
+const rtems_termios_device_handler ns16550_handler_interrupt = {
+ .first_open = ns16550_open,
+ .last_close = ns16550_close,
+ .poll_read = NULL,
+ .write = ns16550_write_support_int,
+ .set_attributes = ns16550_set_attributes,
+ .mode = TERMIOS_IRQ_DRIVEN
+};
+
+const rtems_termios_device_handler ns16550_handler_polled = {
+ .first_open = ns16550_open,
+ .last_close = ns16550_close,
+ .poll_read = ns16550_polled_getchar,
+ .write = ns16550_write_support_polled,
+ .set_attributes = ns16550_set_attributes,
+ .mode = TERMIOS_POLLED
+};
+
+const rtems_termios_device_handler ns16550_handler_task = {
+ .first_open = ns16550_open,
+ .last_close = ns16550_close,
+ .poll_read = ns16550_read_task,
+ .write = ns16550_write_support_task,
+ .set_attributes = ns16550_set_attributes,
+ .mode = TERMIOS_TASK_DRIVEN
+};
diff --git a/bsps/shared/dev/serial/ns16550.c b/bsps/shared/dev/serial/ns16550.c
new file mode 100644
index 0000000000..b1e5892c15
--- /dev/null
+++ b/bsps/shared/dev/serial/ns16550.c
@@ -0,0 +1,875 @@
+/**
+ * @file
+ *
+ * This file contains the TTY driver for the National Semiconductor NS16550.
+ *
+ * This part is widely cloned and second sourced. It is found in a number
+ * of "Super IO" controllers.
+ *
+ * This driver uses the termios pseudo driver.
+ */
+
+/*
+ * COPYRIGHT (c) 1998 by Radstone Technology
+ *
+ * THIS FILE IS PROVIDED TO YOU, THE USER, "AS IS", WITHOUT WARRANTY OF ANY
+ * KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTY OF FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK
+ * AS TO THE QUALITY AND PERFORMANCE OF ALL CODE IN THIS FILE IS WITH YOU.
+ *
+ * You are hereby granted permission to use, copy, modify, and distribute
+ * this file, provided that this notice, plus the above copyright notice
+ * and disclaimer, appears in all copies. Radstone Technology will provide
+ * no support for this code.
+ *
+ * COPYRIGHT (c) 1989-2012.
+ * On-Line Applications Research Corporation (OAR).
+ *
+ * The license and distribution terms for this file may be
+ * found in the file LICENSE in this distribution or at
+ * http://www.rtems.org/license/LICENSE.
+ */
+
+#include <stdlib.h>
+
+#include <rtems.h>
+#include <rtems/libio.h>
+#include <rtems/ringbuf.h>
+#include <rtems/bspIo.h>
+#include <rtems/termiostypes.h>
+
+#include <libchip/serial.h>
+#include <libchip/sersupp.h>
+
+#include <bsp.h>
+
+#include <libchip/ns16550_p.h>
+#include <libchip/ns16550.h>
+
+#if defined(BSP_FEATURE_IRQ_EXTENSION)
+ #include <bsp/irq.h>
+#elif defined(BSP_FEATURE_IRQ_LEGACY)
+ #include <bsp/irq.h>
+#elif defined(__PPC__) || defined(__i386__)
+ #include <bsp/irq.h>
+ #define BSP_FEATURE_IRQ_LEGACY
+ #ifdef BSP_SHARED_HANDLER_SUPPORT
+ #define BSP_FEATURE_IRQ_LEGACY_SHARED_HANDLER_SUPPORT
+ #endif
+#endif
+
+typedef struct {
+ uint8_t ucModemCtrl;
+ int transmitFifoChars;
+} NS16550Context;
+
+/*
+ * Driver functions
+ */
+
+NS16550_STATIC void ns16550_init(int minor);
+
+NS16550_STATIC int ns16550_open(
+ int major,
+ int minor,
+ void * arg
+);
+
+NS16550_STATIC int ns16550_close(
+ int major,
+ int minor,
+ void * arg
+);
+
+NS16550_STATIC void ns16550_write_polled(
+ int minor,
+ char cChar
+);
+
+NS16550_STATIC int ns16550_assert_RTS(
+ int minor
+);
+
+NS16550_STATIC int ns16550_negate_RTS(
+ int minor
+);
+
+NS16550_STATIC int ns16550_assert_DTR(
+ int minor
+);
+
+NS16550_STATIC int ns16550_negate_DTR(
+ int minor
+);
+
+NS16550_STATIC void ns16550_initialize_interrupts(int minor);
+
+NS16550_STATIC void ns16550_cleanup_interrupts(int minor);
+
+NS16550_STATIC ssize_t ns16550_write_support_int(
+ int minor,
+ const char *buf,
+ size_t len
+);
+
+NS16550_STATIC ssize_t ns16550_write_support_polled(
+ int minor,
+ const char *buf,
+ size_t len
+ );
+
+int ns16550_inbyte_nonblocking_polled(
+ int minor
+);
+
+NS16550_STATIC void ns16550_enable_interrupts(
+ console_tbl *c,
+ int mask
+);
+
+NS16550_STATIC int ns16550_set_attributes(
+ int minor,
+ const struct termios *t
+);
+
+#if defined(BSP_FEATURE_IRQ_EXTENSION) || defined(BSP_FEATURE_IRQ_LEGACY)
+ NS16550_STATIC void ns16550_isr(void *arg);
+#endif
+
+RTEMS_INTERRUPT_LOCK_DEFINE(static, ns16550_lock, "NS16550")
+
+/*
+ * Flow control is only supported when using interrupts
+ */
+
+const console_flow ns16550_flow_RTSCTS = {
+ ns16550_negate_RTS, /* deviceStopRemoteTx */
+ ns16550_assert_RTS /* deviceStartRemoteTx */
+};
+
+const console_flow ns16550_flow_DTRCTS = {
+ ns16550_negate_DTR, /* deviceStopRemoteTx */
+ ns16550_assert_DTR /* deviceStartRemoteTx */
+};
+
+const console_fns ns16550_fns = {
+ libchip_serial_default_probe, /* deviceProbe */
+ ns16550_open, /* deviceFirstOpen */
+ ns16550_close, /* deviceLastClose */
+ NULL, /* deviceRead */
+ ns16550_write_support_int, /* deviceWrite */
+ ns16550_init, /* deviceInitialize */
+ ns16550_write_polled, /* deviceWritePolled */
+ ns16550_set_attributes, /* deviceSetAttributes */
+ true /* deviceOutputUsesInterrupts */
+};
+
+const console_fns ns16550_fns_polled = {
+ libchip_serial_default_probe, /* deviceProbe */
+ ns16550_open, /* deviceFirstOpen */
+ ns16550_close, /* deviceLastClose */
+ ns16550_inbyte_nonblocking_polled, /* deviceRead */
+ ns16550_write_support_polled, /* deviceWrite */
+ ns16550_init, /* deviceInitialize */
+ ns16550_write_polled, /* deviceWritePolled */
+ ns16550_set_attributes, /* deviceSetAttributes */
+ false /* deviceOutputUsesInterrupts */
+};
+
+static uint32_t NS16550_GetBaudDivisor(const console_tbl *c, uint32_t baud)
+{
+ uint32_t clock = c->ulClock;
+ uint32_t baudDivisor = (clock != 0 ? clock : 115200) / (baud * 16);
+
+ if (c->deviceType == SERIAL_NS16550_WITH_FDR) {
+ uint32_t fractionalDivider = 0x10;
+ uint32_t err = baud;
+ uint32_t mulVal;
+ uint32_t divAddVal;
+
+ clock /= 16 * baudDivisor;
+ for (mulVal = 1; mulVal < 16; ++mulVal) {
+ for (divAddVal = 0; divAddVal < mulVal; ++divAddVal) {
+ uint32_t actual = (mulVal * clock) / (mulVal + divAddVal);
+ uint32_t newErr = actual > baud ? actual - baud : baud - actual;
+
+ if (newErr < err) {
+ err = newErr;
+ fractionalDivider = (mulVal << 4) | divAddVal;
+ }
+ }
+ }
+
+ (*c->setRegister)(
+ c->ulCtrlPort1,
+ NS16550_FRACTIONAL_DIVIDER,
+ fractionalDivider
+ );
+ }
+
+ return baudDivisor;
+}
+
+/*
+ * ns16550_init
+ */
+
+void ns16550_init(int minor)
+{
+ uintptr_t pNS16550;
+ uint8_t ucDataByte;
+ uint32_t ulBaudDivisor;
+ NS16550Context *pns16550Context;
+ setRegister_f setReg;
+ getRegister_f getReg;
+ console_tbl *c = Console_Port_Tbl [minor];
+
+ pns16550Context=(NS16550Context *)malloc(sizeof(NS16550Context));
+
+ if (pns16550Context == NULL) {
+ printk( "%s: Error: Not enough memory\n", __func__);
+ rtems_fatal_error_occurred( 0xdeadbeef);
+ }
+
+ Console_Port_Data[minor].pDeviceContext=(void *)pns16550Context;
+ pns16550Context->ucModemCtrl=SP_MODEM_IRQ;
+
+ pNS16550 = c->ulCtrlPort1;
+ setReg = c->setRegister;
+ getReg = c->getRegister;
+
+ /* Clear the divisor latch, clear all interrupt enables,
+ * and reset and
+ * disable the FIFO's.
+ */
+
+ (*setReg)(pNS16550, NS16550_LINE_CONTROL, 0x0);
+ ns16550_enable_interrupts( c, NS16550_DISABLE_ALL_INTR );
+
+ /* Set the divisor latch and set the baud rate. */
+
+ ulBaudDivisor = NS16550_GetBaudDivisor(c, (uintptr_t) c->pDeviceParams);
+ ucDataByte = SP_LINE_DLAB;
+ (*setReg)(pNS16550, NS16550_LINE_CONTROL, ucDataByte);
+
+ /* XXX */
+ (*setReg)(pNS16550,NS16550_TRANSMIT_BUFFER,(uint8_t)(ulBaudDivisor & 0xffU));
+ (*setReg)(
+ pNS16550,NS16550_INTERRUPT_ENABLE,
+ (uint8_t)(( ulBaudDivisor >> 8 ) & 0xffU )
+ );
+
+ /* Clear the divisor latch and set the character size to eight bits */
+ /* with one stop bit and no parity checking. */
+ ucDataByte = EIGHT_BITS;
+ (*setReg)(pNS16550, NS16550_LINE_CONTROL, ucDataByte);
+
+ /* Enable and reset transmit and receive FIFOs. TJA */
+ ucDataByte = SP_FIFO_ENABLE;
+ (*setReg)(pNS16550, NS16550_FIFO_CONTROL, ucDataByte);
+
+ ucDataByte = SP_FIFO_ENABLE | SP_FIFO_RXRST | SP_FIFO_TXRST;
+ (*setReg)(pNS16550, NS16550_FIFO_CONTROL, ucDataByte);
+
+ ns16550_enable_interrupts(c, NS16550_DISABLE_ALL_INTR);
+
+ /* Set data terminal ready. */
+ /* And open interrupt tristate line */
+ (*setReg)(pNS16550, NS16550_MODEM_CONTROL,pns16550Context->ucModemCtrl);
+
+ (*getReg)(pNS16550, NS16550_LINE_STATUS );
+ (*getReg)(pNS16550, NS16550_RECEIVE_BUFFER );
+}
+
+/*
+ * ns16550_open
+ */
+
+int ns16550_open(
+ int major,
+ int minor,
+ void *arg
+)
+{
+ rtems_libio_open_close_args_t *oc = (rtems_libio_open_close_args_t *) arg;
+ struct rtems_termios_tty *tty = (struct rtems_termios_tty *) oc->iop->data1;
+ console_tbl *c = Console_Port_Tbl [minor];
+ console_data *d = &Console_Port_Data [minor];
+
+ d->termios_data = tty;
+
+ /* Assert DTR */
+ if (c->pDeviceFlow != &ns16550_flow_DTRCTS) {
+ ns16550_assert_DTR( minor);
+ }
+
+ /* Set initial baud */
+ rtems_termios_set_initial_baud( tty, (intptr_t) c->pDeviceParams);
+
+ if (c->pDeviceFns->deviceOutputUsesInterrupts) {
+ ns16550_initialize_interrupts( minor);
+ ns16550_enable_interrupts( c, NS16550_ENABLE_ALL_INTR_EXCEPT_TX);
+ }
+
+ return RTEMS_SUCCESSFUL;
+}
+
+/*
+ * ns16550_close
+ */
+
+int ns16550_close(
+ int major,
+ int minor,
+ void * arg
+)
+{
+ console_tbl *c = Console_Port_Tbl [minor];
+
+ /*
+ * Negate DTR
+ */
+ if (c->pDeviceFlow != &ns16550_flow_DTRCTS) {
+ ns16550_negate_DTR(minor);
+ }
+
+ ns16550_enable_interrupts(c, NS16550_DISABLE_ALL_INTR);
+
+ if (c->pDeviceFns->deviceOutputUsesInterrupts) {
+ ns16550_cleanup_interrupts(minor);
+ }
+
+ return(RTEMS_SUCCESSFUL);
+}
+
+/**
+ * @brief Polled write for NS16550.
+ */
+void ns16550_outch_polled(console_tbl *c, char out)
+{
+ uintptr_t port = c->ulCtrlPort1;
+ getRegister_f get = c->getRegister;
+ setRegister_f set = c->setRegister;
+ uint32_t status = 0;
+ rtems_interrupt_lock_context lock_context;
+
+ /* Save port interrupt mask */
+ uint32_t interrupt_mask = get( port, NS16550_INTERRUPT_ENABLE);
+
+ /* Disable port interrupts */
+ ns16550_enable_interrupts( c, NS16550_DISABLE_ALL_INTR);
+
+ while (true) {
+ /* Try to transmit the character in a critical section */
+ rtems_interrupt_lock_acquire(&ns16550_lock, &lock_context);
+
+ /* Read the transmitter holding register and check it */
+ status = get( port, NS16550_LINE_STATUS);
+ if ((status & SP_LSR_THOLD) != 0) {
+ /* Transmit character */
+ set( port, NS16550_TRANSMIT_BUFFER, out);
+
+ /* Finished */
+ rtems_interrupt_lock_release(&ns16550_lock, &lock_context);
+ break;
+ } else {
+ rtems_interrupt_lock_release(&ns16550_lock, &lock_context);
+ }
+
+ /* Wait for transmitter holding register to be empty */
+ do {
+ status = get( port, NS16550_LINE_STATUS);
+ } while ((status & SP_LSR_THOLD) == 0);
+ }
+
+ /* Restore port interrupt mask */
+ set( port, NS16550_INTERRUPT_ENABLE, interrupt_mask);
+}
+
+void ns16550_write_polled(int minor, char out)
+{
+ console_tbl *c = Console_Port_Tbl [minor];
+
+ ns16550_outch_polled( c, out );
+}
+
+/*
+ * These routines provide control of the RTS and DTR lines
+ */
+
+/*
+ * ns16550_assert_RTS
+ */
+
+NS16550_STATIC int ns16550_assert_RTS(int minor)
+{
+ uint32_t pNS16550;
+ rtems_interrupt_lock_context lock_context;
+ NS16550Context *pns16550Context;
+ setRegister_f setReg;
+
+ pns16550Context=(NS16550Context *) Console_Port_Data[minor].pDeviceContext;
+
+ pNS16550 = Console_Port_Tbl[minor]->ulCtrlPort1;
+ setReg = Console_Port_Tbl[minor]->setRegister;
+
+ /*
+ * Assert RTS
+ */
+ rtems_interrupt_lock_acquire(&ns16550_lock, &lock_context);
+ pns16550Context->ucModemCtrl|=SP_MODEM_RTS;
+ (*setReg)(pNS16550, NS16550_MODEM_CONTROL, pns16550Context->ucModemCtrl);
+ rtems_interrupt_lock_release(&ns16550_lock, &lock_context);
+ return 0;
+}
+
+/*
+ * ns16550_negate_RTS
+ */
+
+NS16550_STATIC int ns16550_negate_RTS(int minor)
+{
+ uint32_t pNS16550;
+ rtems_interrupt_lock_context lock_context;
+ NS16550Context *pns16550Context;
+ setRegister_f setReg;
+
+ pns16550Context=(NS16550Context *) Console_Port_Data[minor].pDeviceContext;
+
+ pNS16550 = Console_Port_Tbl[minor]->ulCtrlPort1;
+ setReg = Console_Port_Tbl[minor]->setRegister;
+
+ /*
+ * Negate RTS
+ */
+ rtems_interrupt_lock_acquire(&ns16550_lock, &lock_context);
+ pns16550Context->ucModemCtrl&=~SP_MODEM_RTS;
+ (*setReg)(pNS16550, NS16550_MODEM_CONTROL, pns16550Context->ucModemCtrl);
+ rtems_interrupt_lock_release(&ns16550_lock, &lock_context);
+ return 0;
+}
+
+/*
+ * These flow control routines utilise a connection from the local DTR
+ * line to the remote CTS line
+ */
+
+/*
+ * ns16550_assert_DTR
+ */
+
+NS16550_STATIC int ns16550_assert_DTR(int minor)
+{
+ uint32_t pNS16550;
+ rtems_interrupt_lock_context lock_context;
+ NS16550Context *pns16550Context;
+ setRegister_f setReg;
+
+ pns16550Context=(NS16550Context *) Console_Port_Data[minor].pDeviceContext;
+
+ pNS16550 = Console_Port_Tbl[minor]->ulCtrlPort1;
+ setReg = Console_Port_Tbl[minor]->setRegister;
+
+ /*
+ * Assert DTR
+ */
+ rtems_interrupt_lock_acquire(&ns16550_lock, &lock_context);
+ pns16550Context->ucModemCtrl|=SP_MODEM_DTR;
+ (*setReg)(pNS16550, NS16550_MODEM_CONTROL, pns16550Context->ucModemCtrl);
+ rtems_interrupt_lock_release(&ns16550_lock, &lock_context);
+ return 0;
+}
+
+/*
+ * ns16550_negate_DTR
+ */
+
+NS16550_STATIC int ns16550_negate_DTR(int minor)
+{
+ uint32_t pNS16550;
+ rtems_interrupt_lock_context lock_context;
+ NS16550Context *pns16550Context;
+ setRegister_f setReg;
+
+ pns16550Context=(NS16550Context *) Console_Port_Data[minor].pDeviceContext;
+
+ pNS16550 = Console_Port_Tbl[minor]->ulCtrlPort1;
+ setReg = Console_Port_Tbl[minor]->setRegister;
+
+ /*
+ * Negate DTR
+ */
+ rtems_interrupt_lock_acquire(&ns16550_lock, &lock_context);
+ pns16550Context->ucModemCtrl&=~SP_MODEM_DTR;
+ (*setReg)(pNS16550, NS16550_MODEM_CONTROL,pns16550Context->ucModemCtrl);
+ rtems_interrupt_lock_release(&ns16550_lock, &lock_context);
+ return 0;
+}
+
+/*
+ * ns16550_set_attributes
+ *
+ * This function sets the channel to reflect the requested termios
+ * port settings.
+ */
+
+int ns16550_set_attributes(
+ int minor,
+ const struct termios *t
+)
+{
+ uint32_t pNS16550;
+ uint32_t ulBaudDivisor;
+ uint8_t ucLineControl;
+ uint32_t baud_requested;
+ setRegister_f setReg;
+ rtems_interrupt_lock_context lock_context;
+ const console_tbl *c = Console_Port_Tbl [minor];
+
+ pNS16550 = c->ulCtrlPort1;
+ setReg = c->setRegister;
+
+ /*
+ * Calculate the baud rate divisor
+ *
+ * Assert ensures there is no division by 0.
+ */
+
+ baud_requested = rtems_termios_baud_to_number(t->c_ospeed);
+ _Assert( baud_requested != 0 );
+ ulBaudDivisor = NS16550_GetBaudDivisor(c, baud_requested);
+
+ ucLineControl = 0;
+
+ /*
+ * Parity
+ */
+
+ if (t->c_cflag & PARENB) {
+ ucLineControl |= SP_LINE_PAR;
+ if (!(t->c_cflag & PARODD))
+ ucLineControl |= SP_LINE_ODD;
+ }
+
+ /*
+ * Character Size
+ */
+
+ if (t->c_cflag & CSIZE) {
+ switch (t->c_cflag & CSIZE) {
+ case CS5: ucLineControl |= FIVE_BITS; break;
+ case CS6: ucLineControl |= SIX_BITS; break;
+ case CS7: ucLineControl |= SEVEN_BITS; break;
+ case CS8: ucLineControl |= EIGHT_BITS; break;
+ }
+ } else {
+ ucLineControl |= EIGHT_BITS; /* default to 9600,8,N,1 */
+ }
+
+ /*
+ * Stop Bits
+ */
+
+ if (t->c_cflag & CSTOPB) {
+ ucLineControl |= SP_LINE_STOP; /* 2 stop bits */
+ } else {
+ ; /* 1 stop bit */
+ }
+
+ /*
+ * Now actually set the chip
+ */
+
+ rtems_interrupt_lock_acquire(&ns16550_lock, &lock_context);
+
+ /*
+ * Set the baud rate
+ *
+ * NOTE: When the Divisor Latch Access Bit (DLAB) is set to 1,
+ * the transmit buffer and interrupt enable registers
+ * turn into the LSB and MSB divisor latch registers.
+ */
+
+ (*setReg)(pNS16550, NS16550_LINE_CONTROL, SP_LINE_DLAB);
+ (*setReg)(pNS16550, NS16550_TRANSMIT_BUFFER, ulBaudDivisor&0xff);
+ (*setReg)(pNS16550, NS16550_INTERRUPT_ENABLE, (ulBaudDivisor>>8)&0xff);
+
+ /*
+ * Now write the line control
+ */
+ (*setReg)(pNS16550, NS16550_LINE_CONTROL, ucLineControl );
+
+ rtems_interrupt_lock_release(&ns16550_lock, &lock_context);
+
+ return 0;
+}
+
+#if defined(BSP_FEATURE_IRQ_EXTENSION) || defined(BSP_FEATURE_IRQ_LEGACY)
+
+/**
+ * @brief Process interrupt.
+ */
+NS16550_STATIC void ns16550_process( int minor)
+{
+ console_tbl *c = Console_Port_Tbl [minor];
+ console_data *d = &Console_Port_Data [minor];
+ NS16550Context *ctx = d->pDeviceContext;
+ uint32_t port = c->ulCtrlPort1;
+ getRegister_f get = c->getRegister;
+ int i;
+ char buf [SP_FIFO_SIZE];
+
+ /* Iterate until no more interrupts are pending */
+ do {
+ /* Fetch received characters */
+ i = 0;
+ while ((get(port, NS16550_LINE_STATUS) & SP_LSR_RDY) != 0) {
+ buf[i++] = (char) get(port, NS16550_RECEIVE_BUFFER);
+ if (i == SP_FIFO_SIZE) {
+ /* Enqueue fetched characters */
+ rtems_termios_enqueue_raw_characters( d->termios_data, buf, i);
+ i = 0;
+ }
+ }
+
+ if (i > 0)
+ rtems_termios_enqueue_raw_characters( d->termios_data, buf, i);
+
+ /* Check if we can dequeue transmitted characters */
+ if (ctx->transmitFifoChars > 0
+ && (get( port, NS16550_LINE_STATUS) & SP_LSR_THOLD) != 0) {
+ /* Dequeue transmitted characters */
+ rtems_termios_dequeue_characters(
+ d->termios_data,
+ ctx->transmitFifoChars
+ );
+ }
+ } while ((get( port, NS16550_INTERRUPT_ID) & SP_IID_0) == 0);
+}
+#endif
+
+/**
+ * @brief Transmits up to @a len characters from @a buf.
+ *
+ * This routine is invoked either from task context with disabled interrupts to
+ * start a new transmission process with exactly one character in case of an
+ * idle output state or from the interrupt handler to refill the transmitter.
+ *
+ * Returns always zero.
+ */
+ssize_t ns16550_write_support_int(
+ int minor,
+ const char *buf,
+ size_t len
+)
+{
+ console_tbl *c = Console_Port_Tbl [minor];
+ console_data *d = &Console_Port_Data [minor];
+ NS16550Context *ctx = d->pDeviceContext;
+ uint32_t port = c->ulCtrlPort1;
+ setRegister_f set = c->setRegister;
+ int i = 0;
+ int out = len > SP_FIFO_SIZE ? SP_FIFO_SIZE : len;
+
+ for (i = 0; i < out; ++i) {
+ set( port, NS16550_TRANSMIT_BUFFER, buf [i]);
+ }
+
+ ctx->transmitFifoChars = out;
+
+ if (out > 0) {
+ ns16550_enable_interrupts( c, NS16550_ENABLE_ALL_INTR);
+ } else {
+ ns16550_enable_interrupts( c, NS16550_ENABLE_ALL_INTR_EXCEPT_TX);
+ }
+
+ return 0;
+}
+
+/*
+ * ns16550_enable_interrupts
+ *
+ * This routine initializes the port to have the specified interrupts masked.
+ */
+NS16550_STATIC void ns16550_enable_interrupts(
+ console_tbl *c,
+ int mask
+)
+{
+ uint32_t pNS16550;
+ setRegister_f setReg;
+
+ pNS16550 = c->ulCtrlPort1;
+ setReg = c->setRegister;
+
+ (*setReg)(pNS16550, NS16550_INTERRUPT_ENABLE, mask);
+}
+
+#if defined(BSP_FEATURE_IRQ_EXTENSION) || defined(BSP_FEATURE_IRQ_LEGACY)
+ void ns16550_isr(void *arg)
+ {
+ int minor = (intptr_t) arg;
+
+ ns16550_process( minor);
+ }
+#endif
+
+/*
+ * ns16550_initialize_interrupts
+ *
+ * This routine initializes the port to operate in interrupt driver mode.
+ */
+NS16550_STATIC void ns16550_initialize_interrupts( int minor)
+{
+#if defined(BSP_FEATURE_IRQ_EXTENSION) || defined(BSP_FEATURE_IRQ_LEGACY)
+ console_tbl *c = Console_Port_Tbl [minor];
+#endif
+
+ #ifdef BSP_FEATURE_IRQ_EXTENSION
+ {
+ rtems_status_code sc = RTEMS_SUCCESSFUL;
+ sc = rtems_interrupt_handler_install(
+ c->ulIntVector,
+ "NS16550",
+ RTEMS_INTERRUPT_SHARED,
+ ns16550_isr,
+ (void *) (intptr_t) minor
+ );
+ if (sc != RTEMS_SUCCESSFUL) {
+ /* FIXME */
+ printk( "%s: Error: Install interrupt handler\n", __func__);
+ rtems_fatal_error_occurred( 0xdeadbeef);
+ }
+ }
+ #elif defined(BSP_FEATURE_IRQ_LEGACY)
+ {
+ int rv = 0;
+ #ifdef BSP_FEATURE_IRQ_LEGACY_SHARED_HANDLER_SUPPORT
+ rtems_irq_connect_data cd = {
+ c->ulIntVector,
+ ns16550_isr,
+ (void *) minor,
+ NULL,
+ NULL,
+ NULL,
+ NULL
+ };
+ rv = BSP_install_rtems_shared_irq_handler( &cd);
+ #else
+ rtems_irq_connect_data cd = {
+ c->ulIntVector,
+ ns16550_isr,
+ (void *) minor,
+ NULL,
+ NULL,
+ NULL
+ };
+ rv = BSP_install_rtems_irq_handler( &cd);
+ #endif
+ if (rv == 0) {
+ /* FIXME */
+ printk( "%s: Error: Install interrupt handler\n", __func__);
+ rtems_fatal_error_occurred( 0xdeadbeef);
+ }
+ }
+ #endif
+}
+
+NS16550_STATIC void ns16550_cleanup_interrupts(int minor)
+{
+ #if defined(BSP_FEATURE_IRQ_EXTENSION)
+ rtems_status_code sc = RTEMS_SUCCESSFUL;
+ console_tbl *c = Console_Port_Tbl [minor];
+ sc = rtems_interrupt_handler_remove(
+ c->ulIntVector,
+ ns16550_isr,
+ (void *) (intptr_t) minor
+ );
+ if (sc != RTEMS_SUCCESSFUL) {
+ /* FIXME */
+ printk("%s: Error: Remove interrupt handler\n", __func__);
+ rtems_fatal_error_occurred(0xdeadbeef);
+ }
+ #elif defined(BSP_FEATURE_IRQ_LEGACY)
+ int rv = 0;
+ console_tbl *c = Console_Port_Tbl [minor];
+ rtems_irq_connect_data cd = {
+ .name = c->ulIntVector,
+ .hdl = ns16550_isr,
+ .handle = (void *) minor
+ };
+ rv = BSP_remove_rtems_irq_handler(&cd);
+ if (rv == 0) {
+ /* FIXME */
+ printk("%s: Error: Remove interrupt handler\n", __func__);
+ rtems_fatal_error_occurred(0xdeadbeef);
+ }
+ #endif
+}
+
+/*
+ * ns16550_write_support_polled
+ *
+ * Console Termios output entry point.
+ *
+ */
+
+ssize_t ns16550_write_support_polled(
+ int minor,
+ const char *buf,
+ size_t len
+)
+{
+ int nwrite = 0;
+
+ /*
+ * poll each byte in the string out of the port.
+ */
+ while (nwrite < len) {
+ /*
+ * transmit character
+ */
+ ns16550_write_polled(minor, *buf++);
+ nwrite++;
+ }
+
+ /*
+ * return the number of bytes written.
+ */
+ return nwrite;
+}
+
+/*
+ * Debug gets() support
+ */
+int ns16550_inch_polled(
+ console_tbl *c
+)
+{
+ uint32_t pNS16550;
+ unsigned char ucLineStatus;
+ uint8_t cChar;
+ getRegister_f getReg;
+
+ pNS16550 = c->ulCtrlPort1;
+ getReg = c->getRegister;
+
+ ucLineStatus = (*getReg)(pNS16550, NS16550_LINE_STATUS);
+ if (ucLineStatus & SP_LSR_RDY) {
+ cChar = (*getReg)(pNS16550, NS16550_RECEIVE_BUFFER);
+ return (int)cChar;
+ }
+ return -1;
+}
+
+/*
+ * ns16550_inbyte_nonblocking_polled
+ *
+ * Console Termios polling input entry point.
+ */
+int ns16550_inbyte_nonblocking_polled(int minor)
+{
+ console_tbl *c = Console_Port_Tbl [minor];
+
+ return ns16550_inch_polled( c );
+}
diff --git a/bsps/shared/dev/serial/serprobe.c b/bsps/shared/dev/serial/serprobe.c
new file mode 100644
index 0000000000..7f8d392452
--- /dev/null
+++ b/bsps/shared/dev/serial/serprobe.c
@@ -0,0 +1,13 @@
+#include <rtems.h>
+#include <libchip/serial.h>
+#include <libchip/sersupp.h>
+
+bool libchip_serial_default_probe(int minor)
+{
+ /*
+ * If the configuration dependent probe has located the device then
+ * assume it is there
+ */
+
+ return true;
+}
diff --git a/bsps/shared/dev/serial/z85c30.c b/bsps/shared/dev/serial/z85c30.c
new file mode 100644
index 0000000000..55df9d3451
--- /dev/null
+++ b/bsps/shared/dev/serial/z85c30.c
@@ -0,0 +1,893 @@
+/*
+ * This file contains the console driver chip level routines for the
+ * Zilog z85c30 chip.
+ *
+ * The Zilog Z8530 is also available as:
+ *
+ * + Intel 82530
+ * + AMD ???
+ *
+ * COPYRIGHT (c) 1998 by Radstone Technology
+ *
+ *
+ * THIS FILE IS PROVIDED TO YOU, THE USER, "AS IS", WITHOUT WARRANTY OF ANY
+ * KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTY OF FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK
+ * AS TO THE QUALITY AND PERFORMANCE OF ALL CODE IN THIS FILE IS WITH YOU.
+ *
+ * You are hereby granted permission to use, copy, modify, and distribute
+ * this file, provided that this notice, plus the above copyright notice
+ * and disclaimer, appears in all copies. Radstone Technology will provide
+ * no support for this code.
+ *
+ * COPYRIGHT (c) 1989-1997.
+ * On-Line Applications Research Corporation (OAR).
+ *
+ * The license and distribution terms for this file may be
+ * found in the file LICENSE in this distribution or at
+ * http://www.rtems.org/license/LICENSE.
+ */
+
+#include <rtems.h>
+#include <rtems/libio.h>
+#include <rtems/score/sysstate.h>
+#include <stdlib.h>
+
+#include <libchip/serial.h>
+#include <libchip/sersupp.h>
+#include "z85c30_p.h"
+
+/*
+ * Flow control is only supported when using interrupts
+ */
+
+const console_flow z85c30_flow_RTSCTS = {
+ z85c30_negate_RTS, /* deviceStopRemoteTx */
+ z85c30_assert_RTS /* deviceStartRemoteTx */
+};
+
+const console_flow z85c30_flow_DTRCTS = {
+ z85c30_negate_DTR, /* deviceStopRemoteTx */
+ z85c30_assert_DTR /* deviceStartRemoteTx */
+};
+
+/*
+ * Exported driver function table
+ */
+
+const console_fns z85c30_fns = {
+ libchip_serial_default_probe, /* deviceProbe */
+ z85c30_open, /* deviceFirstOpen */
+ NULL, /* deviceLastClose */
+ NULL, /* deviceRead */
+ z85c30_write_support_int, /* deviceWrite */
+ z85c30_initialize_interrupts, /* deviceInitialize */
+ z85c30_write_polled, /* deviceWritePolled */
+ NULL, /* deviceSetAttributes */
+ true /* deviceOutputUsesInterrupts */
+};
+
+const console_fns z85c30_fns_polled = {
+ libchip_serial_default_probe, /* deviceProbe */
+ z85c30_open, /* deviceFirstOpen */
+ z85c30_close, /* deviceLastClose */
+ z85c30_inbyte_nonblocking_polled, /* deviceRead */
+ z85c30_write_support_polled, /* deviceWrite */
+ z85c30_init, /* deviceInitialize */
+ z85c30_write_polled, /* deviceWritePolled */
+ NULL, /* deviceSetAttributes */
+ false /* deviceOutputUsesInterrupts */
+};
+
+#if (CPU_SIMPLE_VECTORED_INTERRUPTS == TRUE)
+ extern void set_vector( rtems_isr_entry, rtems_vector_number, int );
+#endif
+
+/*
+ * z85c30_initialize_port
+ *
+ * initialize a z85c30 Port
+ */
+
+Z85C30_STATIC void z85c30_initialize_port(
+ int minor
+)
+{
+ uintptr_t ulCtrlPort;
+ uintptr_t ulBaudDivisor;
+ setRegister_f setReg;
+
+ ulCtrlPort = Console_Port_Tbl[minor]->ulCtrlPort1;
+ setReg = Console_Port_Tbl[minor]->setRegister;
+
+ /*
+ * Using register 4
+ * Set up the clock rate is 16 times the data
+ * rate, 8 bit sync char, 1 stop bit, no parity
+ */
+
+ (*setReg)( ulCtrlPort, SCC_WR0_SEL_WR4, SCC_WR4_1_STOP | SCC_WR4_16_CLOCK );
+
+ /*
+ * Set up for 8 bits/character on receive with
+ * receiver disable via register 3
+ */
+ (*setReg)( ulCtrlPort, SCC_WR0_SEL_WR3, SCC_WR3_RX_8_BITS );
+
+ /*
+ * Set up for 8 bits/character on transmit
+ * with transmitter disable via register 5
+ */
+ (*setReg)( ulCtrlPort, SCC_WR0_SEL_WR5, SCC_WR5_TX_8_BITS );
+
+ /*
+ * Clear misc control bits
+ */
+ (*setReg)( ulCtrlPort, SCC_WR0_SEL_WR10, 0x00 );
+
+ /*
+ * Setup the source of the receive and xmit
+ * clock as BRG output and the transmit clock
+ * as the output source for TRxC pin via register 11
+ */
+ (*setReg)(
+ ulCtrlPort,
+ SCC_WR0_SEL_WR11,
+ SCC_WR11_OUT_BR_GEN | SCC_WR11_TRXC_OI |
+ SCC_WR11_TX_BR_GEN | SCC_WR11_RX_BR_GEN
+ );
+
+ ulBaudDivisor = Z85C30_Baud(
+ (uint32_t) Console_Port_Tbl[minor]->ulClock,
+ (uint32_t) ((uintptr_t)Console_Port_Tbl[minor]->pDeviceParams)
+ );
+
+ /*
+ * Setup the lower 8 bits time constants=1E.
+ * If the time constans=1E, then the desire
+ * baud rate will be equilvalent to 9600, via register 12.
+ */
+ (*setReg)( ulCtrlPort, SCC_WR0_SEL_WR12, ulBaudDivisor & 0xff );
+
+ /*
+ * using register 13
+ * Setup the upper 8 bits time constant
+ */
+ (*setReg)( ulCtrlPort, SCC_WR0_SEL_WR13, (ulBaudDivisor>>8) & 0xff );
+
+ /*
+ * Enable the baud rate generator enable with clock from the
+ * SCC's PCLK input via register 14.
+ */
+ (*setReg)(
+ ulCtrlPort,
+ SCC_WR0_SEL_WR14,
+ SCC_WR14_BR_EN | SCC_WR14_BR_SRC | SCC_WR14_NULL
+ );
+
+ /*
+ * We are only interested in CTS state changes
+ */
+ (*setReg)( ulCtrlPort, SCC_WR0_SEL_WR15, SCC_WR15_CTS_IE );
+
+ /*
+ * Reset errors
+ */
+ (*setReg)( ulCtrlPort, SCC_WR0_SEL_WR0, SCC_WR0_RST_INT );
+
+ (*setReg)( ulCtrlPort, SCC_WR0_SEL_WR0, SCC_WR0_ERR_RST );
+
+ /*
+ * Enable the receiver via register 3
+ */
+ (*setReg)( ulCtrlPort, SCC_WR0_SEL_WR3, SCC_WR3_RX_8_BITS | SCC_WR3_RX_EN );
+
+ /*
+ * Enable the transmitter pins set via register 5.
+ */
+ (*setReg)( ulCtrlPort, SCC_WR0_SEL_WR5, SCC_WR5_TX_8_BITS | SCC_WR5_TX_EN );
+
+ /*
+ * Disable interrupts
+ */
+ (*setReg)( ulCtrlPort, SCC_WR0_SEL_WR1, 0 );
+
+ /*
+ * Reset TX CRC
+ */
+ (*setReg)( ulCtrlPort, SCC_WR0_SEL_WR0, SCC_WR0_RST_TX_CRC );
+
+ /*
+ * Reset interrupts
+ */
+ (*setReg)( ulCtrlPort, SCC_WR0_SEL_WR0, SCC_WR0_RST_INT );
+}
+
+/*
+ * z85c30_open
+ */
+
+Z85C30_STATIC int z85c30_open(
+ int major,
+ int minor,
+ void *arg
+)
+{
+
+ z85c30_initialize_port(minor);
+
+ /*
+ * Assert DTR
+ */
+
+ if (Console_Port_Tbl[minor]->pDeviceFlow !=&z85c30_flow_DTRCTS) {
+ z85c30_assert_DTR(minor);
+ }
+
+ return(RTEMS_SUCCESSFUL);
+}
+
+/*
+ * z85c30_close
+ */
+
+Z85C30_STATIC int z85c30_close(
+ int major,
+ int minor,
+ void *arg
+)
+{
+ /*
+ * Negate DTR
+ */
+
+ if (Console_Port_Tbl[minor]->pDeviceFlow !=&z85c30_flow_DTRCTS) {
+ z85c30_negate_DTR(minor);
+ }
+
+ return(RTEMS_SUCCESSFUL);
+}
+
+/*
+ * z85c30_init
+ */
+
+Z85C30_STATIC void z85c30_init(int minor)
+{
+ uintptr_t ulCtrlPort;
+ z85c30_context *pz85c30Context;
+ setRegister_f setReg;
+ getRegister_f getReg;
+
+ ulCtrlPort = Console_Port_Tbl[minor]->ulCtrlPort1;
+ setReg = Console_Port_Tbl[minor]->setRegister;
+ getReg = Console_Port_Tbl[minor]->getRegister;
+
+ pz85c30Context = (z85c30_context *)malloc(sizeof(z85c30_context));
+
+ Console_Port_Data[minor].pDeviceContext = (void *)pz85c30Context;
+
+ pz85c30Context->ucModemCtrl = SCC_WR5_TX_8_BITS | SCC_WR5_TX_EN;
+
+ if ( ulCtrlPort == Console_Port_Tbl[minor]->ulCtrlPort2 ) {
+ /*
+ * This is channel A
+ */
+ /*
+ * Ensure port state machine is reset
+ */
+ (*getReg)(ulCtrlPort, SCC_WR0_SEL_RD0);
+
+ (*setReg)(ulCtrlPort, SCC_WR0_SEL_WR9, SCC_WR9_CH_A_RST);
+
+ } else {
+ /*
+ * This is channel B
+ */
+ /*
+ * Ensure port state machine is reset
+ */
+ (*getReg)(ulCtrlPort, SCC_WR0_SEL_RD0);
+
+ (*setReg)(ulCtrlPort, SCC_WR0_SEL_WR9, SCC_WR9_CH_B_RST);
+ }
+}
+
+/*
+ * These routines provide control of the RTS and DTR lines
+ */
+
+/*
+ * z85c30_assert_RTS
+ */
+
+Z85C30_STATIC int z85c30_assert_RTS(int minor)
+{
+ rtems_interrupt_level Irql;
+ z85c30_context *pz85c30Context;
+ setRegister_f setReg;
+
+ setReg = Console_Port_Tbl[minor]->setRegister;
+
+ pz85c30Context = (z85c30_context *) Console_Port_Data[minor].pDeviceContext;
+
+ /*
+ * Assert RTS
+ */
+
+ rtems_interrupt_disable(Irql);
+ pz85c30Context->ucModemCtrl|=SCC_WR5_RTS;
+ (*setReg)(
+ Console_Port_Tbl[minor]->ulCtrlPort1,
+ SCC_WR0_SEL_WR5,
+ pz85c30Context->ucModemCtrl
+ );
+ rtems_interrupt_enable(Irql);
+ return 0;
+}
+
+/*
+ * z85c30_negate_RTS
+ */
+
+Z85C30_STATIC int z85c30_negate_RTS(int minor)
+{
+ rtems_interrupt_level Irql;
+ z85c30_context *pz85c30Context;
+ setRegister_f setReg;
+
+ setReg = Console_Port_Tbl[minor]->setRegister;
+
+ pz85c30Context = (z85c30_context *) Console_Port_Data[minor].pDeviceContext;
+
+ /*
+ * Negate RTS
+ */
+
+ rtems_interrupt_disable(Irql);
+ pz85c30Context->ucModemCtrl&=~SCC_WR5_RTS;
+ (*setReg)(
+ Console_Port_Tbl[minor]->ulCtrlPort1,
+ SCC_WR0_SEL_WR5,
+ pz85c30Context->ucModemCtrl
+ );
+ rtems_interrupt_enable(Irql);
+ return 0;
+}
+
+/*
+ * These flow control routines utilise a connection from the local DTR
+ * line to the remote CTS line
+ */
+
+/*
+ * z85c30_assert_DTR
+ */
+
+Z85C30_STATIC int z85c30_assert_DTR(int minor)
+{
+ rtems_interrupt_level Irql;
+ z85c30_context *pz85c30Context;
+ setRegister_f setReg;
+
+ setReg = Console_Port_Tbl[minor]->setRegister;
+
+ pz85c30Context = (z85c30_context *) Console_Port_Data[minor].pDeviceContext;
+
+ /*
+ * Assert DTR
+ */
+
+ rtems_interrupt_disable(Irql);
+ pz85c30Context->ucModemCtrl|=SCC_WR5_DTR;
+ (*setReg)(
+ Console_Port_Tbl[minor]->ulCtrlPort1,
+ SCC_WR0_SEL_WR5,
+ pz85c30Context->ucModemCtrl
+ );
+ rtems_interrupt_enable(Irql);
+ return 0;
+}
+
+/*
+ * z85c30_negate_DTR
+ */
+
+Z85C30_STATIC int z85c30_negate_DTR(int minor)
+{
+ rtems_interrupt_level Irql;
+ z85c30_context *pz85c30Context;
+ setRegister_f setReg;
+
+ setReg = Console_Port_Tbl[minor]->setRegister;
+
+ pz85c30Context = (z85c30_context *) Console_Port_Data[minor].pDeviceContext;
+
+ /*
+ * Negate DTR
+ */
+
+ rtems_interrupt_disable(Irql);
+ pz85c30Context->ucModemCtrl&=~SCC_WR5_DTR;
+ (*setReg)(
+ Console_Port_Tbl[minor]->ulCtrlPort1,
+ SCC_WR0_SEL_WR5,
+ pz85c30Context->ucModemCtrl
+ );
+ rtems_interrupt_enable(Irql);
+ return 0;
+}
+
+/*
+ * z85c30_set_attributes
+ *
+ * This function sets the SCC channel to reflect the requested termios
+ * port settings.
+ */
+
+Z85C30_STATIC int z85c30_set_attributes(
+ int minor,
+ const struct termios *t
+)
+{
+ uintptr_t ulCtrlPort;
+ uint32_t ulBaudDivisor;
+ uint32_t wr3;
+ uint32_t wr4;
+ uint32_t wr5;
+ int baud_requested;
+ uint32_t baud_number;
+ setRegister_f setReg;
+ rtems_interrupt_level Irql;
+
+ ulCtrlPort = Console_Port_Tbl[minor]->ulCtrlPort1;
+ setReg = Console_Port_Tbl[minor]->setRegister;
+
+ /*
+ * Calculate the baud rate divisor
+ *
+ * Assert ensures there is no division by 0.
+ */
+
+ baud_requested = t->c_ospeed;
+ if (!baud_requested)
+ baud_requested = B9600; /* default to 9600 baud */
+
+ baud_number = (uint32_t) rtems_termios_baud_to_number( baud_requested );
+ _Assert( baud_number != 0 );
+
+ ulBaudDivisor = Z85C30_Baud(
+ (uint32_t) Console_Port_Tbl[minor]->ulClock,
+ baud_number
+ );
+
+ wr3 = SCC_WR3_RX_EN;
+ wr4 = SCC_WR4_16_CLOCK;
+ wr5 = SCC_WR5_TX_EN;
+
+ /*
+ * Parity
+ */
+
+ if (t->c_cflag & PARENB) {
+ wr4 |= SCC_WR4_PAR_EN;
+ if (!(t->c_cflag & PARODD))
+ wr4 |= SCC_WR4_PAR_EVEN;
+ }
+
+ /*
+ * Character Size
+ */
+
+ if (t->c_cflag & CSIZE) {
+ switch (t->c_cflag & CSIZE) {
+ case CS5: break;
+ case CS6: wr3 |= SCC_WR3_RX_6_BITS; wr5 |= SCC_WR5_TX_6_BITS; break;
+ case CS7: wr3 |= SCC_WR3_RX_7_BITS; wr5 |= SCC_WR5_TX_7_BITS; break;
+ case CS8: wr3 |= SCC_WR3_RX_8_BITS; wr5 |= SCC_WR5_TX_8_BITS; break;
+ }
+ } else {
+ wr3 |= SCC_WR3_RX_8_BITS; /* default to 9600,8,N,1 */
+ wr5 |= SCC_WR5_TX_8_BITS; /* default to 9600,8,N,1 */
+ }
+
+ /*
+ * Stop Bits
+ */
+
+ if (t->c_cflag & CSTOPB) {
+ wr4 |= SCC_WR4_2_STOP; /* 2 stop bits */
+ } else {
+ wr4 |= SCC_WR4_1_STOP; /* 1 stop bits */
+ }
+
+ /*
+ * Now actually set the chip
+ */
+
+ rtems_interrupt_disable(Irql);
+ (*setReg)( ulCtrlPort, SCC_WR0_SEL_WR4, wr4 );
+ (*setReg)( ulCtrlPort, SCC_WR0_SEL_WR3, wr3 );
+ (*setReg)( ulCtrlPort, SCC_WR0_SEL_WR5, wr5 );
+
+ /*
+ * Setup the lower 8 bits time constants=1E.
+ * If the time constans=1E, then the desire
+ * baud rate will be equilvalent to 9600, via register 12.
+ */
+
+ (*setReg)( ulCtrlPort, SCC_WR0_SEL_WR12, ulBaudDivisor & 0xff );
+
+ /*
+ * using register 13
+ * Setup the upper 8 bits time constant
+ */
+
+ (*setReg)( ulCtrlPort, SCC_WR0_SEL_WR13, (ulBaudDivisor>>8) & 0xff );
+
+ rtems_interrupt_enable(Irql);
+
+ return 0;
+}
+
+/*
+ * z85c30_process
+ *
+ * This is the per port ISR handler.
+ */
+
+Z85C30_STATIC void z85c30_process(
+ int minor,
+ uint8_t ucIntPend
+)
+{
+ uint32_t ulCtrlPort;
+ volatile uint8_t z85c30_status;
+ char cChar;
+ setRegister_f setReg;
+ getRegister_f getReg;
+
+ ulCtrlPort = Console_Port_Tbl[minor]->ulCtrlPort1;
+ setReg = Console_Port_Tbl[minor]->setRegister;
+ getReg = Console_Port_Tbl[minor]->getRegister;
+
+ /*
+ * Deal with any received characters
+ */
+
+ while (ucIntPend&SCC_RR3_B_RX_IP)
+ {
+ z85c30_status = (*getReg)(ulCtrlPort, SCC_WR0_SEL_RD0);
+ if (!Z85C30_Status_Is_RX_character_available(z85c30_status)) {
+ break;
+ }
+
+ /*
+ * Return the character read.
+ */
+
+ cChar = (*getReg)(ulCtrlPort, SCC_WR0_SEL_RD8);
+
+ rtems_termios_enqueue_raw_characters(
+ Console_Port_Data[minor].termios_data,
+ &cChar,
+ 1
+ );
+ }
+
+ /*
+ * There could be a race condition here if there is not yet a TX
+ * interrupt pending but the buffer is empty. This condition has
+ * been seen before on other z8530 drivers but has not been seen
+ * with this one. The typical solution is to use "vector includes
+ * status" or to only look at the interrupts actually pending
+ * in RR3.
+ */
+
+ while (true) {
+ z85c30_status = (*getReg)(ulCtrlPort, SCC_WR0_SEL_RD0);
+ if (!Z85C30_Status_Is_TX_buffer_empty(z85c30_status)) {
+ /*
+ * We'll get another interrupt when
+ * the transmitter holding reg. becomes
+ * free again and we are clear to send
+ */
+ break;
+ }
+
+#if 0
+ if (!Z85C30_Status_Is_CTS_asserted(z85c30_status)) {
+ /*
+ * We can't transmit yet
+ */
+ (*setReg)(ulCtrlPort, SCC_WR0_SEL_WR0, SCC_WR0_RST_TX_INT);
+ /*
+ * The next state change of CTS will wake us up
+ */
+ break;
+ }
+#endif
+
+ rtems_termios_dequeue_characters(Console_Port_Data[minor].termios_data, 1);
+ if (rtems_termios_dequeue_characters(
+ Console_Port_Data[minor].termios_data, 1)) {
+ if (Console_Port_Tbl[minor]->pDeviceFlow != &z85c30_flow_RTSCTS) {
+ z85c30_negate_RTS(minor);
+ }
+ Console_Port_Data[minor].bActive = FALSE;
+ z85c30_enable_interrupts(minor, SCC_ENABLE_ALL_INTR_EXCEPT_TX);
+ (*setReg)(ulCtrlPort, SCC_WR0_SEL_WR0, SCC_WR0_RST_TX_INT);
+ break;
+ }
+
+ }
+
+ if (ucIntPend & SCC_RR3_B_EXT_IP) {
+ /*
+ * Clear the external status interrupt
+ */
+ (*setReg)(ulCtrlPort, SCC_WR0_SEL_WR0, SCC_WR0_RST_INT);
+ z85c30_status = (*getReg)(ulCtrlPort, SCC_WR0_SEL_RD0);
+ }
+
+ /*
+ * Reset interrupts
+ */
+ (*setReg)(ulCtrlPort, SCC_WR0_SEL_WR0, SCC_WR0_RST_HI_IUS);
+}
+
+/*
+ * z85c30_isr
+ *
+ * This is the ISR handler for each Z8530.
+ */
+
+Z85C30_STATIC rtems_isr z85c30_isr(
+ rtems_vector_number vector
+)
+{
+ int minor;
+ uint32_t ulCtrlPort;
+ volatile uint8_t ucIntPend;
+ volatile uint8_t ucIntPendPort;
+ getRegister_f getReg;
+
+ for (minor=0;minor<Console_Port_Count;minor++) {
+ if(Console_Port_Tbl[minor]->ulIntVector == vector &&
+ Console_Port_Tbl[minor]->deviceType == SERIAL_Z85C30 ) {
+ ulCtrlPort = Console_Port_Tbl[minor]->ulCtrlPort2;
+ getReg = Console_Port_Tbl[minor]->getRegister;
+ do {
+ ucIntPend = (*getReg)(ulCtrlPort, SCC_WR0_SEL_RD3);
+
+ /*
+ * If this is channel A select channel A status
+ */
+
+ if (ulCtrlPort == Console_Port_Tbl[minor]->ulCtrlPort1) {
+ ucIntPendPort = ucIntPend >> 3;
+ ucIntPendPort &= 7;
+ } else {
+ ucIntPendPort = ucIntPend &= 7;
+ }
+
+ if (ucIntPendPort) {
+ z85c30_process(minor, ucIntPendPort);
+ }
+ } while (ucIntPendPort);
+ }
+ }
+}
+
+/*
+ * z85c30_enable_interrupts
+ *
+ * This routine enables the specified interrupts for this minor.
+ */
+
+Z85C30_STATIC void z85c30_enable_interrupts(
+ int minor,
+ int interrupt_mask
+)
+{
+ uint32_t ulCtrlPort;
+ setRegister_f setReg;
+
+ ulCtrlPort = Console_Port_Tbl[minor]->ulCtrlPort1;
+ setReg = Console_Port_Tbl[minor]->setRegister;
+
+ (*setReg)(ulCtrlPort, SCC_WR0_SEL_WR1, interrupt_mask);
+}
+
+/*
+ * z85c30_initialize_interrupts
+ *
+ * This routine initializes the port to use interrupts.
+ */
+
+Z85C30_STATIC void z85c30_initialize_interrupts(
+ int minor
+)
+{
+ uint32_t ulCtrlPort1;
+ setRegister_f setReg;
+
+ ulCtrlPort1 = Console_Port_Tbl[minor]->ulCtrlPort1;
+ setReg = Console_Port_Tbl[minor]->setRegister;
+
+
+ z85c30_init(minor);
+
+ Console_Port_Data[minor].bActive=FALSE;
+
+ z85c30_initialize_port( minor );
+
+ if (Console_Port_Tbl[minor]->pDeviceFlow != &z85c30_flow_RTSCTS) {
+ z85c30_negate_RTS(minor);
+ }
+
+#if (CPU_SIMPLE_VECTORED_INTERRUPTS == TRUE)
+ set_vector(z85c30_isr, Console_Port_Tbl[minor]->ulIntVector, 1);
+#endif
+
+ z85c30_enable_interrupts(minor, SCC_ENABLE_ALL_INTR_EXCEPT_TX);
+
+ (*setReg)(ulCtrlPort1, SCC_WR0_SEL_WR2, 0); /* XXX vector */
+ (*setReg)(ulCtrlPort1, SCC_WR0_SEL_WR9, SCC_WR9_MIE);
+
+ /*
+ * Reset interrupts
+ */
+
+ (*setReg)(ulCtrlPort1, SCC_WR0_SEL_WR0, SCC_WR0_RST_INT);
+}
+
+/*
+ * z85c30_write_support_int
+ *
+ * Console Termios output entry point.
+ *
+ */
+
+Z85C30_STATIC ssize_t z85c30_write_support_int(
+ int minor,
+ const char *buf,
+ size_t len)
+{
+ uint32_t Irql;
+ uint32_t ulCtrlPort;
+ setRegister_f setReg;
+
+ ulCtrlPort = Console_Port_Tbl[minor]->ulCtrlPort1;
+ setReg = Console_Port_Tbl[minor]->setRegister;
+
+ /*
+ * We are using interrupt driven output and termios only sends us
+ * one character at a time.
+ */
+
+ if ( !len )
+ return 0;
+
+ /*
+ * Put the character out and enable interrupts if necessary.
+ */
+
+ if (Console_Port_Tbl[minor]->pDeviceFlow != &z85c30_flow_RTSCTS) {
+ z85c30_assert_RTS(minor);
+ }
+ rtems_interrupt_disable(Irql);
+ if ( Console_Port_Data[minor].bActive == FALSE) {
+ Console_Port_Data[minor].bActive = TRUE;
+ z85c30_enable_interrupts(minor, SCC_ENABLE_ALL_INTR);
+ }
+ (*setReg)(ulCtrlPort, SCC_WR0_SEL_WR8, *buf);
+ rtems_interrupt_enable(Irql);
+
+ return 0;
+}
+
+/*
+ * z85c30_inbyte_nonblocking_polled
+ *
+ * This routine polls for a character.
+ */
+
+Z85C30_STATIC int z85c30_inbyte_nonblocking_polled(
+ int minor
+)
+{
+ volatile uint8_t z85c30_status;
+ uint32_t ulCtrlPort;
+ getRegister_f getReg;
+
+ ulCtrlPort = Console_Port_Tbl[minor]->ulCtrlPort1;
+ getReg = Console_Port_Tbl[minor]->getRegister;
+
+ /*
+ * return -1 if a character is not available.
+ */
+ z85c30_status = (*getReg)(ulCtrlPort, SCC_WR0_SEL_RD0);
+ if (!Z85C30_Status_Is_RX_character_available(z85c30_status)) {
+ return -1;
+ }
+
+ /*
+ * Return the character read.
+ */
+
+ return (*getReg)(ulCtrlPort, SCC_WR0_SEL_RD8);
+}
+
+/*
+ * z85c30_write_support_polled
+ *
+ * Console Termios output entry point.
+ *
+ */
+
+Z85C30_STATIC ssize_t z85c30_write_support_polled(
+ int minor,
+ const char *buf,
+ size_t len)
+{
+ int nwrite=0;
+
+ /*
+ * poll each byte in the string out of the port.
+ */
+ while (nwrite < len) {
+ z85c30_write_polled(minor, *buf++);
+ nwrite++;
+ }
+
+ /*
+ * return the number of bytes written.
+ */
+ return nwrite;
+}
+
+/*
+ * z85c30_write_polled
+ *
+ * This routine transmits a character using polling.
+ */
+
+Z85C30_STATIC void z85c30_write_polled(
+ int minor,
+ char cChar
+)
+{
+ volatile uint8_t z85c30_status;
+ uint32_t ulCtrlPort;
+ getRegister_f getReg;
+ setRegister_f setReg;
+
+ ulCtrlPort = Console_Port_Tbl[minor]->ulCtrlPort1;
+ getReg = Console_Port_Tbl[minor]->getRegister;
+ setReg = Console_Port_Tbl[minor]->setRegister;
+
+ /*
+ * Wait for the Transmit buffer to indicate that it is empty.
+ */
+
+ z85c30_status = (*getReg)( ulCtrlPort, SCC_WR0_SEL_RD0 );
+
+ while (!Z85C30_Status_Is_TX_buffer_empty(z85c30_status)) {
+ /*
+ * Yield while we wait
+ */
+#if 0
+ if (_System_state_Is_up(_System_state_Get())) {
+ rtems_task_wake_after(RTEMS_YIELD_PROCESSOR);
+ }
+#endif
+ z85c30_status = (*getReg)(ulCtrlPort, SCC_WR0_SEL_RD0);
+ }
+
+ /*
+ * Write the character.
+ */
+
+ (*setReg)( ulCtrlPort, SCC_WR0_SEL_WR8, cChar );
+}
diff --git a/bsps/shared/dev/serial/z85c30_p.h b/bsps/shared/dev/serial/z85c30_p.h
new file mode 100644
index 0000000000..af2ed6507c
--- /dev/null
+++ b/bsps/shared/dev/serial/z85c30_p.h
@@ -0,0 +1,420 @@
+/*
+ * This include file contains all private driver definitions for the
+ * Zilog z85c30.
+ *
+ * COPYRIGHT (c) 1998 by Radstone Technology
+ *
+ *
+ * THIS FILE IS PROVIDED TO YOU, THE USER, "AS IS", WITHOUT WARRANTY OF ANY
+ * KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTY OF FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK
+ * AS TO THE QUALITY AND PERFORMANCE OF ALL CODE IN THIS FILE IS WITH YOU.
+ *
+ * You are hereby granted permission to use, copy, modify, and distribute
+ * this file, provided that this notice, plus the above copyright notice
+ * and disclaimer, appears in all copies. Radstone Technology will provide
+ * no support for this code.
+ *
+ * COPYRIGHT (c) 1989-1997.
+ * On-Line Applications Research Corporation (OAR).
+ *
+ * The license and distribution terms for this file may in
+ * the file LICENSE in this distribution or at
+ * http://www.rtems.org/license/LICENSE.
+ */
+
+#ifndef __Z85C30_P_H
+#define __Z85C30_P_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Define Z85C30_STATIC to nothing while debugging so the entry points
+ * will show up in the symbol table.
+ */
+
+#define Z85C30_STATIC
+
+/* #define Z85C30_STATIC static */
+
+/* bit values for write register 0 */
+/* command register */
+
+#define SCC_WR0_SEL_WR0 0x00
+#define SCC_WR0_SEL_WR1 0x01
+#define SCC_WR0_SEL_WR2 0x02
+#define SCC_WR0_SEL_WR3 0x03
+#define SCC_WR0_SEL_WR4 0x04
+#define SCC_WR0_SEL_WR5 0x05
+#define SCC_WR0_SEL_WR6 0x06
+#define SCC_WR0_SEL_WR7 0x07
+#define SCC_WR0_SEL_WR8 0x08
+#define SCC_WR0_SEL_WR9 0x09
+#define SCC_WR0_SEL_WR10 0x0a
+#define SCC_WR0_SEL_WR11 0x0b
+#define SCC_WR0_SEL_WR12 0x0c
+#define SCC_WR0_SEL_WR13 0x0d
+#define SCC_WR0_SEL_WR14 0x0e
+#define SCC_WR0_SEL_WR15 0x0f
+#define SCC_WR0_SEL_RD0 0x00
+#define SCC_WR0_SEL_RD1 0x01
+#define SCC_WR0_SEL_RD2 0x02
+#define SCC_WR0_SEL_RD3 0x03
+#define SCC_WR0_SEL_RD4 0x04
+#define SCC_WR0_SEL_RD5 0x05
+#define SCC_WR0_SEL_RD6 0x06
+#define SCC_WR0_SEL_RD7 0x07
+#define SCC_WR0_SEL_RD8 0x08
+#define SCC_WR0_SEL_RD9 0x09
+#define SCC_WR0_SEL_RD10 0x0a
+#define SCC_WR0_SEL_RD11 0x0b
+#define SCC_WR0_SEL_RD12 0x0c
+#define SCC_WR0_SEL_RD13 0x0d
+#define SCC_WR0_SEL_RD14 0x0e
+#define SCC_WR0_SEL_RD15 0x0f
+#define SCC_WR0_NULL_CODE 0x00
+#define SCC_WR0_RST_INT 0x10
+#define SCC_WR0_SEND_ABORT 0x18
+#define SCC_WR0_EN_INT_RX 0x20
+#define SCC_WR0_RST_TX_INT 0x28
+#define SCC_WR0_ERR_RST 0x30
+#define SCC_WR0_RST_HI_IUS 0x38
+#define SCC_WR0_RST_RX_CRC 0x40
+#define SCC_WR0_RST_TX_CRC 0x80
+#define SCC_WR0_RST_TX_UND 0xc0
+
+/* write register 2 */
+/* interrupt vector */
+
+/* bit values for write register 1 */
+/* tx/rx interrupt and data transfer mode definition */
+
+#define SCC_WR1_EXT_INT_EN 0x01
+#define SCC_WR1_TX_INT_EN 0x02
+#define SCC_WR1_PARITY 0x04
+#define SCC_WR1_RX_INT_DIS 0x00
+#define SCC_WR1_RX_INT_FIR 0x08
+#define SCC_WR1_INT_ALL_RX 0x10
+#define SCC_WR1_RX_INT_SPE 0x18
+#define SCC_WR1_RDMA_RECTR 0x20
+#define SCC_WR1_RDMA_FUNC 0x40
+#define SCC_WR1_RDMA_EN 0x80
+
+#define SCC_ENABLE_ALL_INTR \
+ (SCC_WR1_EXT_INT_EN | SCC_WR1_TX_INT_EN | SCC_WR1_INT_ALL_RX)
+
+#define SCC_DISABLE_ALL_INTR 0x00
+
+#define SCC_ENABLE_ALL_INTR_EXCEPT_TX \
+ (SCC_WR1_EXT_INT_EN | SCC_WR1_INT_ALL_RX)
+
+/* bit values for write register 3 */
+/* receive parameters and control */
+
+#define SCC_WR3_RX_EN 0x01
+#define SCC_WR3_SYNC_CHAR 0x02
+#define SCC_WR3_ADR_SEARCH 0x04
+#define SCC_WR3_RX_CRC_EN 0x08
+#define SCC_WR3_ENTER_HUNT 0x10
+#define SCC_WR3_AUTO_EN 0x20
+#define SCC_WR3_RX_5_BITS 0x00
+#define SCC_WR3_RX_7_BITS 0x40
+#define SCC_WR3_RX_6_BITS 0x80
+#define SCC_WR3_RX_8_BITS 0xc0
+
+/* bit values for write register 4 */
+/* tx/rx misc parameters and modes */
+
+#define SCC_WR4_PAR_EN 0x01
+#define SCC_WR4_PAR_EVEN 0x02
+#define SCC_WR4_SYNC_EN 0x00
+#define SCC_WR4_1_STOP 0x04
+#define SCC_WR4_2_STOP 0x0c
+#define SCC_WR4_8_SYNC 0x00
+#define SCC_WR4_16_SYNC 0x10
+#define SCC_WR4_SDLC 0x20
+#define SCC_WR4_EXT_SYNC 0x30
+#define SCC_WR4_1_CLOCK 0x00
+#define SCC_WR4_16_CLOCK 0x40
+#define SCC_WR4_32_CLOCK 0x80
+#define SCC_WR4_64_CLOCK 0xc0
+
+/* bit values for write register 5 */
+/* transmit parameter and controls */
+
+#define SCC_WR5_TX_CRC_EN 0x01
+#define SCC_WR5_RTS 0x02
+#define SCC_WR5_SDLC 0x04
+#define SCC_WR5_TX_EN 0x08
+#define SCC_WR5_SEND_BRK 0x10
+
+#define SCC_WR5_TX_5_BITS 0x00
+#define SCC_WR5_TX_7_BITS 0x20
+#define SCC_WR5_TX_6_BITS 0x40
+#define SCC_WR5_TX_8_BITS 0x60
+#define SCC_WR5_DTR 0x80
+
+/* write register 6 */
+/* sync chars or sdlc address field */
+
+/* write register 7 */
+/* sync char or sdlc flag */
+
+/* write register 8 */
+/* transmit buffer */
+
+/* bit values for write register 9 */
+/* master interrupt control */
+
+#define SCC_WR9_VIS 0x01
+#define SCC_WR9_NV 0x02
+#define SCC_WR9_DLC 0x04
+#define SCC_WR9_MIE 0x08
+#define SCC_WR9_STATUS_HI 0x10
+#define SCC_WR9_NO_RST 0x00
+#define SCC_WR9_CH_B_RST 0x40
+#define SCC_WR9_CH_A_RST 0x80
+#define SCC_WR9_HDWR_RST 0xc0
+
+/* bit values for write register 10 */
+/* misc tx/rx control bits */
+
+#define SCC_WR10_6_BIT_SYNC 0x01
+#define SCC_WR10_LOOP_MODE 0x02
+#define SCC_WR10_ABORT_UND 0x04
+#define SCC_WR10_MARK_IDLE 0x08
+#define SCC_WR10_ACT_POLL 0x10
+#define SCC_WR10_NRZ 0x00
+#define SCC_WR10_NRZI 0x20
+#define SCC_WR10_FM1 0x40
+#define SCC_WR10_FM0 0x60
+#define SCC_WR10_CRC_PRESET 0x80
+
+/* bit values for write register 11 */
+/* clock mode control */
+
+#define SCC_WR11_OUT_XTAL 0x00
+#define SCC_WR11_OUT_TX_CLK 0x01
+#define SCC_WR11_OUT_BR_GEN 0x02
+#define SCC_WR11_OUT_DPLL 0x03
+#define SCC_WR11_TRXC_OI 0x04
+#define SCC_WR11_TX_RTXC 0x00
+#define SCC_WR11_TX_TRXC 0x08
+#define SCC_WR11_TX_BR_GEN 0x10
+#define SCC_WR11_TX_DPLL 0x18
+#define SCC_WR11_RX_RTXC 0x00
+#define SCC_WR11_RX_TRXC 0x20
+#define SCC_WR11_RX_BR_GEN 0x40
+#define SCC_WR11_RX_DPLL 0x60
+#define SCC_WR11_RTXC_XTAL 0x80
+
+/* write register 12 */
+/* lower byte of baud rate generator time constant */
+
+/* write register 13 */
+/* upper byte of baud rate generator time constant */
+
+/* bit values for write register 14 */
+/* misc control bits */
+
+#define SCC_WR14_BR_EN 0x01
+#define SCC_WR14_BR_SRC 0x02
+#define SCC_WR14_DTR_FUNC 0x04
+#define SCC_WR14_AUTO_ECHO 0x08
+#define SCC_WR14_LCL_LOOP 0x10
+#define SCC_WR14_NULL 0x00
+#define SCC_WR14_SEARCH 0x20
+#define SCC_WR14_RST_CLK 0x40
+#define SCC_WR14_DIS_DPLL 0x60
+#define SCC_WR14_SRC_BR 0x80
+#define SCC_WR14_SRC_RTXC 0xa0
+#define SCC_WR14_FM_MODE 0xc0
+#define SCC_WR14_NRZI 0xe0
+
+/* bit values for write register 15 */
+/* external/status interrupt control */
+
+#define SCC_WR15_ZERO_CNT 0x02
+#define SCC_WR15_CD_IE 0x08
+#define SCC_WR15_SYNC_IE 0x10
+#define SCC_WR15_CTS_IE 0x20
+#define SCC_WR15_TX_UND_IE 0x40
+#define SCC_WR15_BREAK_IE 0x80
+
+/* bit values for read register 0 */
+/* tx/rx buffer status and external status */
+
+#define SCC_RR0_RX_AVAIL 0x01
+#define SCC_RR0_ZERO_CNT 0x02
+#define SCC_RR0_TX_EMPTY 0x04
+#define SCC_RR0_CD 0x08
+#define SCC_RR0_SYNC 0x10
+#define SCC_RR0_CTS 0x20
+#define SCC_RR0_TX_UND 0x40
+#define SCC_RR0_BREAK 0x80
+
+/* bit values for read register 1 */
+
+#define SCC_RR1_ALL_SENT 0x01
+#define SCC_RR1_RES_CD_2 0x02
+#define SCC_RR1_RES_CD_1 0x01
+#define SCC_RR1_RES_CD_0 0x08
+#define SCC_RR1_PAR_ERR 0x10
+#define SCC_RR1_RX_OV_ERR 0x20
+#define SCC_RR1_CRC_ERR 0x40
+#define SCC_RR1_END_FRAME 0x80
+
+/* read register 2 */
+/* interrupt vector */
+
+/* bit values for read register 3 */
+/* interrupt pending register */
+
+#define SCC_RR3_B_EXT_IP 0x01
+#define SCC_RR3_B_TX_IP 0x02
+#define SCC_RR3_B_RX_IP 0x04
+#define SCC_RR3_A_EXT_IP 0x08
+#define SCC_RR3_A_TX_IP 0x10
+#define SCC_RR3_A_RX_IP 0x20
+
+/* read register 8 */
+/* receive data register */
+
+/* bit values for read register 10 */
+/* misc status bits */
+
+#define SCC_RR10_ON_LOOP 0x02
+#define SCC_RR10_LOOP_SEND 0x10
+#define SCC_RR10_2_CLK_MIS 0x40
+#define SCC_RR10_1_CLK_MIS 0x80
+
+/* read register 12 */
+/* lower byte of time constant */
+
+/* read register 13 */
+/* upper byte of time constant */
+
+/* bit values for read register 15 */
+/* external/status ie bits */
+
+#define SCC_RR15_ZERO_CNT 0x02
+#define SCC_RR15_CD_IE 0x08
+#define SCC_RR15_SYNC_IE 0x10
+#define SCC_RR15_CTS_IE 0x20
+#define SCC_RR15_TX_UND_IE 0x40
+#define SCC_RR15_BREAK_IE 0x80
+
+typedef struct _z85c30_context
+{
+ uint8_t ucModemCtrl;
+} z85c30_context;
+
+/*
+ * The following macro calculates the Baud constant. For the Z85C30 chip.
+ *
+ * Note: baud constant = ((clock frequency / Clock_X) / (2 * Baud Rate)) - 2
+ * eg ((10,000,000 / 16) / (2 * Baud Rate)) - 2
+ */
+
+#define Z85C30_Baud( _clock, _baud_rate ) \
+ ( ((_clock) /( 16 * 2 * _baud_rate)) - 2)
+
+#define Z85C30_Status_Is_RX_character_available(_status) \
+ ((_status) & SCC_RR0_RX_AVAIL)
+
+#define Z85C30_Status_Is_TX_buffer_empty(_status) \
+ ((_status) & SCC_RR0_TX_EMPTY)
+
+#define Z85C30_Status_Is_CTS_asserted(_status) \
+ ((_status) & SCC_RR0_CTS)
+
+#define Z85C30_Status_Is_break_abort(_status) \
+ ((_status) & SCC_RR0_BREAK)
+
+/*
+ * Private routines
+ */
+
+Z85C30_STATIC void z85c30_initialize_port(
+ int minor
+);
+
+Z85C30_STATIC void z85c30_init(int minor);
+
+Z85C30_STATIC int z85c30_set_attributes(
+ int minor,
+ const struct termios *t
+);
+
+Z85C30_STATIC int z85c30_open(
+ int major,
+ int minor,
+ void * arg
+);
+
+Z85C30_STATIC int z85c30_close(
+ int major,
+ int minor,
+ void * arg
+);
+
+Z85C30_STATIC void z85c30_write_polled(
+ int minor,
+ char cChar
+);
+
+Z85C30_STATIC int z85c30_assert_RTS(
+ int minor
+);
+
+Z85C30_STATIC int z85c30_negate_RTS(
+ int minor
+);
+
+Z85C30_STATIC int z85c30_assert_DTR(
+ int minor
+);
+
+Z85C30_STATIC int z85c30_negate_DTR(
+ int minor
+);
+
+Z85C30_STATIC void z85c30_initialize_interrupts(int minor);
+
+Z85C30_STATIC ssize_t z85c30_write_support_int(
+ int minor,
+ const char *buf,
+ size_t len
+);
+
+Z85C30_STATIC ssize_t z85c30_write_support_polled(
+ int minor,
+ const char *buf,
+ size_t len
+);
+
+Z85C30_STATIC int z85c30_inbyte_nonblocking_polled(
+ int minor
+);
+
+Z85C30_STATIC void z85c30_enable_interrupts(
+ int minor,
+ int interrupt_mask
+);
+
+Z85C30_STATIC void z85c30_process(
+ int minor,
+ uint8_t ucIntPend
+);
+
+Z85C30_STATIC rtems_isr z85c30_isr(
+ rtems_vector_number vector
+);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/bsps/shared/dev/serial/z85c30_reg.c b/bsps/shared/dev/serial/z85c30_reg.c
new file mode 100644
index 0000000000..6e7b5d3494
--- /dev/null
+++ b/bsps/shared/dev/serial/z85c30_reg.c
@@ -0,0 +1,72 @@
+/*
+ * This file contains a typical set of register access routines which may be
+ * used with the z85c30 chip if accesses to the chip are as follows:
+ *
+ * + registers are accessed as bytes
+ *
+ * COPYRIGHT (c) 1989-1997.
+ * On-Line Applications Research Corporation (OAR).
+ *
+ * The license and distribution terms for this file may be
+ * found in the file LICENSE in this distribution or at
+ * http://www.rtems.org/license/LICENSE.
+ */
+
+#include <rtems.h>
+
+#include <libchip/z85c30.h>
+
+#ifndef _Z85C30_MULTIPLIER
+#define _Z85C30_MULTIPLIER 1
+#define _Z85C30_NAME(_X) _X
+#define _Z85C30_TYPE uint8_t
+#endif
+
+/*
+ * Z85C30 Get Register Routine
+ */
+
+uint8_t _Z85C30_NAME(z85c30_get_register)(
+ uintptr_t ulCtrlPort,
+ uint8_t ucRegNum
+)
+{
+ _Z85C30_TYPE *port;
+ uint8_t data;
+ rtems_interrupt_level level;
+
+ port = (_Z85C30_TYPE *)ulCtrlPort;
+
+ rtems_interrupt_disable(level);
+
+ if(ucRegNum) {
+ *port = ucRegNum;
+ }
+ data = *port;
+ rtems_interrupt_enable(level);
+
+ return data;
+}
+
+/*
+ * Z85C30 Set Register Routine
+ */
+
+void _Z85C30_NAME(z85c30_set_register)(
+ uintptr_t ulCtrlPort,
+ uint8_t ucRegNum,
+ uint8_t ucData
+)
+{
+ _Z85C30_TYPE *port;
+ rtems_interrupt_level level;
+
+ port = (_Z85C30_TYPE *)ulCtrlPort;
+
+ rtems_interrupt_disable(level);
+ if(ucRegNum) {
+ *port = ucRegNum;
+ }
+ *port = ucData;
+ rtems_interrupt_enable(level);
+}
diff --git a/bsps/shared/net/README b/bsps/shared/net/README
new file mode 100644
index 0000000000..ecb996e65f
--- /dev/null
+++ b/bsps/shared/net/README
@@ -0,0 +1,12 @@
+This is the network interface controller portion of the libchip library.
+This directory contains the source code for reusable TCP/IP network driver
+support code. Each driver has its own configuration table and its
+chip specific attach routine must be called by a board specific
+attach routine. The board specific chip routine passes the chip
+configuration and network configuration to the resuable device driver.
+
+The reusable chip drivers do not directly access the controller.
+They access the registers on the controller via a set of
+functions which are provided by the BSP. These functions set and get
+general registers and data buffers.
+
diff --git a/bsps/shared/net/README.3com b/bsps/shared/net/README.3com
new file mode 100644
index 0000000000..b67061dec2
--- /dev/null
+++ b/bsps/shared/net/README.3com
@@ -0,0 +1,3 @@
+#
+# README.3com
+#
diff --git a/bsps/shared/net/README.cs8900 b/bsps/shared/net/README.cs8900
new file mode 100644
index 0000000000..ecd575230f
--- /dev/null
+++ b/bsps/shared/net/README.cs8900
@@ -0,0 +1,26 @@
+Target Support
+==============
+
+The target is required to provide the low level support routines as
+listed in the Configuration section of this file.
+
+The file cs8900.[ch].bsp are an example BSP files for DIMMPC target.
+
+Conditionals
+============
+CS8900_DATA_BUS_SWAPPED - XXX
+
+CS8900_TRACE - XXX
+
+CS8900_VERBOSE - XXX
+
+Todo
+====
++ Build two versions -- one with swapped, one without.
+
++ Document conditionals.
+
+Configuration
+=============
+See the cs8900.h header file for the documentation.
+
diff --git a/bsps/shared/net/README.dec21140 b/bsps/shared/net/README.dec21140
new file mode 100644
index 0000000000..f07bec7a36
--- /dev/null
+++ b/bsps/shared/net/README.dec21140
@@ -0,0 +1,116 @@
+This TULIP driver can be used on BSPs that support PCI bus.
+
+It can handle any DEC21140 and DEC21143 based Ethernet controller
+although the DEC21143 support has only been tested on Intel.
+
+It works on big or little endian target.
+
+The DEC21140 has been tested with powerpc/mcp750 BSP (OnBoard Ethernet
+controller) and i386/pc386 BSP (D-Link DFE-500TX Ethernet board).
+
+The DEC21143 has been tested only on the i386/pc386 using
+the Kingston KNE100TX with 21143PD chip.
+
+*****************************************************************
+******** ***************
+******** tests with ttcp benchmark for DEC driver ***************
+******** optimization ***************
+******** ***************
+*****************************************************************
+
+
+LINUX -> LINUX-ix86
+
+Transmitter :
+
+ttcp-t: buflen=8192, nbuf=2048, align=16384/0, port=5001 tcp -> genesis
+ttcp-t: 16777216 bytes in 1.87 real seconds = 8775.25 KB/sec +++
+ttcp-t: 2048 I/O calls, msec/call = 0.93, calls/sec = 1096.91
+ttcp-t: 0.0user 0.9sys 0:01real 51% 0i+0d 0maxrss 0+2pf 0+0csw
+
+Receiver :
+
+ttcp-r: buflen=8192, nbuf=2048, align=16384/0, port=5001 tcp
+ttcp-r: 16777216 bytes in 1.88 real seconds = 8706.53 KB/sec +++
+ttcp-r: 10802 I/O calls, msec/call = 0.18, calls/sec = 5740.23
+ttcp-r: 0.0user 0.2sys 0:01real 13% 0i+0d 0maxrss 0+2pf 0+0csw
+
+==============================================================
+==============================================================
+==============================================================
+
+LINUX -> RTEMS-ix86 with tulip driver from pc386 bsp
+
+Transmitter :
+
+ttcp-t: buflen=8192, nbuf=2048, align=16384/0, port=5001 tcp -> neil-young-100
+ttcp-t: 16777216 bytes in 1.98 real seconds = 8294.76 KB/sec +++
+ttcp-t: 2048 I/O calls, msec/call = 0.99, calls/sec = 1036.85
+ttcp-t: 0.0user 0.1sys 0:01real 9% 0i+0d 0maxrss 0+2pf 0+0csw
+
+Receiver :
+
+ttcp-r: buflen=8192, nbuf=2048, align=16384/0, port=5001 tcp
+ttcp-r: 16777216 bytes in 2.03 real seconds = 8065.14 KB/sec +++
+ttcp-r: 3088 I/O calls, msec/call = 0.67, calls/sec = 1520.09
+ttcp-r: 0.0user 2.0sys 0:02real 100% 0i+0d 0maxrss 0+0pf 0+0csw
+
+==============================================================
+==============================================================
+==============================================================
+
+RTEMS-ix86 with tulip driver from pc386 bsp -> LINUX
+
+Transmitter :
+
+ttcp-t: buflen=8192, nbuf=2048, align=16384/0, port=5001 tcp -> 194.2.81.126
+ttcp-t: 16777216 bytes in 2.76 real seconds = 5938.77 KB/sec +++
+ttcp-t: 2048 I/O calls, msec/call = 1.38, calls/sec = 742.35
+ttcp-t: 0.0user 2.5sys 0:02real 100% 0i+0d 0maxrss 0+0pf 0+0csw
+
+Receiver :
+
+ttcp-r: buflen=8192, nbuf=2048, align=16384/0, port=5001 tcp
+ttcp-r: 16777216 bytes in 2.75 real seconds = 5948.53 KB/sec +++
+ttcp-r: 11349 I/O calls, msec/call = 0.25, calls/sec = 4120.48
+ttcp-r: 0.0user 0.1sys 0:02real 6% 0i+0d 0maxrss 0+2pf 0+0csw
+
+==============================================================
+==============================================================
+==============================================================
+
+LINUX -> RTEMS-ix86 with optimized tulip driver
+
+Transmitter :
+
+ttcp-t: buflen=8192, nbuf=2048, align=16384/0, port=5001 tcp -> neil-young-100
+ttcp-t: 16777216 bytes in 1.73 real seconds = 9470.13 KB/sec +++
+ttcp-t: 2048 I/O calls, msec/call = 0.87, calls/sec = 1183.77
+ttcp-t: 0.0user 0.1sys 0:01real 6% 0i+0d 0maxrss 0+2pf 0+0csw
+
+Receiver :
+
+ttcp-r: buflen=8192, nbuf=2048, align=16384/0, port=5001 tcp
+ttcp-r: 16777216 bytes in 1.76 real seconds = 9315.33 KB/sec +++
+ttcp-r: 4558 I/O calls, msec/call = 0.40, calls/sec = 2591.51
+ttcp-r: 0.0user 1.7sys 0:01real 100% 0i+0d 0maxrss 0+0pf 0+0csw
+
+==============================================================
+==============================================================
+==============================================================
+
+RTEMS-ix86 with optimized tulip driver -> LINUX
+
+Transmitter :
+
+ttcp-t: buflen=8192, nbuf=2048, align=16384/0, port=5001 tcp -> 194.2.81.126
+ttcp-t: 16777216 bytes in 2.09 real seconds = 7847.80 KB/sec +++
+ttcp-t: 2048 I/O calls, msec/call = 1.04, calls/sec = 980.98
+ttcp-t: 0.0user 2.0sys 0:02real 100% 0i+0d 0maxrss 0+0pf 0+0csw
+
+Receiver :
+
+ttcp-r: buflen=8192, nbuf=2048, align=16384/0, port=5001 tcp
+ttcp-r: 16777216 bytes in 2.08 real seconds = 7874.23 KB/sec +++
+ttcp-r: 11404 I/O calls, msec/call = 0.19, calls/sec = 5480.82
+ttcp-r: 0.0user 0.1sys 0:02real 8% 0i+0d 0maxrss 0+2pf 0+0csw
diff --git a/bsps/shared/net/README.i82586 b/bsps/shared/net/README.i82586
new file mode 100644
index 0000000000..a0990367ef
--- /dev/null
+++ b/bsps/shared/net/README.i82586
@@ -0,0 +1 @@
+TBD
diff --git a/bsps/shared/net/README.open_eth b/bsps/shared/net/README.open_eth
new file mode 100644
index 0000000000..af9d8882cf
--- /dev/null
+++ b/bsps/shared/net/README.open_eth
@@ -0,0 +1,72 @@
+Driver for opencores ethernet MAC - README
+------------------------------------------
+
+The device name for the driver is 'open_eth1', the attach
+function for the leon bsp is rtems_leon_open_eth_driver_attach().
+
+No cache flushing is made when a frame is received. On leon,
+this means that cache snooping must be configured in the
+vhdl model and enabled by software.
+
+TX interrupts are not used and masked in the interrupt mask
+register.
+
+For now, only 10 Mbit/s half-duplex is supported.
+100 Mbit/s operations does not work reliably, the transmitter
+locks up or skips frames. Seems to depend on the TX fifo
+implementation in the opencores MAC. Send a mail to
+jiri@gaisler.com if you know how to fix this.
+
+Tested only on leon, using the GR-PCI-XC2V board @ 40 MHz.
+Output from ttcp receiving 1 Mbyte file:
+
+>>> ttcp -r -s
+ttcp-r: buflen=8192, nbuf=2048, align=16384/0, port=5001 tcp
+ttcp-r: socket
+ttcp-r: accept from 192.168.0.2
+ttcp-r: 1145339 bytes in 1.18 real seconds = 947.88 KB/sec +++
+ttcp-r: 792 I/O calls, msec/call = 1.53, calls/sec = 671.19
+ttcp-r: 0.0user 1.1sys 0:01real 100% 0i+0d 0maxrss 0+0pf 0+0csw
+************ MBUF STATISTICS ************
+mbufs:1024 clusters: 128 free: 112
+drops: 0 waits: 0 drains: 0
+ free:1007 data:17 header:0 socket:0
+ pcb:0 rtable:0 htable:0 atable:0
+ soname:0 soopts:0 ftable:0 rights:0
+ ifaddr:0 control:0 oobdata:0
+
+************ INTERFACE STATISTICS ************
+***** open_eth1 *****
+Address:192.168.0.66 Broadcast Address:192.168.0.255
+Flags: Up Broadcast Running Simplex
+Send queue limit:50 length:0 Dropped:0
+ Rx Packets:796 Rx Interrupts:796 Length:0
+ Bad CRC:0 Overrun:0 Miss:0
+ Tx Interrupts:0 Deferred:0 Missed Hearbeat:0
+ No Carrier:0 Retransmit Limit:0 Late Collision:0
+ Underrun:0 Raw output wait:0
+
+************ IP Statistics ************
+ total packets received 795
+ datagrams delivered to upper level 795
+ total ip packets generated here 401
+
+************ TCP Statistics ************
+ connections accepted 1
+ connections established 1
+ conn. closed (includes drops) 1
+ segs where we tried to get rtt 2
+ times we succeeded 2
+ delayed acks sent 4
+ total packets sent 401
+ ack-only packets sent 6
+ window update-only packets sent 394
+ control (SYN|FIN|RST) packets sent 1
+ total packets received 795
+ packets received in sequence 792
+ bytes received in sequence 1145339
+ rcvd ack packets 2
+ bytes acked by rcvd acks 2
+ times hdr predict ok for data pkts 791
+
+
diff --git a/bsps/shared/net/README.sonic b/bsps/shared/net/README.sonic
new file mode 100644
index 0000000000..b2478b5571
--- /dev/null
+++ b/bsps/shared/net/README.sonic
@@ -0,0 +1,135 @@
+This SONIC driver does not make any attempt to support the SONIC chip
+in any of the following modes:
+
+ + 16-bit
+ + little endian
+
+It does not attempt to handle SONIC's older than Revision C. There is
+a bug in chips before that revision that must be handled in the driver.
+
+The configuration table should be discussed here but if you look in the
+include file for the sonic, it is reasonably obvious. :)
+
+The performance impact of transforming this driver into libchip format
+was minimal.
+
+The powerpc/dmv177 BSP used this driver and the following should
+serve as an example configuration table. This BSP was obsoleted after
+the 4.6 release series so the code is included here.
+
+======================================================================
+
+/*
+ * DMV177 SONIC Configuration Information
+ *
+ * References:
+ *
+ * 1) SVME/DMV-171 Single Board Computer Documentation Package, #805905,
+ * DY 4 Systems Inc., Kanata, Ontario, September, 1996.
+ */
+
+#include <bsp.h>
+#include <rtems/rtems_bsdnet.h>
+#include <libchip/sonic.h>
+
+void dmv177_sonic_write_register(
+ void *base,
+ unsigned32 regno,
+ unsigned32 value
+)
+{
+ volatile unsigned32 *p = base;
+
+#if (SONIC_DEBUG & SONIC_DEBUG_PRINT_REGISTERS)
+ printf( "%p Write 0x%04x to %s (0x%02x)\n",
+ &p[regno], value, SONIC_Reg_name[regno], regno );
+ fflush( stdout );
+#endif
+ p[regno] = value;
+}
+
+unsigned32 dmv177_sonic_read_register(
+ void *base,
+ unsigned32 regno
+)
+{
+ volatile unsigned32 *p = base;
+ unsigned32 value;
+
+ value = p[regno];
+#if (SONIC_DEBUG & SONIC_DEBUG_PRINT_REGISTERS)
+ printf( "%p Read 0x%04x from %s (0x%02x)\n",
+ &p[regno], value, SONIC_Reg_name[regno], regno );
+ fflush( stdout );
+#endif
+ return value;
+}
+
+/*
+ * Default sizes of transmit and receive descriptor areas
+ */
+#define RDA_COUNT 20 /* 20 */
+#define TDA_COUNT 20 /* 10 */
+
+/*
+ * Default device configuration register values
+ * Conservative, generic values.
+ * DCR:
+ * No extended bus mode
+ * Unlatched bus retry
+ * Programmable outputs unused
+ * Asynchronous bus mode
+ * User definable pins unused
+ * No wait states (access time controlled by DTACK*)
+ * 32-bit DMA
+ * Empty/Fill DMA mode
+ * Maximum Transmit/Receive FIFO
+ * DC2:
+ * Extended programmable outputs unused
+ * Normal HOLD request
+ * Packet compress output unused
+ * No reject on CAM match
+ */
+#define SONIC_DCR \
+ (DCR_DW32 | DCR_WAIT0 | DCR_PO0 | DCR_PO1 | DCR_RFT24 | DCR_TFT28)
+#ifndef SONIC_DCR
+# define SONIC_DCR (DCR_DW32 | DCR_TFT28)
+#endif
+#ifndef SONIC_DC2
+# define SONIC_DC2 (0)
+#endif
+
+/*
+ * Default location of device registers
+ */
+#ifndef SONIC_BASE_ADDRESS
+# define SONIC_BASE_ADDRESS 0xF3000000
+# warning "Using default SONIC_BASE_ADDRESS."
+#endif
+
+/*
+ * Default interrupt vector
+ */
+#ifndef SONIC_VECTOR
+# define SONIC_VECTOR 1
+# warning "Using default SONIC_VECTOR."
+#endif
+
+sonic_configuration_t dmv177_sonic_configuration = {
+ SONIC_BASE_ADDRESS, /* base address */
+ SONIC_VECTOR, /* vector number */
+ SONIC_DCR, /* DCR register value */
+ SONIC_DC2, /* DC2 register value */
+ TDA_COUNT, /* number of transmit descriptors */
+ RDA_COUNT, /* number of receive descriptors */
+ dmv177_sonic_write_register,
+ dmv177_sonic_read_register
+};
+
+int rtems_dmv177_sonic_driver_attach(struct rtems_bsdnet_ifconfig *config)
+{
+ return rtems_sonic_driver_attach( config, &dmv177_sonic_configuration );
+
+}
+
+======================================================================
diff --git a/bsps/shared/net/README.tulipclone b/bsps/shared/net/README.tulipclone
new file mode 100644
index 0000000000..90332bfd61
--- /dev/null
+++ b/bsps/shared/net/README.tulipclone
@@ -0,0 +1,101 @@
+*****************************************************************
+******** ***************
+******** ttcp benchmark tests of dec2114x driver ***************
+******** adapted from FreeBSD's if_dc.c for RTEMS ***************
+******** by: Daron Chabot (12/15/03), ***************
+******** <daron@nucleus.usask.ca> ***************
+*****************************************************************
+
+Test Equipment:
+-----------------------
+- Intel 450 MHz P3's
+- RH Linux v7.3, 2.4.7-10 kernel, D-Link DFE-530TX (via-rhine)
+- RTEMS rtems-ss-20030703, pc386 BSP, Linksys LNE100TX ( actually,
+a cleverly disguised ADMtek Centaur, aka "Comet").
+- the PCs were directly connected ( RJ-45 X-over cable ) on a class C
+subnet (private)
+
+NOTE:
+-----------------------
+- the following lines must be added to the BSP's "bsp.h" file, or
+inserted into an application header (e.g. a "network_config.h" file, or
+similar):
+
+extern int rtems_dc_driver_attach(struct rtems_bsdnet_ifconfig *, int);
+#define BSP_DEC_NETWORK_DRIVER_NAME "tl1" /* "tl" as in "tulip-clone" */
+#define BSP_DEC_NETWORK_DRIVER_ATTACH rtems_dc_driver_attach
+
+
+**************************
+Linux Tx ----> RTEMS Rx: *
+**************************
+TRANSMITTER:
+ttcp-t: buflen=8192, nbuf=65536, align=16384/0, port=5001 tcp -> rtems
+ttcp-t: 536870912 bytes in 45.72 real seconds = 11468.54 KB/sec +++
+ttcp-t: 536870912 bytes in 6.87 CPU seconds = 76315.57 KB/cpu sec
+ttcp-t: 65536 I/O calls, msec/call = 0.71, calls/sec = 1433.57
+ttcp-t: 0.1user 6.7sys 0:45real 15% 0i+0d 0maxrss 0+2pf 0+0csw
+
+
+RECEIVER:
+ttcp-r: buflen=8192, nbuf=65536, align=16384/0, port=5001 tcp
+ttcp-r: 536870912 bytes in 45.72 real seconds = 11467.37 KB/sec +++
+ttcp-r: 536870912 bytes in 45.87 CPU seconds = 11467.37 KB/cpu sec
+ttcp-r: 370837 I/O calls, msec/call = 0.13, calls/sec = 8111.05
+ttcp-r: 0.0user 45.7sys 0:45real 100% 0i+0d 0maxrss 0+0pf 0+0csw
+
+
+
+**************************
+RTEMS Tx ----> Linux Rx: *
+**************************
+TRANSMITTER:
+ttcp-t: buflen=8192, nbuf=65536, align=16384/0, port=5001 tcp ->192.168.1.1
+ttcp-t: 536870912 bytes in 46.22 real seconds = 11343.31 KB/sec +++
+ttcp-t: 536870912 bytes in 46.22 CPU seconds = 11343.31 KB/cpu sec
+ttcp-t: 65536 I/O calls, msec/call = 0.72, calls/sec = 1417.91
+ttcp-t: 0.0user 46.2sys 0:46real 100% 0i+0d 0maxrss 0+0pf 0+0csw
+
+
+
+
+RECEIVER:
+ttcp-r: buflen=8192, nbuf=65536, align=16384/0, port=5001 tcp
+ttcp-r: 536870912 bytes in 46.22 real seconds = 11343.05 KB/sec +++
+ttcp-r: 536870912 bytes in 11.60 CPU seconds = 45197.24 KB/cpu sec
+ttcp-r: 356183 I/O calls, msec/call = 0.13, calls/sec = 7706.07
+ttcp-r: 0.6user 10.9sys 0:46real 25% 0i+0d 0maxrss 0+2pf 0+0csw
+
+
+
+****************************************************************************
+****************************************************************************
+****************************************************************************
+******************* Test with 40kB buffer size *****************************
+****************************************************************************
+****************************************************************************
+****************************************************************************
+
+
+**************************
+RTEMS Tx ----> Linux Rx: *
+**************************
+TRANSMITTER:
+ttcp-t: buflen=40960, nbuf=13107, align=16384/0, port=5001 tcp -> 192.168.1.1
+ttcp-t: 536862720 bytes in 46.23 real seconds = 11340.69 KB/sec +++
+ttcp-t: 536862720 bytes in 46.23 CPU seconds = 11340.69 KB/cpu sec
+ttcp-t: 13107 I/O calls, msec/call = 3.61, calls/sec = 283.52
+ttcp-t: 0.0user 46.2sys 0:46real 100% 0i+0d 0maxrss 0+0pf 0+0csw
+
+
+RECEIVER:
+ttcp-r: buflen=40960, nbuf=13107, align=16384/0, port=5001 tcp
+ttcp-r: 536862720 bytes in 46.23 real seconds = 11339.54 KB/sec +++
+ttcp-r: 536862720 bytes in 10.70 CPU seconds = 48998.13 KB/cpu sec
+ttcp-r: 355970 I/O calls, msec/call = 0.13, calls/sec = 7699.20
+ttcp-r: 0.5user 10.1sys 0:46real 23% 0i+0d 0maxrss 0+5pf 0+0csw
+
+
+
+****************************************************************************
+****************************************************************************
diff --git a/bsps/shared/net/cs8900.c b/bsps/shared/net/cs8900.c
new file mode 100644
index 0000000000..650a0e1fef
--- /dev/null
+++ b/bsps/shared/net/cs8900.c
@@ -0,0 +1,1216 @@
+/*
+ ------------------------------------------------------------------------
+
+ Copyright Cybertec Pty Ltd, 2000
+ All rights reserved Cybertec Pty Ltd, 2000
+
+ Port to the DIMM PC copyright (c) 2004 Angelo Fraietta
+ This project has been assisted by the Commonwealth Government
+ through the Australia Council, its arts funding and advisory body.
+
+ COPYRIGHT (c) 1989-1998.
+ On-Line Applications Research Corporation (OAR).
+
+ The license and distribution terms for this file may be
+ found in the file LICENSE in this distribution or at
+ http://www.rtems.org/license/LICENSE.
+
+ ------------------------------------------------------------------------
+
+ CS8900 RTEMS driver.
+
+ See the header file for details.
+
+*/
+
+#define __INSIDE_RTEMS_BSD_TCPIP_STACK__
+
+#include <errno.h>
+#include <string.h>
+#include <stdio.h>
+
+#include <libchip/cs8900.h>
+
+/*
+ * We expect to be able to read a complete packet into an mbuf.
+ */
+
+#if (MCLBYTES < 1520)
+#error "CS8900 Driver must have MCLBYTES >= 1520"
+#endif
+
+/*
+ * Task event usage.
+ */
+
+#define CS8900_RX_OK_EVENT RTEMS_EVENT_1
+#define CS8900_TX_START_EVENT RTEMS_EVENT_1
+#define CS8900_TX_OK_EVENT RTEMS_EVENT_2
+#define CS8900_TX_WAIT_EVENT RTEMS_EVENT_3
+
+/*
+ * IO Packet Page inteface.
+ */
+
+static inline unsigned short
+io_pp_get_reg_16 (cs8900_device *cs, unsigned short reg)
+{
+ rtems_interrupt_level level;
+ unsigned short data;
+ rtems_interrupt_disable (level);
+ cs8900_io_set_reg (cs, CS8900_IO_PACKET_PAGE_PTR,
+ 0x3000 | CS8900_PPP_AUTO_INCREMENT | reg);
+ data = cs8900_io_get_reg (cs, CS8900_IO_PP_DATA_PORT0);
+ rtems_interrupt_enable (level);
+ return data;
+}
+
+static inline uint32_t
+io_pp_get_reg_32 (cs8900_device *cs, uint16_t reg)
+{
+ rtems_interrupt_level level;
+ uint32_t data;
+ rtems_interrupt_disable (level);
+ cs8900_io_set_reg (cs, CS8900_IO_PACKET_PAGE_PTR,
+ 0x3000 | CS8900_PPP_AUTO_INCREMENT | reg);
+ data = cs8900_io_get_reg (cs, CS8900_IO_PP_DATA_PORT0);
+ data <<= 16;
+ data |= cs8900_io_get_reg (cs, CS8900_IO_PP_DATA_PORT1);
+ rtems_interrupt_enable (level);
+ return data;
+}
+
+static inline void
+io_pp_set_reg_16 (cs8900_device *cs, unsigned short reg, unsigned short data)
+{
+ rtems_interrupt_level level;
+ rtems_interrupt_disable (level);
+ cs8900_io_set_reg (cs, CS8900_IO_PACKET_PAGE_PTR,
+ 0x3000 | CS8900_PPP_AUTO_INCREMENT | reg);
+ cs8900_io_set_reg (cs, CS8900_IO_PP_DATA_PORT0, data);
+ rtems_interrupt_enable (level);
+}
+
+static inline void
+io_pp_set_reg_32 (cs8900_device *cs, unsigned short reg, unsigned long data)
+{
+ cs8900_io_set_reg (cs, CS8900_IO_PACKET_PAGE_PTR,
+ 0x3000 | CS8900_PPP_AUTO_INCREMENT | reg);
+ cs8900_io_set_reg (cs, CS8900_IO_PP_DATA_PORT0, data >> 16);
+ cs8900_io_set_reg (cs, CS8900_IO_PP_DATA_PORT1, data);
+}
+
+static inline void
+io_pp_bit_set_reg_16 (cs8900_device *cs, unsigned short reg, unsigned short mask)
+{
+ rtems_interrupt_level level;
+ rtems_interrupt_disable (level);
+ io_pp_set_reg_16 (cs, reg, io_pp_get_reg_16 (cs, reg) | mask);
+ rtems_interrupt_enable (level);
+}
+
+static inline void
+io_pp_bit_clear_reg_16 (cs8900_device *cs, unsigned short reg, unsigned short mask)
+{
+ rtems_interrupt_level level;
+ rtems_interrupt_disable (level);
+ io_pp_set_reg_16 (cs, reg, io_pp_get_reg_16 (cs, reg) & ~mask);
+ rtems_interrupt_enable (level);
+}
+
+/*
+ * Memory Mapped Packet Page interface.
+ *
+ * If the BSP does not configure mem_base use the I/O register accesses.
+ */
+
+static inline unsigned short
+mem_pp_get_reg (cs8900_device *cs, unsigned short reg)
+{
+ if (!cs->mem_base)
+ return io_pp_get_reg_16 (cs, reg);
+ return cs8900_mem_get_reg (cs, reg);
+}
+
+static inline void
+mem_pp_set_reg (cs8900_device *cs, unsigned short reg, unsigned short data)
+{
+ if (!cs->mem_base)
+ io_pp_set_reg_16 (cs, reg, data);
+ else
+ cs8900_mem_set_reg (cs, reg, data);
+}
+
+static inline void
+mem_pp_bit_set_reg (cs8900_device *cs, unsigned short reg, unsigned short mask)
+{
+ if (!cs->mem_base)
+ io_pp_bit_set_reg_16 (cs, reg, mask);
+ else
+ {
+ rtems_interrupt_level level;
+ rtems_interrupt_disable (level);
+ mem_pp_set_reg (cs, reg, mem_pp_get_reg (cs, reg) | mask);
+ rtems_interrupt_enable (level);
+ }
+}
+
+static inline void
+mem_pp_bit_clear_reg (cs8900_device *cs, unsigned short reg, unsigned short mask)
+{
+ if (!cs->mem_base)
+ io_pp_bit_clear_reg_16 (cs, reg, mask);
+ else
+ {
+ rtems_interrupt_level level;
+ rtems_interrupt_disable (level);
+ mem_pp_set_reg (cs, reg, mem_pp_get_reg (cs, reg) & ~mask);
+ rtems_interrupt_enable (level);
+ }
+}
+
+/*
+ * Trace defines and control structures.
+ */
+
+#define CS8900_T_INT (0)
+#define CS8900_T_RX_OK (1)
+#define CS8900_T_RX_DROPPED (2)
+#define CS8900_T_NO_MBUF (3)
+#define CS8900_T_NO_CLUSTERS (4)
+#define CS8900_T_RX_BEGIN (5)
+#define CS8900_T_RX_END (6)
+
+#if CS8900_TRACE
+
+static const char *cs8900_trace_labels[] =
+{
+ "int",
+ "rx ok",
+ "rx dropped",
+ "no mbuf",
+ "no clusters",
+ "rx begin",
+ "rx end"
+};
+
+/*
+ * Assumes a micro-second timer such as the Coldfire.
+ */
+
+uint32_t rtems_read_timer ();
+
+static inline void
+cs8900_trace (cs8900_device *cs, unsigned short key, unsigned long var)
+{
+ rtems_interrupt_level level;
+
+ rtems_interrupt_disable (level);
+
+ if (cs->trace_in < CS8900_TRACE_SIZE)
+ {
+ cs->trace_key[cs->trace_in] = key;
+ cs->trace_var[cs->trace_in] = var;
+ cs->trace_time[cs->trace_in] = rtems_read_timer ();
+ cs->trace_in++;
+ }
+
+ rtems_interrupt_enable (level);
+}
+#else
+#define cs8900_trace(c, k, v)
+#endif
+
+void cs8900_get_mac_addr (cs8900_device *cs, unsigned char *mac_address)
+{
+ unsigned short ma;
+
+ /*
+ * Only ever use IO calls for this function as it can be
+ * called before memory mode has been enabled.
+ */
+
+ ma = io_pp_get_reg_16 (cs, CS8900_PP_IA);
+ mac_address[0] = ma >> 8;
+ mac_address[1] = ma;
+
+ ma = io_pp_get_reg_16 (cs, CS8900_PP_IA + 2);
+ mac_address[2] = ma >> 8;
+ mac_address[3] = ma;
+
+ ma = io_pp_get_reg_16 (cs, CS8900_PP_IA + 4);
+ mac_address[4] = ma >> 8;
+ mac_address[5] = ma;
+}
+
+/*
+ * Bring the chip online.
+ */
+
+static void
+cs8900_hardware_init (cs8900_device *cs)
+{
+ unsigned long prod_id;
+ unsigned short status;
+
+ /*
+ * Do nothing while the device is calibrating and checking the EEPROM.
+ * We must wait 20msecs.
+ */
+
+ io_pp_bit_set_reg_16 (cs, CS8900_PP_SelfCTL, CS8900_SELF_CTRL_RESET);
+
+ rtems_task_wake_after (RTEMS_MILLISECONDS_TO_TICKS (20));
+
+ status = io_pp_get_reg_16 (cs, CS8900_PP_SelfST);
+ if (status == 0) {
+ printf("Reading status register again\n");
+ status = io_pp_get_reg_16 (cs, CS8900_PP_SelfST);
+ }
+
+ if (((status & CS8900_SELF_STATUS_INITD) == 0) ||
+ ((status & CS8900_SELF_STATUS_INITD) &&
+ (status & CS8900_SELF_STATUS_EEPROM_PRESENT) &&
+ (status & CS8900_SELF_STATUS_SIBUST)))
+ {
+ printf ("CS8900: %s. Initialisation aborted.\n",
+ (status & CS8900_SELF_STATUS_INITD) ?
+ "EEPROM read/write failed to complete" :
+ "Failed to complete to reset");
+ return;
+ }
+
+ /* Set the RX queue size if not set by the BSP. */
+
+ if (cs->rx_queue_size == 0)
+ cs->rx_queue_size = 10;
+
+ /* Probe the device for its ID */
+
+ prod_id = io_pp_get_reg_32 (cs, CS8900_PP_PROD_ID);
+
+ if ((prod_id >> 16) != CS8900_ESIA_ID)
+ {
+ printf ("CS8900: Invalid EISA ID, read product code 0x%08lx\n", prod_id);
+ return;
+ }
+
+ if ((prod_id & 0x000000ff) != 0)
+ {
+ printf ("CS8900: Unsupported product id, read product code 0x%08lx\n",
+ prod_id);
+ return;
+ }
+
+ printf ("CS8900 Rev %ld, %s, %s.\n",
+ (prod_id >> 8) & 0x1f,
+ status & CS8900_SELF_STATUS_3_3_V ? "3.3V" : "5.0V",
+ status & CS8900_SELF_STATUS_EEPROM_PRESENT ?
+ "EEPROM present" : "no EEPROM");
+
+ /*
+ * Switch to memory base accesses as they are faster. No indirect access.
+ */
+
+ if (cs->mem_base)
+ {
+ io_pp_set_reg_16 (cs, CS8900_PP_MEM_BASE, cs->mem_base);
+ io_pp_set_reg_16 (cs, CS8900_PP_MEM_BASE + 2, (cs->mem_base >> 16) & 0xf);
+
+ io_pp_set_reg_16 (cs,
+ CS8900_PP_BusCTL,
+ CS8900_BUS_CTRL_RESET_RX_DMA |
+ CS8900_BUS_CTRL_USE_SA |
+ CS8900_BUS_CTRL_MEMORY_ENABLE);
+ io_pp_set_reg_16 (cs,
+ CS8900_PP_BusCTL,
+ CS8900_BUS_CTRL_USE_SA |
+ CS8900_BUS_CTRL_MEMORY_ENABLE);
+ }
+
+ /*
+ * We are now in memory mapped mode.
+ */
+
+ /*
+ * Program the Line Control register with the mode we want.
+ *
+ * No auto detect support at the moment. Only 10BaseT.
+ */
+
+ mem_pp_set_reg (cs, CS8900_PP_LineCFG, CS8900_LINE_CTRL_10BASET);
+
+ /*
+ * Ask the user for the MAC address, the program into the device.
+ */
+
+#define MACO(o) cs->arpcom.ac_enaddr[o]
+
+ mem_pp_set_reg (cs, CS8900_PP_IA,
+ (((unsigned int) MACO (1)) << 8) |
+ ((unsigned int) MACO (0)));
+ mem_pp_set_reg (cs, CS8900_PP_IA + 2,
+ (((unsigned int) MACO (3)) << 8) |
+ ((unsigned int) MACO (2)));
+ mem_pp_set_reg (cs, CS8900_PP_IA + 4,
+ (((unsigned int) MACO (5)) << 8) |
+ ((unsigned int) MACO (4)));
+
+ /*
+ * Set the Buffer configuration.
+ */
+
+ mem_pp_set_reg (cs, CS8900_PP_BufCFG,
+ CS8900_BUFFER_CONFIG_RDY_FOR_TX |
+ CS8900_BUFFER_CONFIG_TX_UNDERRUN |
+ CS8900_BUFFER_CONFIG_TX_COL_OVF |
+ CS8900_BUFFER_CONFIG_RX_MISSED_OVF);
+
+ /*
+ * Set the Receiver configuration.
+ */
+
+ mem_pp_set_reg (cs, CS8900_PP_RxCFG,
+ CS8900_RX_CONFIG_RX_OK |
+ CS8900_RX_CONFIG_CRC_ERROR |
+ CS8900_RX_CONFIG_RUNT|
+ CS8900_RX_CONFIG_EXTRA_DATA);
+
+ /*
+ * Set the Receiver control.
+ */
+
+ mem_pp_set_reg (cs, CS8900_PP_RxCTL,
+ CS8900_RX_CTRL_RX_OK |
+ CS8900_RX_CTRL_MULTICAST |
+ CS8900_RX_CTRL_INDIVIDUAL |
+ CS8900_RX_CTRL_BROADCAST);
+
+ /*
+ * Set the Transmitter configuration.
+ */
+
+ mem_pp_set_reg (cs, CS8900_PP_TxCFG,
+ CS8900_TX_CONFIG_TX_OK |
+ CS8900_TX_CONFIG_OUT_OF_WINDOW |
+ CS8900_TX_CONFIG_JABBER |
+ CS8900_TX_CONFIG_16_COLLISION);
+
+ /*
+ * Attach the interrupt handler.
+ */
+
+ cs8900_attach_interrupt (cs);
+
+ /*
+ * Program the interrupt level we require then enable interrupts.
+ *
+ * Note, this will need to change to support other levels.
+ */
+
+ mem_pp_set_reg (cs, CS8900_PP_INT, cs->irq_level & 3);
+
+ mem_pp_bit_set_reg (cs, CS8900_PP_BusCTL,
+ CS8900_BUS_CTRL_ENABLE_INT);
+}
+
+rtems_isr
+cs8900_interrupt (rtems_vector_number v, void *csp)
+{
+ cs8900_device *cs = csp;
+ unsigned short isq = 0;
+ struct mbuf *m;
+ unsigned char *p;
+
+ ++cs->eth_stats.interrupts;
+
+ while (1)
+ {
+ isq = mem_pp_get_reg (cs, CS8900_PP_ISQ);
+
+ cs8900_trace (cs, CS8900_T_INT, isq);
+
+ /*
+ * No more interrupts to service.
+ */
+
+ if (isq == 0)
+ return;
+
+ switch (isq & 0x1f)
+ {
+ case 0x04:
+
+ /*
+ * RxEvent.
+ */
+
+ ++cs->eth_stats.rx_interrupts;
+
+ if (isq & CS8900_RX_EVENT_RX_OK)
+ {
+ m = cs->rx_ready_head;
+ if (m)
+ {
+ cs->rx_ready_head = m->m_nextpkt;
+ if (cs->rx_ready_head == 0)
+ cs->rx_ready_tail = 0;
+ m->m_nextpkt = 0;
+ cs->rx_ready_len--;
+
+ p = mtod (m, unsigned char *);
+
+ m->m_pkthdr.len = cs8900_get_data_block (cs, p);
+
+ if (cs->rx_loaded_tail == 0)
+ cs->rx_loaded_head = m;
+ else
+ cs->rx_loaded_tail->m_nextpkt = m;
+ cs->rx_loaded_tail = m;
+ cs->rx_loaded_len++;
+
+ if (cs->rx_loaded_len == 1)
+ {
+ cs8900_trace (cs, CS8900_T_RX_OK, cs->rx_loaded_len);
+ rtems_bsdnet_event_send (cs->rx_task, CS8900_RX_OK_EVENT);
+ }
+ }
+ else
+ {
+ ++cs->eth_stats.rx_dropped;
+
+ cs8900_trace (cs, CS8900_T_RX_DROPPED, cs->rx_loaded_len);
+
+ if (cs->rx_loaded_len == 0)
+ rtems_bsdnet_event_send (cs->rx_task, CS8900_RX_OK_EVENT);
+ }
+ }
+ else
+ {
+ if (isq & CS8900_RX_EVENT_CRC_ERROR)
+ ++cs->eth_stats.rx_crc_errors;
+
+ if (isq & CS8900_RX_EVENT_RUNT)
+ ++cs->eth_stats.rx_runt_errors;
+
+ if (isq & CS8900_RX_EVENT_EXTRA_DATA)
+ ++cs->eth_stats.rx_oversize_errors;
+ }
+ break;
+
+ case 0x08:
+
+ /*
+ * TxEvent.
+ */
+
+ ++cs->eth_stats.tx_interrupts;
+
+ if (cs->tx_active)
+ {
+ if (isq & CS8900_TX_EVENT_TX_OK)
+ ++cs->eth_stats.tx_ok;
+
+ cs->tx_active = 0;
+
+ rtems_bsdnet_event_send (cs->tx_task, CS8900_TX_OK_EVENT);
+ }
+ break;
+
+ case 0x0c:
+
+ /*
+ * BufEvent.
+ */
+
+ if (isq & CS8900_BUFFER_EVENT_RDY_FOR_TX)
+ {
+ if (cs->tx_active)
+ {
+ ++cs->eth_stats.tx_rdy4tx;
+ rtems_bsdnet_event_send (cs->tx_task, CS8900_TX_WAIT_EVENT);
+ }
+ }
+ else if (isq & CS8900_BUFFER_EVENT_TX_UNDERRUN)
+ {
+ ++cs->eth_stats.tx_underrun_errors;
+ if (cs->tx_active)
+ rtems_bsdnet_event_send (cs->tx_task, CS8900_TX_OK_EVENT);
+ }
+ else if (isq & CS8900_BUFFER_EVENT_SW_INT)
+ {
+ ++cs->eth_stats.int_swint_res;
+ }
+ break;
+
+ case 0x10:
+
+ /*
+ * RxMiss.
+ */
+
+ cs->eth_stats.rx_missed_errors +=
+ mem_pp_get_reg (cs, CS8900_PP_RxMISS) >> 6;
+ break;
+
+ case 0x12:
+
+ /*
+ * TxCol.
+ */
+
+ cs->eth_stats.tx_collisions +=
+ mem_pp_get_reg (cs, CS8900_PP_TxCol) >> 6;
+ break;
+
+ default:
+ break;
+ }
+ }
+
+}
+
+int
+cs8900_link_active (cs8900_device *cs)
+{
+ return ((mem_pp_get_reg (cs, CS8900_PP_LineST) & CS8900_LINE_STATUS_LINK_OK) ?
+ 1 : 0);
+}
+
+static inline void
+cs8900_rx_refill_queue (cs8900_device *cs)
+{
+ struct ifnet *ifp = &cs->arpcom.ac_if;
+ struct mbuf *m;
+ rtems_interrupt_level level;
+
+ /*
+ * Hold a single queue of mbuf's at the interface. This
+ * will lower the latency of the driver.
+ */
+
+ while (cs->rx_ready_len < cs->rx_queue_size)
+ {
+ MGETHDR (m, M_DONTWAIT, MT_DATA);
+
+ if (!m)
+ {
+ ++cs->eth_stats.rx_no_mbufs;
+ cs8900_trace (cs, CS8900_T_NO_MBUF, cs->eth_stats.rx_no_mbufs);
+ return;
+ }
+
+ MCLGET (m, M_DONTWAIT);
+
+ if (!m->m_ext.ext_buf)
+ {
+ ++cs->eth_stats.rx_no_clusters;
+ cs8900_trace (cs, CS8900_T_NO_CLUSTERS, cs->eth_stats.rx_no_clusters);
+ m_free (m);
+ return;
+ }
+ m->m_pkthdr.rcvif = ifp;
+ m->m_nextpkt = 0;
+
+ rtems_interrupt_disable (level);
+
+ if (cs->rx_ready_tail == 0)
+ cs->rx_ready_head = m;
+ else
+ cs->rx_ready_tail->m_nextpkt = m;
+ cs->rx_ready_tail = m;
+ cs->rx_ready_len++;
+
+ rtems_interrupt_enable (level);
+ }
+}
+
+static void
+cs8900_rx_task (void *arg)
+{
+ cs8900_device *cs = arg;
+ struct ifnet *ifp = &cs->arpcom.ac_if;
+ rtems_event_set events;
+ struct mbuf *m;
+ struct ether_header *eh;
+ rtems_status_code sc;
+ rtems_interrupt_level level;
+
+ /*
+ * Turn the receiver and transmitter on.
+ */
+
+ mem_pp_bit_set_reg (cs, CS8900_PP_LineCFG,
+ CS8900_LINE_CTRL_RX_ON |
+ CS8900_LINE_CTRL_TX_ON);
+
+ /*
+ * Start the software interrupt watchdog.
+ */
+
+ mem_pp_bit_set_reg (cs, CS8900_PP_BufCFG,
+ CS8900_BUFFER_CONFIG_SW_INT);
+ ++cs->eth_stats.int_swint_req;
+
+ /*
+ * Loop reading packets.
+ */
+
+ while (1)
+ {
+ cs8900_rx_refill_queue (cs);
+
+ sc = rtems_bsdnet_event_receive (CS8900_RX_OK_EVENT,
+ RTEMS_WAIT | RTEMS_EVENT_ANY,
+ RTEMS_MILLISECONDS_TO_TICKS (250),
+ &events);
+
+ cs8900_rx_refill_queue (cs);
+
+ if (sc == RTEMS_TIMEOUT)
+ {
+ /*
+ * We need to check the interrupt hardware in the cs8900a
+ * has not locked up. It seems this occurs if the ISQ
+ * queue fills up.
+ * To test we generate a software interrupt and watch
+ * a counter go up. If the counter does not go for 2
+ * software interrupts requests we flush the ISQ queue.
+ */
+
+ if ((cs->eth_stats.int_swint_req - cs->eth_stats.int_swint_res) > 1)
+ {
+ printf ("cs8900: int lockup, isq flush\n");
+
+ mem_pp_bit_clear_reg (cs, CS8900_PP_BusCTL,
+ CS8900_BUS_CTRL_ENABLE_INT);
+
+ while (mem_pp_get_reg (cs, CS8900_PP_ISQ) != 0);
+
+ cs->eth_stats.int_swint_req = cs->eth_stats.int_swint_res = 0;
+ ++cs->eth_stats.int_lockup;
+
+ mem_pp_bit_set_reg (cs, CS8900_PP_BusCTL,
+ CS8900_BUS_CTRL_ENABLE_INT);
+ }
+
+ mem_pp_bit_set_reg (cs, CS8900_PP_BufCFG,
+ CS8900_BUFFER_CONFIG_SW_INT);
+ ++cs->eth_stats.int_swint_req;
+ }
+
+ cs8900_trace (cs, CS8900_T_RX_BEGIN, cs->rx_loaded_len);
+
+ while (cs->rx_loaded_len)
+ {
+ rtems_interrupt_disable (level);
+
+ m = cs->rx_loaded_head;
+ if (m)
+ {
+ cs->rx_loaded_head = m->m_nextpkt;
+ if (cs->rx_loaded_head == 0)
+ cs->rx_loaded_tail = 0;
+ m->m_nextpkt = 0;
+ cs->rx_loaded_len--;
+
+ rtems_interrupt_enable (level);
+
+ m->m_pkthdr.rcvif = ifp;
+
+ cs->eth_stats.rx_bytes += m->m_pkthdr.len;
+
+ m->m_len = m->m_pkthdr.len = m->m_pkthdr.len - sizeof (struct ether_header);
+
+ eh = mtod (m, struct ether_header *);
+ m->m_data += sizeof (struct ether_header);
+
+ ++cs->eth_stats.rx_packets;
+
+ ether_input (ifp, eh, m);
+ }
+ else
+ {
+ rtems_interrupt_enable (level);
+ }
+ }
+ cs8900_trace (cs, CS8900_T_RX_END, cs->rx_loaded_len);
+ }
+}
+
+static void
+cs8900_tx_task (void *arg)
+{
+ cs8900_device *cs = arg;
+ struct ifnet *ifp = &cs->arpcom.ac_if;
+ rtems_event_set events;
+ struct mbuf *m;
+ rtems_status_code sc;
+
+ /*
+ * Wait for the link to come up.
+ */
+
+ rtems_task_wake_after (RTEMS_MILLISECONDS_TO_TICKS (750));
+
+ /*
+ * Loop processing the tx queue.
+ */
+
+ while (1)
+ {
+ /*
+ * Fetch the mbuf list from the interface's queue.
+ */
+
+ IF_DEQUEUE (&ifp->if_snd, m);
+
+ /*
+ * If something actually is present send it.
+ */
+
+ if (!m)
+ {
+ ifp->if_flags &= ~IFF_OACTIVE;
+
+ rtems_bsdnet_event_receive (CS8900_TX_START_EVENT,
+ RTEMS_WAIT | RTEMS_EVENT_ANY,
+ RTEMS_NO_TIMEOUT,
+ &events);
+ }
+ else
+ {
+ if (cs8900_link_active (cs))
+ {
+ int resending;
+
+ do
+ {
+ unsigned short buf_status;
+
+ resending = 0;
+
+ cs->tx_active = 1;
+
+ mem_pp_set_reg (cs, CS8900_PP_TxCMD,
+ CS8900_TX_CMD_STATUS_TX_START_ENTIRE |
+ CS8900_TX_CMD_STATUS_FORCE);
+ mem_pp_set_reg (cs, CS8900_PP_TxLength, m->m_pkthdr.len);
+
+ buf_status = mem_pp_get_reg (cs, CS8900_PP_BusST);
+
+ /*
+ * If the bid for memory in the device fails trash the
+ * transmit and try again next time.
+ */
+
+ if (buf_status & CS8900_BUS_STATUS_TX_BID_ERROR)
+ ++cs->eth_stats.tx_bid_errors;
+ else
+ {
+ /*
+ * If the buffer is not read enable the interrupt and then wait.
+ */
+
+ if ((buf_status & CS8900_BUS_STATUS_RDY_FOR_TX_NOW) == 0)
+ {
+ cs->eth_stats.tx_wait_for_rdy4tx++;
+ sc = rtems_bsdnet_event_receive (CS8900_TX_WAIT_EVENT,
+ RTEMS_WAIT | RTEMS_EVENT_ANY,
+ RTEMS_MILLISECONDS_TO_TICKS (750),
+ &events);
+ if (sc == RTEMS_TIMEOUT)
+ {
+ /*
+ * For some reason the wait request has been dropped,
+ * so lets resend from the start.
+ */
+
+ printf ("tx resend\n");
+ ++cs->eth_stats.tx_resends;
+ resending = 1;
+ }
+ }
+
+ if (!resending)
+ {
+ cs8900_tx_load (cs, m);
+ cs->eth_stats.tx_packets++;
+ cs->eth_stats.tx_bytes += m->m_pkthdr.len;
+ }
+ }
+ }
+ while (resending);
+
+ m_freem (m);
+
+ do
+ {
+ rtems_bsdnet_event_receive (CS8900_TX_OK_EVENT,
+ RTEMS_WAIT | RTEMS_EVENT_ANY,
+ RTEMS_NO_TIMEOUT,
+ &events);
+ }
+ while (cs->tx_active);
+ }
+ else
+ {
+ ++cs->eth_stats.tx_dropped;
+ m_freem (m);
+ }
+ }
+ }
+}
+
+static void
+cs8900_start (struct ifnet *ifp)
+{
+ cs8900_device *cs = ifp->if_softc;
+
+ /*
+ * Tell the transmit daemon to wake up and send a packet.
+ */
+
+ ifp->if_flags |= IFF_OACTIVE;
+
+ rtems_bsdnet_event_send (cs->tx_task, CS8900_TX_START_EVENT);
+}
+
+static void
+cs8900_stop (cs8900_device *cs)
+{
+ mem_pp_bit_clear_reg (cs, CS8900_PP_LineCFG,
+ CS8900_LINE_CTRL_RX_ON |
+ CS8900_LINE_CTRL_TX_ON);
+
+ mem_pp_bit_clear_reg (cs, CS8900_PP_BusCTL,
+ CS8900_BUS_CTRL_ENABLE_INT);
+}
+
+static const char *eth_statistics_labels[] =
+{
+ "rx packets",
+ "tx packets",
+ "rx bytes",
+ "tx bytes",
+ "rx interrupts",
+ "tx interrupts",
+ "rx dropped",
+ "rx no mbuf",
+ "rx no custers",
+ "rx oversize errors",
+ "rx crc errors",
+ "rx runt errors",
+ "rx missed errors",
+ "tx ok",
+ "tx collisions",
+ "tx bid errors",
+ "tx wait for rdy4tx",
+ "tx rdy4tx",
+ "tx underrun errors",
+ "tx dropped",
+ "tx resends",
+ "int swint req",
+ "int swint res",
+ "int lockup",
+ "interrupts"
+};
+
+static void
+cs8900_stats (cs8900_device *cs)
+{
+ int i;
+ int max_label = 0;
+ int len;
+ unsigned long *value = (unsigned long*) &cs->eth_stats.rx_packets;
+
+ cs->eth_stats.rx_missed_errors +=
+ mem_pp_get_reg (cs, CS8900_PP_RxMISS) >> 6;
+
+ cs->eth_stats.tx_collisions +=
+ mem_pp_get_reg (cs, CS8900_PP_TxCol) >> 6;
+
+ printf ("Network Driver Stats for CS8900 :\n");
+
+ for (i = 0; i < (sizeof (eth_statistics_labels) / sizeof (const char *)); i++)
+ {
+ len = strlen (eth_statistics_labels[i]);
+ if (len > max_label)
+ max_label = len;
+ }
+
+ max_label += 2;
+
+ printf ("%*s - %10u %*s - %10u\n",
+ max_label, "rx ready len", cs->rx_ready_len,
+ max_label, "rx loaded len", cs->rx_loaded_len);
+
+ for (i = 0;
+ i < (sizeof (eth_statistics_labels) / sizeof (const char *));
+ i++)
+ {
+ printf ("%*s - %10lu",
+ max_label, eth_statistics_labels[i], value[i]);
+
+ i++;
+
+ if (i < (sizeof (eth_statistics_labels) / sizeof (const char *)))
+ printf (" %*s - %10lu",
+ max_label, eth_statistics_labels[i], value[i]);
+ printf ("\n");
+ }
+
+#if CS8900_TRACE
+
+ for (i = 0; i < cs->trace_in; i++)
+ {
+ printf ("%8ld.%03ld ", cs->trace_time[i] / 1000, cs->trace_time[i] % 1000);
+
+ if (cs->trace_key[i] < sizeof (cs8900_trace_labels) / sizeof (char*))
+ printf ("%s : ", cs8900_trace_labels[cs->trace_key[i]]);
+ else
+ printf ("unknown trace key, %d : ", cs->trace_key[i]);
+
+ if (cs->trace_key[i] == CS8900_T_INT)
+ {
+ printf ("0x%04lx ", cs->trace_var[i]);
+ if (cs->trace_var[i] == 0)
+ printf ("end");
+ else
+ {
+ switch (cs->trace_var[i] & 0x1f)
+ {
+ case 0x04:
+ printf ("rx event");
+ break;
+
+ case 0x08:
+ printf ("tx event");
+ break;
+
+ case 0x0c:
+ printf ("buffer event");
+ break;
+
+ case 0x10:
+ printf ("rx missed");
+ break;
+
+ case 0x12:
+ printf ("tx collisions");
+ break;
+
+ case 0x1f:
+ printf ("tx request");
+ break;
+
+ case 0x1e:
+ printf ("tx wait 4 tx");
+ break;
+
+ case 0x1d:
+ printf ("tx already active");
+ break;
+
+ default:
+ printf ("unknown event");
+ break;
+ }
+ }
+ }
+ else
+ printf ("0x%08lx", cs->trace_var[i]);
+
+ printf ("\n");
+ }
+
+ cs->trace_in = 0;
+
+#endif
+}
+
+static void
+cs8900_init (void *arg)
+{
+ cs8900_device *cs = arg;
+ struct ifnet *ifp = &cs->arpcom.ac_if;
+
+ if (cs->rx_task == 0)
+ {
+
+ /*
+ * Set up the hardware.
+ */
+
+ cs8900_hardware_init (cs);
+
+ /*
+ * Start driver task. We have only one task.
+ */
+
+ cs->rx_task = rtems_bsdnet_newproc ("CSr0", 4096, cs8900_rx_task, cs);
+ cs->tx_task = rtems_bsdnet_newproc ("CSt0", 4096, cs8900_tx_task, cs);
+ }
+
+#ifdef todo
+ /*
+ * Set flags appropriately
+ */
+ if (ifp->if_flags & IFF_PROMISC)
+ else
+#endif
+
+ /*
+ * Tell the world that we're running.
+ */
+
+ ifp->if_flags |= IFF_RUNNING;
+
+ /*
+ * Set the Line Control to bring the receive and transmitter online.
+ */
+
+ mem_pp_bit_set_reg (cs, CS8900_PP_LineCFG,
+ CS8900_LINE_CTRL_RX_ON |
+ CS8900_LINE_CTRL_TX_ON);
+
+ mem_pp_bit_set_reg (cs, CS8900_PP_BusCTL,
+ CS8900_BUS_CTRL_ENABLE_INT);
+}
+
+static int
+cs8900_ioctl (struct ifnet *ifp, ioctl_command_t cmd, caddr_t data)
+{
+ cs8900_device *cs = ifp->if_softc;
+ int error = 0;
+
+ switch (cmd)
+ {
+ case SIOCGIFADDR:
+ case SIOCSIFADDR:
+
+ error = ether_ioctl (ifp, cmd, data);
+ break;
+
+ case SIOCSIFFLAGS:
+
+ switch (ifp->if_flags & (IFF_UP | IFF_RUNNING))
+ {
+ case IFF_RUNNING:
+
+ cs8900_stop (cs);
+ break;
+
+ case IFF_UP:
+
+ cs8900_init (cs);
+ break;
+
+ case IFF_UP | IFF_RUNNING:
+
+ cs8900_stop (cs);
+ cs8900_init (cs);
+ break;
+
+ default:
+ break;
+ }
+ break;
+
+ case SIO_RTEMS_SHOW_STATS:
+
+ cs8900_stats (cs);
+ break;
+
+ /* FIXME: Multicast commands must be added here. */
+
+ default:
+ error = EINVAL;
+ break;
+ }
+
+ return error;
+}
+
+int
+cs8900_driver_attach (struct rtems_bsdnet_ifconfig *config, int attaching)
+{
+ cs8900_device *cs;
+ struct ifnet *ifp;
+ int mtu;
+ int unit;
+ char *name;
+
+ /*
+ * Parse driver name
+ */
+
+ if ((unit = rtems_bsdnet_parse_driver_name (config, &name)) < 0)
+ return 0;
+
+ cs = config->drv_ctrl;
+ cs->dev = unit;
+ ifp = &cs->arpcom.ac_if;
+
+ if (attaching)
+ {
+ if (ifp->if_softc)
+ {
+ printf ("Driver `%s' already in use.\n", config->name);
+ return 0;
+ }
+
+ /*
+ * Process options
+ */
+
+ if (config->hardware_address)
+ memcpy (cs->arpcom.ac_enaddr, config->hardware_address, ETHER_ADDR_LEN);
+ else
+ cs8900_get_mac_addr (cs, cs->arpcom.ac_enaddr);
+
+ if (config->mtu)
+ mtu = config->mtu;
+ else
+ mtu = ETHERMTU;
+
+ cs->accept_bcast = !config->ignore_broadcast;
+
+ /*
+ * Set up network interface values.
+ */
+
+ ifp->if_softc = cs;
+ ifp->if_unit = unit;
+ ifp->if_name = name;
+ ifp->if_mtu = mtu;
+ ifp->if_init = cs8900_init;
+ ifp->if_ioctl = cs8900_ioctl;
+ ifp->if_start = cs8900_start;
+ ifp->if_output = ether_output;
+ ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX;
+
+ if (ifp->if_snd.ifq_maxlen == 0)
+ ifp->if_snd.ifq_maxlen = ifqmaxlen;
+
+ /*
+ * Attach the interface to the stack.
+ */
+
+ if_attach (ifp);
+ ether_ifattach (ifp);
+ }
+ else
+ {
+ if (!ifp->if_softc)
+ {
+ printf ("Driver `%s' not found.\n", config->name);
+ return 0;
+ }
+
+ cs8900_stop (cs);
+ cs8900_detach_interrupt (cs);
+ }
+
+ return 1;
+}
diff --git a/bsps/shared/net/cs8900.c.bsp b/bsps/shared/net/cs8900.c.bsp
new file mode 100644
index 0000000000..7b7374a0f2
--- /dev/null
+++ b/bsps/shared/net/cs8900.c.bsp
@@ -0,0 +1,510 @@
+/*
+ * RTEMS CS8900 Driver Setup for the DIMM-PC/i386 made by Kontron.
+ *
+ * Port to the DIMM PC copyright (c) 2004 Angelo Fraietta
+ * This project has been assisted by the Commonwealth Government
+ * through the Australia Council, its arts funding and advisory body.
+ *
+ * Port performed by Chris Johns, Cybertec Pty Ltd, Jan 2004.
+ * Based on the Cybertec CS8900 driver setup for the SFP-101.
+ *
+ */
+
+#define CS8900_VERBOSE 0
+#define HAVE_MRB_CS8900_DATA_BUS_SWAPPED 1
+
+#include <bsp.h>
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <arpa/inet.h>
+
+#include <rtems.h>
+#include <rtems/monitor.h>
+#include <rtems/rtems_bsdnet.h>
+#include <irq.h>
+
+#include "cs8900.h"
+
+#include <net/route.h>
+
+/*
+ * Loopback interface.
+ */
+
+extern int rtems_bsdnet_loopattach (struct rtems_bsdnet_ifconfig *, int);
+
+static struct rtems_bsdnet_ifconfig loopback_config =
+{
+ "lo0", /* name */
+ rtems_bsdnet_loopattach, /* attach function */
+ NULL, /* link to next interface */
+ "127.0.0.1", /* IP address */
+ "255.0.0.0", /* IP net mask */
+};
+
+/*
+ * Network configuration
+ */
+
+struct rtems_bsdnet_config rtems_bsdnet_config =
+{
+ &loopback_config,
+ NULL,
+ 20, /* Network task priority */
+ 32 * 1024, /* Mbuf capacity */
+ 96 * 1024, /* Mbuf cluster capacity */
+};
+
+
+static void cs8900_isr ();
+static void cs8900_int_off (const rtems_irq_connect_data* unused);
+static void cs8900_int_on (const rtems_irq_connect_data* unused);
+static int cs8900_int_is_on (const rtems_irq_connect_data *irq);
+
+/**
+ * The device's data.
+ */
+static cs8900_device cs8900;
+static rtems_irq_connect_data cs8900_irq =
+{
+ 0,
+ cs8900_isr,
+ cs8900_int_on,
+ cs8900_int_off,
+ cs8900_int_is_on
+};
+
+#if CS8900_VERBOSE
+static int cs8900_io_verbose = 1;
+#endif
+
+/**
+ * Device structure for attaching to the BSD stack.
+ */
+static struct rtems_bsdnet_ifconfig cs8900_ifconfig =
+{
+ "cs0", /* name */
+ cs8900_driver_attach, /* attach funtion */
+ NULL, /* next interface */
+ NULL, /* ip address */
+ NULL, /* ip netmask */
+ NULL, /* hardware address */
+ 0, /* ignore broadcast */
+ 0, /* mtu */
+ 0, /* rbuf count */
+ 0, /* xbuf count */
+ 0, /* port */
+ 0, /* irno */
+ 0, /* bpar */
+ 0 /* drv ctrl */
+};
+
+
+/*
+ * Commands to register.
+ */
+
+rtems_monitor_command_entry_t rtems_bsdnet_commands[] =
+{
+ {
+ "ifstats",
+ "Show the interface stats.\n",
+ 0,
+ (void*) rtems_bsdnet_show_if_stats,
+ 0,
+ 0,
+ },
+ {
+ "ipstats",
+ "Show the IP stats.\n",
+ 0,
+ (void*) rtems_bsdnet_show_ip_stats,
+ 0,
+ 0,
+ },
+ {
+ "routes",
+ "Show the inet routes.\n",
+ 0,
+ (void*) rtems_bsdnet_show_inet_routes,
+ 0,
+ 0,
+ },
+ {
+ "mbufs",
+ "Show the mbuf stats.\n",
+ 0,
+ (void*) rtems_bsdnet_show_mbuf_stats,
+ 0,
+ 0,
+ },
+ {
+ "icmp",
+ "Show the ICMP stats.\n",
+ 0,
+ (void*) rtems_bsdnet_show_icmp_stats,
+ 0,
+ 0,
+ },
+ {
+ "udp",
+ "Show the UDP stats.\n",
+ 0,
+ (void*) rtems_bsdnet_show_udp_stats,
+ 0,
+ 0,
+ },
+ {
+ "tcp",
+ "Show the TCP stats.\n",
+ 0,
+ (void*) rtems_bsdnet_show_tcp_stats,
+ 0,
+ 0,
+ }
+};
+
+static void
+cs8900_isr ()
+{
+ /*
+ * Note: we could have a high priority task here to call the
+ * drivers handler. The would lower the interrupt latancy
+ * we aother wise have.
+ */
+ cs8900_interrupt (cs8900_irq.name, &cs8900);
+}
+
+static void
+cs8900_int_on (const rtems_irq_connect_data *unused)
+{
+}
+
+static void
+cs8900_int_off (const rtems_irq_connect_data *unused)
+{
+}
+
+static int
+cs8900_int_is_on (const rtems_irq_connect_data *irq)
+{
+ return BSP_irq_enabled_at_i8259s (irq->name);
+}
+
+void cs8900_io_set_reg (cs8900_device *cs, unsigned short reg, unsigned short data)
+{
+#if CS8900_VERBOSE
+ if (cs8900_io_verbose)
+ printk ("CS8900: io set reg=0x%04x, data=0x%04x\n", reg, data);
+#endif
+ outport_word (cs->io_base + reg, data);
+}
+
+unsigned short cs8900_io_get_reg (cs8900_device *cs, unsigned short reg)
+{
+ unsigned short data;
+ inport_word (cs->io_base + reg, data);
+#if CS8900_VERBOSE
+ if (cs8900_io_verbose)
+ printk ("CS8900: io get reg=0x%04x, data=0x%04x\n", reg, data);
+#endif
+ return data;
+}
+
+void cs8900_mem_set_reg (cs8900_device *cs, unsigned long reg, unsigned short data)
+{
+ printk ("CS8900: mem_set_reg register access called. Only IO supported.\n");
+ while (1);
+}
+
+unsigned short cs8900_mem_get_reg (cs8900_device *cs, unsigned long reg)
+{
+ printk ("CS8900: mem_get_reg register access called. Only IO supported.\n");
+ while (1);
+ return 0;
+}
+
+void cs8900_put_data_block (cs8900_device *cs, int len, unsigned char *data)
+{
+ unsigned short *src = (unsigned short *) ((unsigned long) data);
+
+ for (; len > 1; len -= 2)
+ outport_word (cs->io_base, *src++);
+
+ if (len)
+ outport_word (cs->io_base, *src++);
+}
+
+unsigned short cs8900_get_data_block (cs8900_device *cs, unsigned char *data)
+{
+ unsigned short *dst;
+ int cnt;
+ int len;
+
+ /*
+ * Drop the Rx status first.
+ */
+ inport_word (cs->io_base, len);
+
+ /*
+ * Now the length.
+ */
+ inport_word (cs->io_base, len);
+
+ dst = (unsigned short *) ((unsigned long) data);
+ cnt = len >> 1;
+
+ while (cnt--)
+ inport_word (cs->io_base, *dst++);
+
+ if (len & 1)
+ inport_word (cs->io_base, *dst++);
+
+ return len;
+}
+
+void
+cs8900_tx_load (cs8900_device *cs, struct mbuf *m)
+{
+ unsigned int len;
+ unsigned char *src = 0;
+ unsigned short *wsrc = 0;
+ unsigned char remainder = 0;
+ unsigned char word[2];
+
+ while (m)
+ {
+ /*
+ * We can get empty mbufs from the stack.
+ */
+
+ len = m->m_len;
+ src = mtod (m, unsigned char*);
+
+ if (len)
+ {
+ if (remainder)
+ {
+#if HAVE_MRB_CS8900_DATA_BUS_SWAPPED
+ word[1] = *src++;
+#else
+ word[0] = *src++;
+#endif
+ outport_word (cs->io_base, *((unsigned short*) (unsigned long) &word));
+ len--;
+ remainder = 0;
+ }
+
+ if (len & 1)
+ {
+ remainder = 1;
+ len--;
+ }
+
+ wsrc = (unsigned short*) src;
+
+ for (; len; len -= 2, src += 2)
+ outport_word (cs->io_base, *wsrc++);
+
+ if (remainder)
+#if HAVE_MRB_CS8900_DATA_BUS_SWAPPED
+ word[0] = *src++;
+#else
+ word[1] = *src++;
+#endif
+ }
+
+ m = m->m_next;
+ }
+
+ if (remainder)
+ {
+#if HAVE_MRB_CS8900_DATA_BUS_SWAPPED
+ word[1] = *src++;
+#else
+ word[0] = *src++;
+#endif
+ outport_word (cs->io_base, *((unsigned short*) (unsigned long) &word));
+ }
+}
+
+void cs8900_attach_interrupt (cs8900_device *cs)
+{
+ BSP_install_rtems_irq_handler (&cs8900_irq);
+}
+
+void cs8900_detach_interrupt (cs8900_device *cs)
+{
+ BSP_remove_rtems_irq_handler (&cs8900_irq);
+}
+
+void
+BSP_cs8900_attach (unsigned long io_base, unsigned long mem_base, int intrp,
+ const char* ip, const char* nm, const char* gw)
+{
+ cs8900_device *cs = &cs8900;
+ int flags;
+ struct sockaddr_in address;
+ struct sockaddr_in netmask;
+ struct sockaddr_in broadcast;
+ struct sockaddr_in gateway;
+ int cmd;
+
+ printf ("cso: io=0x%0lx mem=0 irq=%d\n", io_base, intrp);
+
+ memset (cs, 0, sizeof (cs8900));
+
+ cs->dev = 0;
+ cs->rx_queue_size = 30;
+ cs->io_base = io_base;
+
+ if (mem_base)
+ printf ("cs8900: memory mode is currently not supported.\n");
+
+ cs->mem_base = 0;
+
+ switch (intrp)
+ {
+ case 5:
+ cs->irq_level = 3;
+ break;
+ case 10:
+ cs->irq_level = 0;
+ break;
+ case 11:
+ cs->irq_level = 1;
+ break;
+ case 12:
+ cs->irq_level = 2;
+ break;
+ default:
+ printf ("cs8900: unsupported IRQ level\n");
+ return;
+ }
+
+ cs8900_irq.name = intrp;
+
+ /*
+ * Get the MAC adress from the CS8900.
+ */
+
+ cs8900_get_mac_addr (cs, cs->mac_address);
+
+ /*
+ * Setup the BSD interface configure structure.
+ */
+
+ cs8900_ifconfig.drv_ctrl = cs;
+ cs8900_ifconfig.hardware_address = cs->mac_address;
+
+ printf ("CS8900 initialisation\n");
+
+ rtems_bsdnet_attach (&cs8900_ifconfig);
+
+ /*
+ * Configure the interface using the boot configuration.
+ */
+
+ flags = IFF_UP;
+ if (rtems_bsdnet_ifconfig (cs8900_ifconfig.name,
+ SIOCSIFFLAGS,
+ &flags) < 0)
+ {
+ printf ("error: can't bring up %s: %s\n",
+ cs8900_ifconfig.name, strerror (errno));
+ return;
+ }
+
+ if (ip && strlen (ip) && nm && strlen (nm))
+ {
+ printf ("%s: addr: %s netmask: %s gateway: %s\n",
+ cs8900_ifconfig.name, ip, nm, gw ? gw : "none");
+
+ memset (&netmask, '\0', sizeof netmask);
+ netmask.sin_len = sizeof netmask;
+ netmask.sin_family = AF_INET;
+
+ if (!inet_aton (nm, &netmask.sin_addr))
+ {
+ printf ("error: cannot parse the network mask: %s\n", nm);
+ return;
+ }
+
+ memset (&address, '\0', sizeof address);
+ address.sin_len = sizeof address;
+ address.sin_family = AF_INET;
+
+ if (!inet_aton (ip, &address.sin_addr))
+ {
+ printf ("error: cannot parse the ip address: %s\n", ip);
+ return;
+ }
+
+ if (rtems_bsdnet_ifconfig (cs8900_ifconfig.name,
+ SIOCSIFNETMASK,
+ &netmask) < 0)
+ {
+ printf ("error: can't set %s netmask: %s\n",
+ cs8900_ifconfig.name, strerror (errno));
+ return;
+ }
+
+ if (rtems_bsdnet_ifconfig (cs8900_ifconfig.name,
+ SIOCSIFADDR,
+ &address) < 0)
+ {
+ printf ("error: can't set %s address: %s\n",
+ cs8900_ifconfig.name, strerror (errno));
+ return;
+ }
+
+ memset (&broadcast, '\0', sizeof broadcast);
+ broadcast.sin_len = sizeof broadcast;
+ broadcast.sin_family = AF_INET;
+ broadcast.sin_addr.s_addr =
+ (address.sin_addr.s_addr & netmask.sin_addr.s_addr) | ~netmask.sin_addr.s_addr;
+
+ if (rtems_bsdnet_ifconfig (cs8900_ifconfig.name,
+ SIOCSIFBRDADDR,
+ &broadcast) < 0)
+ {
+ printf ("error: can't set %s broadcast address: %s\n",
+ cs8900_ifconfig.name, strerror (errno));
+ return;
+ }
+
+ if (gw && strlen (gw))
+ {
+ address.sin_addr.s_addr = INADDR_ANY;
+ netmask.sin_addr.s_addr = INADDR_ANY;
+ memset (&gateway, '\0', sizeof gateway);
+ gateway.sin_len = sizeof gateway;
+ gateway.sin_family = AF_INET;
+
+ if (!inet_aton (gw, &gateway.sin_addr))
+ printf ("warning: cannot parse the gateway address: %s\n", ip);
+ else
+ {
+ if (rtems_bsdnet_rtrequest (RTM_ADD,
+ (struct sockaddr *) &address,
+ (struct sockaddr *) &gateway,
+ (struct sockaddr *) &netmask,
+ (RTF_UP | RTF_GATEWAY | RTF_STATIC), NULL) < 0)
+ printf ("error: can't set default route: %s\n", strerror (errno));
+ }
+ }
+ }
+ else
+ {
+ rtems_bsdnet_do_bootp_and_rootfs ();
+ }
+
+ for (cmd = 0;
+ cmd < sizeof (rtems_bsdnet_commands) / sizeof (rtems_monitor_command_entry_t);
+ cmd++)
+ rtems_monitor_insert_cmd (&rtems_bsdnet_commands[cmd]);
+}
diff --git a/bsps/shared/net/cs8900.h.bsp b/bsps/shared/net/cs8900.h.bsp
new file mode 100644
index 0000000000..65ce0d2e93
--- /dev/null
+++ b/bsps/shared/net/cs8900.h.bsp
@@ -0,0 +1,38 @@
+/*
+ * RTEMS CS8900 Driver Setup for the DIMM-PC/i386 made by Kontron.
+ *
+ * Port to the DIMM PC copyright (c) 2004 Angelo Fraietta
+ * This project has been assisted by the Commonwealth Government
+ * through the Australia Council, its arts funding and advisory body.
+ *
+ * Port performed by Chris Johns, Cybertec Pty Ltd, Jan 2004.
+ * Based on the Cybertec CS8900 driver setup for the SFP-101.
+ */
+
+#if !defined (__BSP_CS8900_H__)
+#define __BSP_CS8900_H__
+
+/**
+ * BSP CS8900 Device initialisation and interface attach.
+ *
+ * @param io_base The I/O base address of the device.
+ *
+ * @param mem_base The memory base address. Currently not used.
+ *
+ * @param intrp The ISA bus IRQ. These are currently limited to
+ * 5, 10, 11, and 12 as documented in the CS8900
+ * manual.
+ *
+ * @param ip IP address in ASCII. For example 10.10.10.10. If the
+ * pointer is a NULL (0) the interface will BOOTP.
+ *
+ * @param nm Network Mask in ASCII. For example 10.10.10.255.
+ *
+ * @param gw Address of the gateway machine. For example
+ * 10.10.10.1.
+ */
+
+void BSP_cs8900_attach (unsigned long io_base, unsigned long mem_base, int intrp,
+ const char* ip, const char* nm, const char* gw);
+
+#endif
diff --git a/bsps/shared/net/dec21140.c b/bsps/shared/net/dec21140.c
new file mode 100644
index 0000000000..6fd3d5b33f
--- /dev/null
+++ b/bsps/shared/net/dec21140.c
@@ -0,0 +1,1112 @@
+/*
+ * RTEMS driver for TULIP based Ethernet Controller
+ *
+ * Copyright (C) 1999 Emmanuel Raguet. raguet@crf.canon.fr
+ *
+ * The license and distribution terms for this file may be
+ * found in the file LICENSE in this distribution or at
+ * http://www.rtems.org/license/LICENSE.
+ *
+ * ------------------------------------------------------------------------
+ * [22.05.2000,StWi/CWA] added support for the DEC/Intel 21143 chip
+ *
+ * Thanks go to Andrew Klossner who provided the vital information about the
+ * Intel 21143 chip. FWIW: The 21143 additions to this driver were initially
+ * tested with a PC386 BSP using a Kingston KNE100TX with 21143PD chip.
+ *
+ * The driver will automatically detect whether there is a 21140 or 21143
+ * network card in the system and activate support accordingly. It will
+ * look for the 21140 first. If the 21140 is not found the driver will
+ * look for the 21143.
+ *
+ * 2004-11-10, Joel/Richard - 21143 support works on MVME2100.
+ * ------------------------------------------------------------------------
+ *
+ * 2003-03-13, Greg Menke, gregory.menke@gsfc.nasa.gov
+ *
+ * Added support for up to 8 units (which is an arbitrary limit now),
+ * consolidating their support into a single pair of rx/tx daemons and a
+ * single ISR for all vectors servicing the DEC units. The driver now
+ * simply uses whatever INTERRUPT_LINE the card supplies, requiring it
+ * be configured either by the boot monitor or bspstart() hackery.
+ * Tested on a MCP750 PPC based system with 2 DEC21140 boards.
+ *
+ * Also fixed a few bugs related to board configuration, start and stop.
+ *
+ */
+
+#define __INSIDE_RTEMS_BSD_TCPIP_STACK__
+
+#include <rtems.h>
+#include <inttypes.h>
+
+/*
+ * This driver only supports architectures with the new style
+ * exception processing. The following checks try to keep this
+ * from being compiled on systems which can't support this driver.
+ */
+
+#if defined(__i386__)
+ #define DEC21140_SUPPORTED
+ #define PCI_DRAM_OFFSET 0
+#endif
+#if defined(__PPC__)
+ #define DEC21140_SUPPORTED
+#endif
+
+#include <bsp.h>
+
+#if !defined(PCI_DRAM_OFFSET)
+ #undef DEC21140_SUPPORTED
+#endif
+
+#if defined(DEC21140_SUPPORTED)
+#include <rtems/pci.h>
+
+#if defined(__PPC__)
+#include <libcpu/byteorder.h>
+#include <libcpu/io.h>
+#endif
+
+#if defined(__i386__)
+#include <libcpu/byteorder.h>
+#include <libcpu/page.h>
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+#include <errno.h>
+#include <rtems/error.h>
+#include <rtems/bspIo.h>
+#include <rtems/rtems_bsdnet.h>
+
+#include <sys/param.h>
+#include <sys/mbuf.h>
+
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <netinet/if_ether.h>
+
+#include <bsp/irq.h>
+
+#ifdef malloc
+#undef malloc
+#endif
+#ifdef free
+#undef free
+#endif
+
+#define DEC_DEBUG
+
+/* note: the 21143 isn't really a DEC, it's an Intel chip */
+#define PCI_VENDOR_ID_DEC 0x1011
+#define PCI_DEVICE_ID_DEC_21140 0x0009
+#define PCI_DEVICE_ID_DEC_21143 0x0019
+
+#define DRIVER_PREFIX "dc"
+
+#define IO_MASK 0x3
+#define MEM_MASK 0xF
+
+/* command and status registers, 32-bit access, only if IO-ACCESS */
+#define ioCSR0 0x00 /* bus mode register */
+#define ioCSR1 0x08 /* transmit poll demand */
+#define ioCSR2 0x10 /* receive poll demand */
+#define ioCSR3 0x18 /* receive list base address */
+#define ioCSR4 0x20 /* transmit list base address */
+#define ioCSR5 0x28 /* status register */
+#define ioCSR6 0x30 /* operation mode register */
+#define ioCSR7 0x38 /* interrupt mask register */
+#define ioCSR8 0x40 /* missed frame counter */
+#define ioCSR9 0x48 /* Ethernet ROM register */
+#define ioCSR10 0x50 /* reserved */
+#define ioCSR11 0x58 /* full-duplex register */
+#define ioCSR12 0x60 /* SIA status register */
+#define ioCSR13 0x68
+#define ioCSR14 0x70
+#define ioCSR15 0x78 /* SIA general register */
+
+/* command and status registers, 32-bit access, only if MEMORY-ACCESS */
+#define memCSR0 0x00 /* bus mode register */
+#define memCSR1 0x02 /* transmit poll demand */
+#define memCSR2 0x04 /* receive poll demand */
+#define memCSR3 0x06 /* receive list base address */
+#define memCSR4 0x08 /* transmit list base address */
+#define memCSR5 0x0A /* status register */
+#define memCSR6 0x0C /* operation mode register */
+#define memCSR7 0x0E /* interrupt mask register */
+#define memCSR8 0x10 /* missed frame counter */
+#define memCSR9 0x12 /* Ethernet ROM register */
+#define memCSR10 0x14 /* reserved */
+#define memCSR11 0x16 /* full-duplex register */
+#define memCSR12 0x18 /* SIA status register */
+#define memCSR13 0x1A
+#define memCSR14 0x1C
+#define memCSR15 0x1E /* SIA general register */
+
+#define DEC_REGISTER_SIZE 0x100 /* to reserve virtual memory */
+
+
+
+
+#define RESET_CHIP 0x00000001
+#if defined(__PPC__)
+#define CSR0_MODE 0x0030e002 /* 01b08000 */
+#else
+#define CSR0_MODE 0x0020e002 /* 01b08000 */
+#endif
+#define ROM_ADDRESS 0x00004800
+#define CSR6_INIT 0x022cc000 /* 022c0000 020c0000 */
+#define CSR6_TX 0x00002000
+#define CSR6_TXRX 0x00002002
+#define IT_SETUP 0x000100c0 /* 000100e0 */
+#define CLEAR_IT 0xFFFFFFFF
+#define NO_IT 0x00000000
+
+/* message descriptor entry */
+struct MD {
+ /* used by hardware */
+ volatile uint32_t status;
+ volatile uint32_t counts;
+ volatile uint32_t buf1, buf2;
+ /* used by software */
+ volatile struct mbuf *m;
+ volatile struct MD *next;
+} __attribute__ ((packed));
+
+/*
+** These buffers allocated for each unit, so ensure
+**
+** rtems_bsdnet_config.mbuf_bytecount
+** rtems_bsdnet_config.mbuf_cluster_bytecount
+**
+** are adequately sized to provide enough clusters and mbufs for all the
+** units. The default bsdnet configuration is sufficient for one dec
+** unit, but will be nearing exhaustion with 2 or more. Although a
+** little expensive in memory, the following configuration should
+** eliminate all mbuf/cluster issues;
+**
+** rtems_bsdnet_config.mbuf_bytecount = 128*1024;
+** rtems_bsdnet_config.mbuf_cluster_bytecount = 256*1024;
+*/
+
+#define NRXBUFS 16 /* number of receive buffers */
+#define NTXBUFS 16 /* number of transmit buffers */
+
+/*
+ * Number of DEC boards supported by this driver
+ */
+#define NDECDRIVER 8
+
+/*
+ * Receive buffer size -- Allow for a full ethernet packet including CRC
+ */
+#define RBUF_SIZE 1536
+
+#define ET_MINLEN 60 /* minimum message length */
+
+/*
+** Events, one per unit. The event is sent to the rx task from the isr
+** or from the stack to the tx task whenever a unit needs service. The
+** rx/tx tasks identify the requesting unit(s) by their particular
+** events so only requesting units are serviced.
+*/
+
+static rtems_event_set unit_signals[NDECDRIVER]= { RTEMS_EVENT_1,
+ RTEMS_EVENT_2,
+ RTEMS_EVENT_3,
+ RTEMS_EVENT_4,
+ RTEMS_EVENT_5,
+ RTEMS_EVENT_6,
+ RTEMS_EVENT_7,
+ RTEMS_EVENT_8 };
+
+#if defined(__PPC__)
+#define phys_to_bus(address) ((unsigned int)((address)) + PCI_DRAM_OFFSET)
+#define bus_to_phys(address) ((unsigned int)((address)) - PCI_DRAM_OFFSET)
+#define CPU_CACHE_ALIGNMENT_FOR_BUFFER PPC_CACHE_ALIGNMENT
+#else
+extern void Wait_X_ms( unsigned int timeToWait );
+#define phys_to_bus(address) ((unsigned int) ((address)))
+#define bus_to_phys(address) ((unsigned int) ((address)))
+#define rtems_bsp_delay_in_bus_cycles(cycle) Wait_X_ms( cycle/100 )
+#define CPU_CACHE_ALIGNMENT_FOR_BUFFER PG_SIZE
+#endif
+
+#if (MCLBYTES < RBUF_SIZE)
+# error "Driver must have MCLBYTES > RBUF_SIZE"
+#endif
+
+/*
+ * Per-device data
+ */
+struct dec21140_softc {
+
+ struct arpcom arpcom;
+
+ rtems_irq_connect_data irqInfo;
+ rtems_event_set ioevent;
+
+ int numRxbuffers, numTxbuffers;
+
+ volatile struct MD *MDbase;
+ volatile struct MD *nextRxMD;
+ volatile unsigned char *bufferBase;
+ int acceptBroadcast;
+
+ volatile struct MD *TxMD;
+ volatile struct MD *SentTxMD;
+ int PendingTxCount;
+ int TxSuspended;
+
+ unsigned int port;
+ volatile uint32_t *base;
+
+ /*
+ * Statistics
+ */
+ unsigned long rxInterrupts;
+ unsigned long rxNotFirst;
+ unsigned long rxNotLast;
+ unsigned long rxGiant;
+ unsigned long rxNonOctet;
+ unsigned long rxRunt;
+ unsigned long rxBadCRC;
+ unsigned long rxOverrun;
+ unsigned long rxCollision;
+
+ unsigned long txInterrupts;
+ unsigned long txDeferred;
+ unsigned long txHeartbeat;
+ unsigned long txLateCollision;
+ unsigned long txRetryLimit;
+ unsigned long txUnderrun;
+ unsigned long txLostCarrier;
+ unsigned long txRawWait;
+};
+
+static struct dec21140_softc dec21140_softc[NDECDRIVER];
+static rtems_id rxDaemonTid;
+static rtems_id txDaemonTid;
+
+void dec21140_txDaemon (void *arg);
+
+/*
+ * This routine reads a word (16 bits) from the serial EEPROM.
+ */
+/* EEPROM_Ctrl bits. */
+#define EE_SHIFT_CLK 0x02 /* EEPROM shift clock. */
+#define EE_CS 0x01 /* EEPROM chip select. */
+#define EE_DATA_WRITE 0x04 /* EEPROM chip data in. */
+#define EE_WRITE_0 0x01
+#define EE_WRITE_1 0x05
+#define EE_DATA_READ 0x08 /* EEPROM chip data out. */
+#define EE_ENB (0x4800 | EE_CS)
+
+/* The EEPROM commands include the alway-set leading bit. */
+#define EE_WRITE_CMD (5 << 6)
+#define EE_READ_CMD (6 << 6)
+#define EE_ERASE_CMD (7 << 6)
+
+static int eeget16(volatile uint32_t *ioaddr, int location)
+{
+ int i;
+ unsigned short retval = 0;
+ int read_cmd = location | EE_READ_CMD;
+
+ st_le32(ioaddr, EE_ENB & ~EE_CS);
+ st_le32(ioaddr, EE_ENB);
+
+ /* Shift the read command bits out. */
+ for (i = 10; i >= 0; i--) {
+ short dataval = (read_cmd & (1 << i)) ? EE_DATA_WRITE : 0;
+ st_le32(ioaddr, EE_ENB | dataval);
+ rtems_bsp_delay_in_bus_cycles(200);
+ st_le32(ioaddr, EE_ENB | dataval | EE_SHIFT_CLK);
+ rtems_bsp_delay_in_bus_cycles(200);
+ st_le32(ioaddr, EE_ENB | dataval); /* Finish EEPROM a clock tick. */
+ rtems_bsp_delay_in_bus_cycles(200);
+ }
+ st_le32(ioaddr, EE_ENB);
+
+ for (i = 16; i > 0; i--) {
+ st_le32(ioaddr, EE_ENB | EE_SHIFT_CLK);
+ rtems_bsp_delay_in_bus_cycles(200);
+ retval = (retval << 1) | ((ld_le32(ioaddr) & EE_DATA_READ) ? 1 : 0);
+ st_le32(ioaddr, EE_ENB);
+ rtems_bsp_delay_in_bus_cycles(200);
+ }
+
+ /* Terminate the EEPROM access. */
+ st_le32(ioaddr, EE_ENB & ~EE_CS);
+ return ( ((retval<<8)&0xff00) | ((retval>>8)&0xff) );
+}
+
+static void no_op(const rtems_irq_connect_data* irq)
+{
+ return;
+}
+
+/*
+ * DEC21140 interrupt handler
+ */
+static rtems_isr
+dec21140Enet_interrupt_handler ( struct dec21140_softc *sc )
+{
+ volatile uint32_t *tbase;
+ uint32_t status;
+
+ tbase = (uint32_t*)(sc->base);
+
+ /*
+ * Read status
+ */
+ status = ld_le32(tbase+memCSR5);
+ st_le32((tbase+memCSR5), status);
+
+ /*
+ * Frame received?
+ */
+ if( status & 0x000000c0 )
+ {
+ sc->rxInterrupts++;
+ rtems_bsdnet_event_send(rxDaemonTid, sc->ioevent);
+ }
+}
+
+static rtems_isr
+dec21140Enet_interrupt_handler_entry(void)
+{
+ int i;
+
+ /*
+ ** Check all the initialized dec units for interrupt service
+ */
+
+ for(i=0; i< NDECDRIVER; i++ )
+ {
+ if( dec21140_softc[i].base )
+ dec21140Enet_interrupt_handler( &dec21140_softc[i] );
+ }
+}
+
+/*
+ * Initialize the ethernet hardware
+ */
+static void
+dec21140Enet_initialize_hardware (struct dec21140_softc *sc)
+{
+ int i,st;
+ volatile uint32_t *tbase;
+ volatile unsigned char *cp, *setup_frm, *eaddrs;
+ volatile unsigned char *buffer;
+ volatile struct MD *rmd;
+
+
+#ifdef DEC_DEBUG
+ printk("dec2114x : %02x:%02x:%02x:%02x:%02x:%02x name '%s%d', io %x, mem %x, int %d\n",
+ sc->arpcom.ac_enaddr[0], sc->arpcom.ac_enaddr[1],
+ sc->arpcom.ac_enaddr[2], sc->arpcom.ac_enaddr[3],
+ sc->arpcom.ac_enaddr[4], sc->arpcom.ac_enaddr[5],
+ sc->arpcom.ac_if.if_name, sc->arpcom.ac_if.if_unit,
+ sc->port, (unsigned) sc->base, sc->irqInfo.name );
+#endif
+
+ tbase = sc->base;
+
+ /*
+ * WARNING : First write in CSR6
+ * Then Reset the chip ( 1 in CSR0)
+ */
+ st_le32( (tbase+memCSR6), CSR6_INIT);
+ st_le32( (tbase+memCSR0), RESET_CHIP);
+ rtems_bsp_delay_in_bus_cycles(200);
+
+ st_le32( (tbase+memCSR7), NO_IT);
+
+ /*
+ * Init CSR0
+ */
+ st_le32( (tbase+memCSR0), CSR0_MODE);
+
+ /*
+ * Init RX ring
+ */
+ cp = (volatile unsigned char *)malloc(((sc->numRxbuffers+sc->numTxbuffers)*sizeof(struct MD))
+ + (sc->numTxbuffers*RBUF_SIZE)
+ + CPU_CACHE_ALIGNMENT_FOR_BUFFER);
+ sc->bufferBase = cp;
+ cp += (CPU_CACHE_ALIGNMENT_FOR_BUFFER - (int)cp) & (CPU_CACHE_ALIGNMENT_FOR_BUFFER - 1);
+#if defined(__i386__)
+#ifdef PCI_BRIDGE_DOES_NOT_ENSURE_CACHE_COHERENCY_FOR_DMA
+ if (_CPU_is_paging_enabled())
+ _CPU_change_memory_mapping_attribute
+ (NULL, cp,
+ ((sc->numRxbuffers+sc->numTxbuffers)*sizeof(struct MD))
+ + (sc->numTxbuffers*RBUF_SIZE),
+ PTE_CACHE_DISABLE | PTE_WRITABLE);
+#endif
+#endif
+ rmd = (volatile struct MD*)cp;
+ sc->MDbase = rmd;
+ sc->nextRxMD = sc->MDbase;
+
+ buffer = cp + ((sc->numRxbuffers+sc->numTxbuffers)*sizeof(struct MD));
+ st_le32( (tbase+memCSR3), (long)(phys_to_bus((long)(sc->MDbase))));
+
+ for (i=0 ; i<sc->numRxbuffers; i++)
+ {
+ struct mbuf *m;
+
+ /* allocate an mbuf for each receive descriptor */
+ MGETHDR (m, M_WAIT, MT_DATA);
+ MCLGET (m, M_WAIT);
+ m->m_pkthdr.rcvif = &sc->arpcom.ac_if;
+ rmd->m = m;
+
+ rmd->buf2 = phys_to_bus(rmd+1);
+ rmd->buf1 = phys_to_bus(mtod(m, void *));
+ rmd->status = 0x80000000;
+ rmd->counts = 0xfdc00000 | (RBUF_SIZE);
+ rmd->next = rmd+1;
+ rmd++;
+ }
+ /*
+ * mark last RX buffer.
+ */
+ sc->MDbase [sc->numRxbuffers-1].buf2 = 0;
+ sc->MDbase [sc->numRxbuffers-1].counts = 0xfec00000 | (RBUF_SIZE);
+ sc->MDbase [sc->numRxbuffers-1].next = sc->MDbase;
+
+
+
+ /*
+ * Init TX ring
+ */
+ st_le32( (tbase+memCSR4), (long)(phys_to_bus((long)(rmd))) );
+ for (i=0 ; i<sc->numTxbuffers; i++){
+ (rmd+i)->buf2 = phys_to_bus(rmd+i+1);
+ (rmd+i)->buf1 = phys_to_bus(buffer + (i*RBUF_SIZE));
+ (rmd+i)->counts = 0x01000000;
+ (rmd+i)->status = 0x0;
+ (rmd+i)->next = rmd+i+1;
+ (rmd+i)->m = 0;
+ }
+
+ /*
+ * mark last TX buffer.
+ */
+ (rmd+sc->numTxbuffers-1)->buf2 = phys_to_bus(rmd);
+ (rmd+sc->numTxbuffers-1)->next = rmd;
+
+
+ /*
+ * Build setup frame
+ */
+ setup_frm = (volatile unsigned char *)(bus_to_phys(rmd->buf1));
+ eaddrs = (unsigned char *)(sc->arpcom.ac_enaddr);
+ /* Fill the buffer with our physical address. */
+ for (i = 1; i < 16; i++) {
+ *setup_frm++ = eaddrs[0];
+ *setup_frm++ = eaddrs[1];
+ *setup_frm++ = eaddrs[0];
+ *setup_frm++ = eaddrs[1];
+ *setup_frm++ = eaddrs[2];
+ *setup_frm++ = eaddrs[3];
+ *setup_frm++ = eaddrs[2];
+ *setup_frm++ = eaddrs[3];
+ *setup_frm++ = eaddrs[4];
+ *setup_frm++ = eaddrs[5];
+ *setup_frm++ = eaddrs[4];
+ *setup_frm++ = eaddrs[5];
+ }
+
+ /* Add the broadcast address when doing perfect filtering */
+ memset((void*) setup_frm, 0xff, 12);
+ rmd->counts = 0x09000000 | 192 ;
+ rmd->status = 0x80000000;
+ st_le32( (tbase+memCSR6), CSR6_INIT | CSR6_TX);
+ st_le32( (tbase+memCSR1), 1);
+
+ while (rmd->status != 0x7fffffff);
+ rmd->counts = 0x01000000;
+
+ sc->TxMD = rmd+1;
+
+ sc->irqInfo.hdl = (rtems_irq_hdl)dec21140Enet_interrupt_handler_entry;
+ sc->irqInfo.on = no_op;
+ sc->irqInfo.off = no_op;
+ sc->irqInfo.isOn = NULL;
+
+#ifdef DEC_DEBUG
+ printk( "dec2114x: Installing IRQ %d\n", sc->irqInfo.name );
+#endif
+#ifdef BSP_SHARED_HANDLER_SUPPORT
+ st = BSP_install_rtems_shared_irq_handler( &sc->irqInfo );
+#else
+ st = BSP_install_rtems_irq_handler( &sc->irqInfo );
+#endif
+
+ if (!st)
+ rtems_panic ("dec2114x : Interrupt name %d already in use\n", sc->irqInfo.name );
+}
+
+static void
+dec21140_rxDaemon (void *arg)
+{
+ volatile struct MD *rmd;
+ struct dec21140_softc *sc;
+ struct ifnet *ifp;
+ struct ether_header *eh;
+ struct mbuf *m;
+ unsigned int i,len;
+ rtems_event_set events;
+
+ for (;;)
+ {
+
+ rtems_bsdnet_event_receive( RTEMS_ALL_EVENTS,
+ RTEMS_WAIT|RTEMS_EVENT_ANY,
+ RTEMS_NO_TIMEOUT,
+ &events);
+
+ for(i=0; i< NDECDRIVER; i++ )
+ {
+ sc = &dec21140_softc[i];
+ if( sc->base )
+ {
+ if( events & sc->ioevent )
+ {
+ ifp = &sc->arpcom.ac_if;
+ rmd = sc->nextRxMD;
+
+ /*
+ ** Read off all the packets we've received on this unit
+ */
+ while((rmd->status & 0x80000000) == 0)
+ {
+ /* printk("unit %i rx\n", ifp->if_unit ); */
+
+ /* pass on the packet in the mbuf */
+ len = (rmd->status >> 16) & 0x7ff;
+ m = (struct mbuf *)(rmd->m);
+ m->m_len = m->m_pkthdr.len = len - sizeof(struct ether_header);
+ eh = mtod (m, struct ether_header *);
+ m->m_data += sizeof(struct ether_header);
+ ether_input (ifp, eh, m);
+
+ /* get a new mbuf for the 21140 */
+ MGETHDR (m, M_WAIT, MT_DATA);
+ MCLGET (m, M_WAIT);
+ m->m_pkthdr.rcvif = ifp;
+ rmd->m = m;
+ rmd->buf1 = phys_to_bus(mtod(m, void *));
+
+ /* mark the descriptor as ready to receive */
+ rmd->status = 0x80000000;
+
+ rmd=rmd->next;
+ }
+
+ sc->nextRxMD = rmd;
+ }
+ }
+ }
+
+ }
+}
+
+static void
+sendpacket (struct ifnet *ifp, struct mbuf *m)
+{
+ struct dec21140_softc *dp = ifp->if_softc;
+ volatile struct MD *tmd;
+ volatile unsigned char *temp;
+ struct mbuf *n;
+ unsigned int len;
+ volatile uint32_t *tbase;
+
+ tbase = dp->base;
+ /*
+ * Waiting for Transmitter ready
+ */
+
+ tmd = dp->TxMD;
+ n = m;
+
+ while ((tmd->status & 0x80000000) != 0)
+ {
+ tmd=tmd->next;
+ }
+
+ len = 0;
+ temp = (volatile unsigned char *)(bus_to_phys(tmd->buf1));
+
+ for (;;)
+ {
+ len += m->m_len;
+ memcpy((void*) temp, (char *)m->m_data, m->m_len);
+ temp += m->m_len ;
+ if ((m = m->m_next) == NULL)
+ break;
+ }
+
+ if (len < ET_MINLEN) len = ET_MINLEN;
+ tmd->counts = 0xe1000000 | (len & 0x7ff);
+ tmd->status = 0x80000000;
+
+ st_le32( (tbase+memCSR1), 0x1);
+
+ m_freem(n);
+
+ dp->TxMD = tmd->next;
+}
+
+/*
+ * Driver transmit daemon
+ */
+void
+dec21140_txDaemon (void *arg)
+{
+ struct dec21140_softc *sc;
+ struct ifnet *ifp;
+ struct mbuf *m;
+ int i;
+ rtems_event_set events;
+
+ for (;;)
+ {
+ /*
+ * Wait for packets bound for any of the dec units
+ */
+ rtems_bsdnet_event_receive( RTEMS_ALL_EVENTS,
+ RTEMS_EVENT_ANY | RTEMS_WAIT,
+ RTEMS_NO_TIMEOUT, &events);
+
+ for(i=0; i< NDECDRIVER; i++ )
+ {
+ sc = &dec21140_softc[i];
+ if( sc->base )
+ {
+ if( events & sc->ioevent )
+ {
+ ifp = &sc->arpcom.ac_if;
+
+ /*
+ * Send packets till queue is empty
+ */
+ for(;;)
+ {
+ IF_DEQUEUE(&ifp->if_snd, m);
+ if( !m ) break;
+ /* printk("unit %i tx\n", ifp->if_unit ); */
+ sendpacket (ifp, m);
+ }
+
+ ifp->if_flags &= ~IFF_OACTIVE;
+ }
+ }
+ }
+
+ }
+}
+
+static void
+dec21140_start (struct ifnet *ifp)
+{
+ struct dec21140_softc *sc = ifp->if_softc;
+ rtems_bsdnet_event_send( txDaemonTid, sc->ioevent );
+ ifp->if_flags |= IFF_OACTIVE;
+}
+
+/*
+ * Initialize and start the device
+ */
+static void
+dec21140_init (void *arg)
+{
+ struct dec21140_softc *sc = arg;
+ struct ifnet *ifp = &sc->arpcom.ac_if;
+ volatile uint32_t *tbase;
+
+ /*
+ * Set up DEC21140 hardware if its not already been done
+ */
+ if( !sc->irqInfo.hdl )
+ {
+ dec21140Enet_initialize_hardware (sc);
+ }
+
+ /*
+ * Enable RX and TX
+ */
+ tbase = sc->base;
+ st_le32( (tbase+memCSR5), IT_SETUP);
+ st_le32( (tbase+memCSR7), IT_SETUP);
+ st_le32( (tbase+memCSR6), CSR6_INIT | CSR6_TXRX);
+
+ /*
+ * Tell the world that we're running.
+ */
+ ifp->if_flags |= IFF_RUNNING;
+}
+
+/*
+ * Stop the device
+ */
+static void
+dec21140_stop (struct dec21140_softc *sc)
+{
+ volatile uint32_t *tbase;
+ struct ifnet *ifp = &sc->arpcom.ac_if;
+
+ ifp->if_flags &= ~IFF_RUNNING;
+
+ /*
+ * Stop the transmitter
+ */
+ tbase = sc->base;
+ st_le32( (tbase+memCSR7), NO_IT);
+ st_le32( (tbase+memCSR6), CSR6_INIT);
+
+ /* free((void*)sc->bufferBase); */
+}
+
+/*
+ * Show interface statistics
+ */
+static void
+dec21140_stats (struct dec21140_softc *sc)
+{
+ printf (" Rx Interrupts:%-8lu", sc->rxInterrupts);
+ printf (" Not First:%-8lu", sc->rxNotFirst);
+ printf (" Not Last:%-8lu\n", sc->rxNotLast);
+ printf (" Giant:%-8lu", sc->rxGiant);
+ printf (" Runt:%-8lu", sc->rxRunt);
+ printf (" Non-octet:%-8lu\n", sc->rxNonOctet);
+ printf (" Bad CRC:%-8lu", sc->rxBadCRC);
+ printf (" Overrun:%-8lu", sc->rxOverrun);
+ printf (" Collision:%-8lu\n", sc->rxCollision);
+
+ printf (" Tx Interrupts:%-8lu", sc->txInterrupts);
+ printf (" Deferred:%-8lu", sc->txDeferred);
+ printf (" Missed Hearbeat:%-8lu\n", sc->txHeartbeat);
+ printf (" No Carrier:%-8lu", sc->txLostCarrier);
+ printf ("Retransmit Limit:%-8lu", sc->txRetryLimit);
+ printf (" Late Collision:%-8lu\n", sc->txLateCollision);
+ printf (" Underrun:%-8lu", sc->txUnderrun);
+ printf (" Raw output wait:%-8lu\n", sc->txRawWait);
+}
+
+/*
+ * Driver ioctl handler
+ */
+static int
+dec21140_ioctl (struct ifnet *ifp, ioctl_command_t command, caddr_t data)
+{
+ struct dec21140_softc *sc = ifp->if_softc;
+ int error = 0;
+
+ switch (command) {
+ case SIOCGIFADDR:
+ case SIOCSIFADDR:
+ ether_ioctl (ifp, command, data);
+ break;
+
+ case SIOCSIFFLAGS:
+ switch (ifp->if_flags & (IFF_UP | IFF_RUNNING)) {
+ case IFF_RUNNING:
+ dec21140_stop (sc);
+ break;
+
+ case IFF_UP:
+ dec21140_init (sc);
+ break;
+
+ case IFF_UP | IFF_RUNNING:
+ dec21140_stop (sc);
+ dec21140_init (sc);
+ break;
+
+ default:
+ break;
+ }
+ break;
+
+ case SIO_RTEMS_SHOW_STATS:
+ dec21140_stats (sc);
+ break;
+
+ /*
+ * FIXME: All sorts of multicast commands need to be added here!
+ */
+ default:
+ error = EINVAL;
+ break;
+ }
+
+ return error;
+}
+
+
+/*
+int iftap(struct ifnet *ifp, struct ether_header *eh, struct mbuf *m )
+{
+ int i;
+
+ if( ifp->if_unit == 1 ) return 0;
+
+ printf("unit %i, src ", ifp->if_unit );
+ for(i=0; i< ETHER_ADDR_LEN; i++) printf("%02x", (char) eh->ether_shost[i] );
+ printf(" dest ");
+ for(i=0; i< ETHER_ADDR_LEN; i++) printf("%02x", (char) eh->ether_dhost[i] );
+ printf("\n");
+
+ return -1;
+}
+*/
+
+/*
+ * Attach an DEC21140 driver to the system
+ */
+int
+rtems_dec21140_driver_attach (struct rtems_bsdnet_ifconfig *config, int attach)
+{
+ struct dec21140_softc *sc;
+ struct ifnet *ifp;
+ char *unitName;
+ int unitNumber;
+ int mtu;
+ unsigned char cvalue;
+#if defined(__i386__)
+ uint32_t value;
+ uint8_t interrupt;
+#endif
+ int pbus, pdev, pfun;
+#if defined(__PPC__)
+ int tmp;
+ uint32_t lvalue;
+#endif
+
+ /*
+ * Get the instance number for the board we're going to configure
+ * from the user.
+ */
+ if( (unitNumber = rtems_bsdnet_parse_driver_name(config, &unitName)) == -1 )
+ {
+ return 0;
+ }
+ if( strcmp(unitName, DRIVER_PREFIX) )
+ {
+ printk("dec2114x : unit name '%s' not %s\n", unitName, DRIVER_PREFIX );
+ return 0;
+ }
+
+ /*
+ * Find the board
+ */
+ if ( pci_find_device(PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_DEC_21140,
+ unitNumber-1, &pbus, &pdev, &pfun) == -1 ) {
+ if ( pci_find_device(PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_DEC_21143,
+ unitNumber-1, &pbus, &pdev, &pfun) != -1 ) {
+
+ /* the 21143 chip must be enabled before it can be accessed */
+#if defined(__i386__)
+ pci_write_config_dword(pbus, pdev, pfun, 0x40, 0 );
+#else
+ pci_write_config_dword(pbus, pdev, pfun, 0x40, PCI_DEVICE_ID_DEC_21143);
+#endif
+
+ } else {
+ printk("dec2114x : device '%s' not found on PCI bus\n", config->name );
+ return 0;
+ }
+ }
+
+#ifdef DEC_DEBUG
+ else {
+ printk("dec21140 : found device '%s', bus 0x%02x, dev 0x%02x, func 0x%02x\n",
+ config->name, pbus, pdev, pfun);
+ }
+#endif
+
+ if ((unitNumber < 1) || (unitNumber > NDECDRIVER))
+ {
+ printk("dec2114x : unit %i is invalid, must be (1 <= n <= %d)\n",
+ unitNumber, NDECDRIVER);
+ return 0;
+ }
+
+ sc = &dec21140_softc[unitNumber - 1];
+ ifp = &sc->arpcom.ac_if;
+ if (ifp->if_softc != NULL)
+ {
+ printk("dec2114x : unit %i already in use.\n", unitNumber );
+ return 0;
+ }
+
+
+ /*
+ ** Get this unit's rx/tx event
+ */
+ sc->ioevent = unit_signals[unitNumber-1];
+
+ /*
+ ** Save the buffer counts
+ */
+ sc->numRxbuffers = (config->rbuf_count) ? config->rbuf_count : NRXBUFS;
+ sc->numTxbuffers = (config->xbuf_count) ? config->xbuf_count : NTXBUFS;
+
+
+ /*
+ * Get card address spaces & retrieve its isr vector
+ */
+#if defined(__i386__)
+
+ pci_read_config_dword(pbus, pdev, pfun, 16, &value);
+ sc->port = value & ~IO_MASK;
+
+ pci_read_config_dword(pbus, pdev, pfun, 20, &value);
+ if (_CPU_is_paging_enabled())
+ _CPU_map_phys_address((void **) &(sc->base),
+ (void *)(value & ~MEM_MASK),
+ DEC_REGISTER_SIZE ,
+ PTE_CACHE_DISABLE | PTE_WRITABLE);
+ else
+ sc->base = (uint32_t *)(value & ~MEM_MASK);
+
+ pci_read_config_byte(pbus, pdev, pfun, 60, &interrupt);
+ cvalue = interrupt;
+#endif
+#if defined(__PPC__)
+ (void)pci_read_config_dword(pbus,
+ pdev,
+ pfun,
+ PCI_BASE_ADDRESS_0,
+ &lvalue);
+
+ sc->port = lvalue & (unsigned int)(~IO_MASK);
+
+ (void)pci_read_config_dword(pbus,
+ pdev,
+ pfun,
+ PCI_BASE_ADDRESS_1,
+ &lvalue);
+
+ tmp = (unsigned int)(lvalue & (unsigned int)(~MEM_MASK))
+ + (unsigned int)PCI_MEM_BASE;
+
+ sc->base = (uint32_t*)(tmp);
+
+ pci_read_config_byte(pbus,
+ pdev,
+ pfun,
+ PCI_INTERRUPT_LINE,
+ &cvalue);
+
+#endif
+
+ /*
+ ** Prep the board
+ */
+
+ pci_write_config_word(pbus, pdev, pfun,
+ PCI_COMMAND,
+ (uint16_t) ( PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER ) );
+
+ /*
+ ** Store the interrupt name, we'll use it later when we initialize
+ ** the board.
+ */
+ memset(&sc->irqInfo,0,sizeof(rtems_irq_connect_data));
+ sc->irqInfo.name = cvalue;
+
+
+#ifdef DEC_DEBUG
+ printk("dec2114x : unit %d base address %p.\n", unitNumber, sc->base);
+#endif
+
+
+ /*
+ ** Setup ethernet address
+ */
+ if (config->hardware_address) {
+ memcpy (sc->arpcom.ac_enaddr, config->hardware_address,
+ ETHER_ADDR_LEN);
+ }
+ else {
+ union {char c[64]; unsigned short s[32];} rombuf;
+ int i;
+
+ for (i=0; i<32; i++){
+ rombuf.s[i] = eeget16( sc->base + memCSR9, i);
+ }
+#if defined(__i386__)
+ for (i=0 ; i<(ETHER_ADDR_LEN/2); i++){
+ sc->arpcom.ac_enaddr[2*i] = rombuf.c[20+2*i+1];
+ sc->arpcom.ac_enaddr[2*i+1] = rombuf.c[20+2*i];
+ }
+#endif
+#if defined(__PPC__)
+ memcpy (sc->arpcom.ac_enaddr, rombuf.c+20, ETHER_ADDR_LEN);
+#endif
+ }
+
+ if (config->mtu)
+ mtu = config->mtu;
+ else
+ mtu = ETHERMTU;
+
+ sc->acceptBroadcast = !config->ignore_broadcast;
+
+ /*
+ * Set up network interface values
+ */
+
+/* ifp->if_tap = iftap; */
+
+ ifp->if_softc = sc;
+ ifp->if_unit = unitNumber;
+ ifp->if_name = unitName;
+ ifp->if_mtu = mtu;
+ ifp->if_init = dec21140_init;
+ ifp->if_ioctl = dec21140_ioctl;
+ ifp->if_start = dec21140_start;
+ ifp->if_output = ether_output;
+ ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX;
+ if (ifp->if_snd.ifq_maxlen == 0)
+ ifp->if_snd.ifq_maxlen = ifqmaxlen;
+
+ /*
+ * Attach the interface
+ */
+ if_attach (ifp);
+ ether_ifattach (ifp);
+
+#ifdef DEC_DEBUG
+ printk( "dec2114x : driver attached\n" );
+#endif
+
+ /*
+ * Start driver tasks if this is the first dec unit initialized
+ */
+ if (txDaemonTid == 0)
+ {
+ rxDaemonTid = rtems_bsdnet_newproc( "DCrx", 4096,
+ dec21140_rxDaemon, NULL);
+
+ txDaemonTid = rtems_bsdnet_newproc( "DCtx", 4096,
+ dec21140_txDaemon, NULL);
+#ifdef DEC_DEBUG
+ printk( "dec2114x : driver tasks created\n" );
+#endif
+ }
+
+ return 1;
+};
+
+#endif /* DEC21140_SUPPORTED */
diff --git a/bsps/shared/net/elnk.c b/bsps/shared/net/elnk.c
new file mode 100644
index 0000000000..29adbe9c21
--- /dev/null
+++ b/bsps/shared/net/elnk.c
@@ -0,0 +1,3553 @@
+/*
+ * RTEMS driver for Etherlink based Ethernet Controllers
+ *
+ * Copyright (C) 2003, Gregory Menke, NASA/GSFC
+ *
+ * The license and distribution terms for this file may be
+ * found in the file LICENSE in this distribution or at
+ * http://www.rtems.org/license/LICENSE.
+ *
+ * elnk.c
+ *
+ *
+ */
+
+
+/*
+ * Portions of this driver are taken from the Freebsd if_xl.c driver,
+ * version "1.133 2003/03/19 01:48:14" and are covered by the license
+ * text included below that was taken verbatim from the original file.
+ * More particularly, all structures, variables, and #defines prefixed
+ * with XL_ or xl_, along with their associated comments were taken
+ * directly from the Freebsd driver and modified as required to suit the
+ * purposes of this one. Additionally, much of the device setup &
+ * manipulation logic was also copied and modified to suit. All types
+ * and functions beginning with elnk are either my own creations or were
+ * adapted from other RTEMS components, and regardless, are subject to
+ * the standard OAR licensing terms given in the comments at the top of
+ * this file.
+ *
+ * Greg Menke, 6/11/2003
+ */
+
+ /*
+ * Copyright (c) 1997, 1998, 1999
+ * Bill Paul <wpaul@ctr.columbia.edu>. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Bill Paul.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Bill Paul 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 Bill Paul OR THE VOICES IN HIS HEAD
+ * 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.
+ */
+
+#define __INSIDE_RTEMS_BSD_TCPIP_STACK__
+
+#include <rtems.h>
+
+/*
+ * This driver only supports architectures with the new style
+ * exception processing. The following checks try to keep this
+ * from being compiled on systems which can't support this driver.
+ */
+
+#if defined(__i386__)
+#define ELNK_SUPPORTED
+ #define PCI_DRAM_OFFSET 0
+#endif
+
+#if defined(__PPC__)
+#define ELNK_SUPPORTED
+#endif
+
+#include <bsp.h>
+
+#if !defined(PCI_DRAM_OFFSET)
+ #undef ELNK_SUPPORTED
+#endif
+
+/* #undef ELNK_SUPPORTED */
+
+
+#if defined(ELNK_SUPPORTED)
+#include <rtems/pci.h>
+
+#if defined(__PPC__)
+#include <libcpu/byteorder.h>
+#include <libcpu/io.h>
+#endif
+
+#if defined(__i386__)
+#include <libcpu/byteorder.h>
+#endif
+
+#include <inttypes.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+#include <errno.h>
+#include <rtems/error.h>
+#include <rtems/bspIo.h>
+#include <rtems/rtems_bsdnet.h>
+
+#include <sys/param.h>
+#include <sys/mbuf.h>
+
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <netinet/if_ether.h>
+
+#include <net/if_media.h>
+#include <dev/mii/mii.h>
+#include <bsp/irq.h>
+
+#if defined(__i386__)
+#define IO_MASK 0x3
+#define MEM_MASK 0xF
+#endif
+
+#ifdef malloc
+#undef malloc
+#endif
+#ifdef free
+#undef free
+#endif
+
+#define ELNK_DEBUG
+
+
+#define DRIVER_PREFIX "elnk"
+
+
+
+
+
+/*
+* These buffers allocated for each unit, so ensure
+*
+* rtems_bsdnet_config.mbuf_bytecount
+* rtems_bsdnet_config.mbuf_cluster_bytecount
+*
+* are adequately sized to provide enough clusters and mbufs for all the
+* units. The default bsdnet configuration is sufficient for one unit,
+* but will be nearing exhaustion with 2 or more. Although a little
+* expensive in memory, the following configuration should eliminate all
+* mbuf/cluster issues;
+*
+* rtems_bsdnet_config.mbuf_bytecount = 128*1024;
+* rtems_bsdnet_config.mbuf_cluster_bytecount = 256*1024;
+*
+* The default size in buffers of the rx & tx rings are given below.
+* This driver honors the rtems_bsdnet_ifconfig fields 'rbuf_count' and
+* 'xbuf_count', allowing the user to specify something else.
+*/
+
+#define RX_RING_SIZE 16 /* default number of receive buffers */
+#define TX_RING_SIZE 16 /* default number of transmit buffers */
+
+
+/*
+ * Number of boards supported by this driver
+ */
+#define NUM_UNITS 8
+
+/*
+ * Receive buffer size -- Allow for a full ethernet packet including CRC
+ */
+#define XL_PACKET_SIZE 1540
+
+
+/*
+** Events, one per unit. The event is sent to the rx task from the isr
+** or from the stack to the tx task whenever a unit needs service. The
+** rx/tx tasks identify the requesting unit(s) by their particular
+** events so only requesting units are serviced.
+*/
+
+static rtems_event_set unit_signals[NUM_UNITS]= { RTEMS_EVENT_1,
+ RTEMS_EVENT_2,
+ RTEMS_EVENT_3,
+ RTEMS_EVENT_4,
+ RTEMS_EVENT_5,
+ RTEMS_EVENT_6,
+ RTEMS_EVENT_7,
+ RTEMS_EVENT_8 };
+
+
+
+
+#if defined(__PPC__)
+#define phys_to_bus(address) ((unsigned int)((address)) + PCI_DRAM_OFFSET)
+#define bus_to_phys(address) ((unsigned int)((address)) - PCI_DRAM_OFFSET)
+#define CPU_CACHE_ALIGNMENT_FOR_BUFFER PPC_CACHE_ALIGNMENT
+#else
+extern void Wait_X_ms( unsigned int timeToWait );
+#define phys_to_bus(address) ((unsigned int) ((address)))
+#define bus_to_phys(address) ((unsigned int) ((address)))
+#define rtems_bsp_delay_in_bus_cycles(cycle) Wait_X_ms( cycle/100 )
+#define CPU_CACHE_ALIGNMENT_FOR_BUFFER PG_SIZE
+#endif
+
+/* the actual duration waited in DELAY is not especially predictable,
+ * though it will be consistent on a given host. It should not be
+ * relied upon for specific timing given the vague per-bsp,
+ * per-architecture implementation of the actual delay function. It
+ * would probably be helpful to make this more accurate at some point...
+ */
+#define DELAY(n) rtems_bsp_delay_in_bus_cycles( n*20 )
+
+
+
+
+/*
+ * Register layouts.
+ */
+#define XL_COMMAND 0x0E
+#define XL_STATUS 0x0E
+
+#define XL_TX_STATUS 0x1B
+#define XL_TX_FREE 0x1C
+#define XL_DMACTL 0x20
+#define XL_DOWNLIST_PTR 0x24
+#define XL_DOWN_POLL 0x2D /* 3c90xB only */
+#define XL_TX_FREETHRESH 0x2F
+#define XL_UPLIST_PTR 0x38
+#define XL_UPLIST_STATUS 0x30
+#define XL_UP_POLL 0x3D /* 3c90xB only */
+
+#define XL_PKTSTAT_UP_STALLED 0x00002000
+#define XL_PKTSTAT_UP_ERROR 0x00004000
+#define XL_PKTSTAT_UP_CMPLT 0x00008000
+
+#define XL_DMACTL_DN_CMPLT_REQ 0x00000002
+#define XL_DMACTL_DOWN_STALLED 0x00000004
+#define XL_DMACTL_UP_CMPLT 0x00000008
+#define XL_DMACTL_DOWN_CMPLT 0x00000010
+#define XL_DMACTL_UP_RX_EARLY 0x00000020
+#define XL_DMACTL_ARM_COUNTDOWN 0x00000040
+#define XL_DMACTL_DOWN_INPROG 0x00000080
+#define XL_DMACTL_COUNTER_SPEED 0x00000100
+#define XL_DMACTL_DOWNDOWN_MODE 0x00000200
+#define XL_DMACTL_TARGET_ABORT 0x40000000
+#define XL_DMACTL_MASTER_ABORT 0x80000000
+
+/*
+ * Command codes. Some command codes require that we wait for
+ * the CMD_BUSY flag to clear. Those codes are marked as 'mustwait.'
+ */
+#define XL_CMD_RESET 0x0000 /* mustwait */
+#define XL_CMD_WINSEL 0x0800
+#define XL_CMD_COAX_START 0x1000
+#define XL_CMD_RX_DISABLE 0x1800
+#define XL_CMD_RX_ENABLE 0x2000
+#define XL_CMD_RX_RESET 0x2800 /* mustwait */
+#define XL_CMD_UP_STALL 0x3000 /* mustwait */
+#define XL_CMD_UP_UNSTALL 0x3001
+#define XL_CMD_DOWN_STALL 0x3002 /* mustwait */
+#define XL_CMD_DOWN_UNSTALL 0x3003
+#define XL_CMD_RX_DISCARD 0x4000
+#define XL_CMD_TX_ENABLE 0x4800
+#define XL_CMD_TX_DISABLE 0x5000
+#define XL_CMD_TX_RESET 0x5800 /* mustwait */
+#define XL_CMD_INTR_FAKE 0x6000
+#define XL_CMD_INTR_ACK 0x6800
+#define XL_CMD_INTR_ENB 0x7000
+#define XL_CMD_STAT_ENB 0x7800
+#define XL_CMD_RX_SET_FILT 0x8000
+#define XL_CMD_RX_SET_THRESH 0x8800
+#define XL_CMD_TX_SET_THRESH 0x9000
+#define XL_CMD_TX_SET_START 0x9800
+#define XL_CMD_DMA_UP 0xA000
+#define XL_CMD_DMA_STOP 0xA001
+#define XL_CMD_STATS_ENABLE 0xA800
+#define XL_CMD_STATS_DISABLE 0xB000
+#define XL_CMD_COAX_STOP 0xB800
+
+#define XL_CMD_SET_TX_RECLAIM 0xC000 /* 3c905B only */
+#define XL_CMD_RX_SET_HASH 0xC800 /* 3c905B only */
+
+#define XL_HASH_SET 0x0400
+#define XL_HASHFILT_SIZE 256
+
+/*
+ * status codes
+ * Note that bits 15 to 13 indicate the currently visible register window
+ * which may be anything from 0 to 7.
+ */
+#define XL_STAT_INTLATCH 0x0001 /* 0 */
+#define XL_STAT_ADFAIL 0x0002 /* 1 */
+#define XL_STAT_TX_COMPLETE 0x0004 /* 2 */
+#define XL_STAT_TX_AVAIL 0x0008 /* 3 first generation */
+#define XL_STAT_RX_COMPLETE 0x0010 /* 4 */
+#define XL_STAT_RX_EARLY 0x0020 /* 5 */
+#define XL_STAT_INTREQ 0x0040 /* 6 */
+#define XL_STAT_STATSOFLOW 0x0080 /* 7 */
+#define XL_STAT_DMADONE 0x0100 /* 8 first generation */
+#define XL_STAT_LINKSTAT 0x0100 /* 8 3c509B */
+#define XL_STAT_DOWN_COMPLETE 0x0200 /* 9 */
+#define XL_STAT_UP_COMPLETE 0x0400 /* 10 */
+#define XL_STAT_DMABUSY 0x0800 /* 11 first generation */
+#define XL_STAT_CMDBUSY 0x1000 /* 12 */
+
+/*
+ * Interrupts we normally want enabled.
+ */
+#define XL_INTRS \
+ (XL_STAT_UP_COMPLETE | XL_STAT_STATSOFLOW | XL_STAT_ADFAIL| \
+ XL_STAT_DOWN_COMPLETE | XL_STAT_TX_COMPLETE | XL_STAT_INTLATCH)
+
+
+
+/*
+ * General constants that are fun to know.
+ *
+ * 3Com PCI vendor ID
+ */
+#define TC_VENDORID 0x10B7
+
+/*
+ * 3Com chip device IDs.
+ */
+#define TC_DEVICEID_BOOMERANG_10BT 0x9000
+#define TC_DEVICEID_BOOMERANG_10BT_COMBO 0x9001
+#define TC_DEVICEID_BOOMERANG_10_100BT 0x9050
+#define TC_DEVICEID_BOOMERANG_100BT4 0x9051
+#define TC_DEVICEID_KRAKATOA_10BT 0x9004
+#define TC_DEVICEID_KRAKATOA_10BT_COMBO 0x9005
+#define TC_DEVICEID_KRAKATOA_10BT_TPC 0x9006
+#define TC_DEVICEID_CYCLONE_10FL 0x900A
+#define TC_DEVICEID_HURRICANE_10_100BT 0x9055
+#define TC_DEVICEID_CYCLONE_10_100BT4 0x9056
+#define TC_DEVICEID_CYCLONE_10_100_COMBO 0x9058
+#define TC_DEVICEID_CYCLONE_10_100FX 0x905A
+#define TC_DEVICEID_TORNADO_10_100BT 0x9200
+#define TC_DEVICEID_TORNADO_10_100BT_920B 0x9201
+#define TC_DEVICEID_HURRICANE_10_100BT_SERV 0x9800
+#define TC_DEVICEID_TORNADO_10_100BT_SERV 0x9805
+#define TC_DEVICEID_HURRICANE_SOHO100TX 0x7646
+#define TC_DEVICEID_TORNADO_HOMECONNECT 0x4500
+#define TC_DEVICEID_HURRICANE_555 0x5055
+#define TC_DEVICEID_HURRICANE_556 0x6055
+#define TC_DEVICEID_HURRICANE_556B 0x6056
+#define TC_DEVICEID_HURRICANE_575A 0x5057
+#define TC_DEVICEID_HURRICANE_575B 0x5157
+#define TC_DEVICEID_HURRICANE_575C 0x5257
+#define TC_DEVICEID_HURRICANE_656 0x6560
+#define TC_DEVICEID_HURRICANE_656B 0x6562
+#define TC_DEVICEID_TORNADO_656C 0x6564
+
+
+
+#define XL_RXSTAT_LENMASK 0x00001FFF
+#define XL_RXSTAT_UP_ERROR 0x00004000
+#define XL_RXSTAT_UP_CMPLT 0x00008000
+#define XL_RXSTAT_UP_OVERRUN 0x00010000
+#define XL_RXSTAT_RUNT 0x00020000
+#define XL_RXSTAT_ALIGN 0x00040000
+#define XL_RXSTAT_CRC 0x00080000
+#define XL_RXSTAT_OVERSIZE 0x00100000
+#define XL_RXSTAT_DRIBBLE 0x00800000
+#define XL_RXSTAT_UP_OFLOW 0x01000000
+#define XL_RXSTAT_IPCKERR 0x02000000 /* 3c905B only */
+#define XL_RXSTAT_TCPCKERR 0x04000000 /* 3c905B only */
+#define XL_RXSTAT_UDPCKERR 0x08000000 /* 3c905B only */
+#define XL_RXSTAT_BUFEN 0x10000000 /* 3c905B only */
+#define XL_RXSTAT_IPCKOK 0x20000000 /* 3c905B only */
+#define XL_RXSTAT_TCPCOK 0x40000000 /* 3c905B only */
+#define XL_RXSTAT_UDPCKOK 0x80000000 /* 3c905B only */
+
+#define XL_TXSTAT_LENMASK 0x00001FFF
+#define XL_TXSTAT_CRCDIS 0x00002000
+#define XL_TXSTAT_TX_INTR 0x00008000
+#define XL_TXSTAT_DL_COMPLETE 0x00010000
+#define XL_TXSTAT_IPCKSUM 0x02000000 /* 3c905B only */
+#define XL_TXSTAT_TCPCKSUM 0x04000000 /* 3c905B only */
+#define XL_TXSTAT_UDPCKSUM 0x08000000 /* 3c905B only */
+#define XL_TXSTAT_RND_DEFEAT 0x10000000 /* 3c905B only */
+#define XL_TXSTAT_EMPTY 0x20000000 /* 3c905B only */
+#define XL_TXSTAT_DL_INTR 0x80000000
+
+
+#define XL_FLAG_FUNCREG 0x0001
+#define XL_FLAG_PHYOK 0x0002
+#define XL_FLAG_EEPROM_OFFSET_30 0x0004
+#define XL_FLAG_WEIRDRESET 0x0008
+#define XL_FLAG_8BITROM 0x0010
+#define XL_FLAG_INVERT_LED_PWR 0x0020
+#define XL_FLAG_INVERT_MII_PWR 0x0040
+#define XL_FLAG_NO_XCVR_PWR 0x0080
+#define XL_FLAG_USE_MMIO 0x0100
+
+
+
+#define XL_EE_READ 0x0080 /* read, 5 bit address */
+#define XL_EE_WRITE 0x0040 /* write, 5 bit address */
+#define XL_EE_ERASE 0x00c0 /* erase, 5 bit address */
+#define XL_EE_EWEN 0x0030 /* erase, no data needed */
+#define XL_EE_8BIT_READ 0x0200 /* read, 8 bit address */
+#define XL_EE_BUSY 0x8000
+
+#define XL_EE_EADDR0 0x00 /* station address, first word */
+#define XL_EE_EADDR1 0x01 /* station address, next word, */
+#define XL_EE_EADDR2 0x02 /* station address, last word */
+#define XL_EE_PRODID 0x03 /* product ID code */
+#define XL_EE_MDATA_DATE 0x04 /* manufacturing data, date */
+#define XL_EE_MDATA_DIV 0x05 /* manufacturing data, division */
+#define XL_EE_MDATA_PCODE 0x06 /* manufacturing data, product code */
+#define XL_EE_MFG_ID 0x07
+#define XL_EE_PCI_PARM 0x08
+#define XL_EE_ROM_ONFO 0x09
+#define XL_EE_OEM_ADR0 0x0A
+#define XL_EE_OEM_ADR1 0x0B
+#define XL_EE_OEM_ADR2 0x0C
+#define XL_EE_SOFTINFO1 0x0D
+#define XL_EE_COMPAT 0x0E
+#define XL_EE_SOFTINFO2 0x0F
+#define XL_EE_CAPS 0x10 /* capabilities word */
+#define XL_EE_RSVD0 0x11
+#define XL_EE_ICFG_0 0x12
+#define XL_EE_ICFG_1 0x13
+#define XL_EE_RSVD1 0x14
+#define XL_EE_SOFTINFO3 0x15
+#define XL_EE_RSVD_2 0x16
+
+/*
+ * Bits in the capabilities word
+ */
+#define XL_CAPS_PNP 0x0001
+#define XL_CAPS_FULL_DUPLEX 0x0002
+#define XL_CAPS_LARGE_PKTS 0x0004
+#define XL_CAPS_SLAVE_DMA 0x0008
+#define XL_CAPS_SECOND_DMA 0x0010
+#define XL_CAPS_FULL_BM 0x0020
+#define XL_CAPS_FRAG_BM 0x0040
+#define XL_CAPS_CRC_PASSTHRU 0x0080
+#define XL_CAPS_TXDONE 0x0100
+#define XL_CAPS_NO_TXLENGTH 0x0200
+#define XL_CAPS_RX_REPEAT 0x0400
+#define XL_CAPS_SNOOPING 0x0800
+#define XL_CAPS_100MBPS 0x1000
+#define XL_CAPS_PWRMGMT 0x2000
+
+
+
+/*
+ * Window 0 registers
+ */
+#define XL_W0_EE_DATA 0x0C
+#define XL_W0_EE_CMD 0x0A
+#define XL_W0_RSRC_CFG 0x08
+#define XL_W0_ADDR_CFG 0x06
+#define XL_W0_CFG_CTRL 0x04
+
+#define XL_W0_PROD_ID 0x02
+#define XL_W0_MFG_ID 0x00
+
+/*
+ * Window 1
+ */
+
+#define XL_W1_TX_FIFO 0x10
+
+#define XL_W1_FREE_TX 0x0C
+#define XL_W1_TX_STATUS 0x0B
+#define XL_W1_TX_TIMER 0x0A
+#define XL_W1_RX_STATUS 0x08
+#define XL_W1_RX_FIFO 0x00
+
+/*
+ * RX status codes
+ */
+#define XL_RXSTATUS_OVERRUN 0x01
+#define XL_RXSTATUS_RUNT 0x02
+#define XL_RXSTATUS_ALIGN 0x04
+#define XL_RXSTATUS_CRC 0x08
+#define XL_RXSTATUS_OVERSIZE 0x10
+#define XL_RXSTATUS_DRIBBLE 0x20
+
+/*
+ * TX status codes
+ */
+#define XL_TXSTATUS_RECLAIM 0x02 /* 3c905B only */
+#define XL_TXSTATUS_OVERFLOW 0x04
+#define XL_TXSTATUS_MAXCOLS 0x08
+#define XL_TXSTATUS_UNDERRUN 0x10
+#define XL_TXSTATUS_JABBER 0x20
+#define XL_TXSTATUS_INTREQ 0x40
+#define XL_TXSTATUS_COMPLETE 0x80
+
+/*
+ * Window 2
+ */
+#define XL_W2_RESET_OPTIONS 0x0C /* 3c905B only */
+#define XL_W2_STATION_MASK_HI 0x0A
+#define XL_W2_STATION_MASK_MID 0x08
+#define XL_W2_STATION_MASK_LO 0x06
+#define XL_W2_STATION_ADDR_HI 0x04
+#define XL_W2_STATION_ADDR_MID 0x02
+#define XL_W2_STATION_ADDR_LO 0x00
+
+#define XL_RESETOPT_FEATUREMASK 0x0001|0x0002|0x004
+#define XL_RESETOPT_D3RESETDIS 0x0008
+#define XL_RESETOPT_DISADVFD 0x0010
+#define XL_RESETOPT_DISADV100 0x0020
+#define XL_RESETOPT_DISAUTONEG 0x0040
+#define XL_RESETOPT_DEBUGMODE 0x0080
+#define XL_RESETOPT_FASTAUTO 0x0100
+#define XL_RESETOPT_FASTEE 0x0200
+#define XL_RESETOPT_FORCEDCONF 0x0400
+#define XL_RESETOPT_TESTPDTPDR 0x0800
+#define XL_RESETOPT_TEST100TX 0x1000
+#define XL_RESETOPT_TEST100RX 0x2000
+
+#define XL_RESETOPT_INVERT_LED 0x0010
+#define XL_RESETOPT_INVERT_MII 0x4000
+
+/*
+ * Window 3 (fifo management)
+ */
+#define XL_W3_INTERNAL_CFG 0x00
+#define XL_W3_MAXPKTSIZE 0x04 /* 3c905B only */
+#define XL_W3_RESET_OPT 0x08
+#define XL_W3_FREE_TX 0x0C
+#define XL_W3_FREE_RX 0x0A
+#define XL_W3_MAC_CTRL 0x06
+
+#define XL_ICFG_CONNECTOR_MASK 0x00F00000
+#define XL_ICFG_CONNECTOR_BITS 20
+
+#define XL_ICFG_RAMSIZE_MASK 0x00000007
+#define XL_ICFG_RAMWIDTH 0x00000008
+#define XL_ICFG_ROMSIZE_MASK (0x00000040|0x00000080)
+#define XL_ICFG_DISABLE_BASSD 0x00000100
+#define XL_ICFG_RAMLOC 0x00000200
+#define XL_ICFG_RAMPART (0x00010000|0x00020000)
+#define XL_ICFG_XCVRSEL (0x00100000|0x00200000|0x00400000)
+#define XL_ICFG_AUTOSEL 0x01000000
+
+#define XL_XCVR_10BT 0x00
+#define XL_XCVR_AUI 0x01
+#define XL_XCVR_RSVD_0 0x02
+#define XL_XCVR_COAX 0x03
+#define XL_XCVR_100BTX 0x04
+#define XL_XCVR_100BFX 0x05
+#define XL_XCVR_MII 0x06
+#define XL_XCVR_RSVD_1 0x07
+#define XL_XCVR_AUTO 0x08 /* 3c905B only */
+
+#define XL_MACCTRL_DEFER_EXT_END 0x0001
+#define XL_MACCTRL_DEFER_0 0x0002
+#define XL_MACCTRL_DEFER_1 0x0004
+#define XL_MACCTRL_DEFER_2 0x0008
+#define XL_MACCTRL_DEFER_3 0x0010
+#define XL_MACCTRL_DUPLEX 0x0020
+#define XL_MACCTRL_ALLOW_LARGE_PACK 0x0040
+#define XL_MACCTRL_EXTEND_AFTER_COL 0x0080 (3c905B only)
+#define XL_MACCTRL_FLOW_CONTROL_ENB 0x0100 (3c905B only)
+#define XL_MACCTRL_VLT_END 0x0200 (3c905B only)
+
+/*
+ * The 'reset options' register contains power-on reset values
+ * loaded from the EEPROM. This includes the supported media
+ * types on the card. It is also known as the media options register.
+ */
+#define XL_W3_MEDIA_OPT 0x08
+
+#define XL_MEDIAOPT_BT4 0x0001 /* MII */
+#define XL_MEDIAOPT_BTX 0x0002 /* on-chip */
+#define XL_MEDIAOPT_BFX 0x0004 /* on-chip */
+#define XL_MEDIAOPT_BT 0x0008 /* on-chip */
+#define XL_MEDIAOPT_BNC 0x0010 /* on-chip */
+#define XL_MEDIAOPT_AUI 0x0020 /* on-chip */
+#define XL_MEDIAOPT_MII 0x0040 /* MII */
+#define XL_MEDIAOPT_VCO 0x0100 /* 1st gen chip only */
+
+#define XL_MEDIAOPT_10FL 0x0100 /* 3x905B only, on-chip */
+#define XL_MEDIAOPT_MASK 0x01FF
+
+/*
+ * Window 4 (diagnostics)
+ */
+#define XL_W4_UPPERBYTESOK 0x0D
+#define XL_W4_BADSSD 0x0C
+#define XL_W4_MEDIA_STATUS 0x0A
+#define XL_W4_PHY_MGMT 0x08
+#define XL_W4_NET_DIAG 0x06
+#define XL_W4_FIFO_DIAG 0x04
+#define XL_W4_VCO_DIAG 0x02
+
+#define XL_W4_CTRLR_STAT 0x08
+#define XL_W4_TX_DIAG 0x00
+
+#define XL_MII_CLK 0x01
+#define XL_MII_DATA 0x02
+#define XL_MII_DIR 0x04
+
+#define XL_MEDIA_SQE 0x0008
+#define XL_MEDIA_10TP 0x00C0
+#define XL_MEDIA_LNK 0x0080
+#define XL_MEDIA_LNKBEAT 0x0800
+
+#define XL_MEDIASTAT_CRCSTRIP 0x0004
+#define XL_MEDIASTAT_SQEENB 0x0008
+#define XL_MEDIASTAT_COLDET 0x0010
+#define XL_MEDIASTAT_CARRIER 0x0020
+#define XL_MEDIASTAT_JABGUARD 0x0040
+#define XL_MEDIASTAT_LINKBEAT 0x0080
+#define XL_MEDIASTAT_JABDETECT 0x0200
+#define XL_MEDIASTAT_POLREVERS 0x0400
+#define XL_MEDIASTAT_LINKDETECT 0x0800
+#define XL_MEDIASTAT_TXINPROG 0x1000
+#define XL_MEDIASTAT_DCENB 0x4000
+#define XL_MEDIASTAT_AUIDIS 0x8000
+
+#define XL_NETDIAG_TEST_LOWVOLT 0x0001
+#define XL_NETDIAG_ASIC_REVMASK (0x0002|0x0004|0x0008|0x0010|0x0020)
+#define XL_NETDIAG_UPPER_BYTES_ENABLE 0x0040
+#define XL_NETDIAG_STATS_ENABLED 0x0080
+#define XL_NETDIAG_TX_FATALERR 0x0100
+#define XL_NETDIAG_TRANSMITTING 0x0200
+#define XL_NETDIAG_RX_ENABLED 0x0400
+#define XL_NETDIAG_TX_ENABLED 0x0800
+#define XL_NETDIAG_FIFO_LOOPBACK 0x1000
+#define XL_NETDIAG_MAC_LOOPBACK 0x2000
+#define XL_NETDIAG_ENDEC_LOOPBACK 0x4000
+#define XL_NETDIAG_EXTERNAL_LOOP 0x8000
+
+/*
+ * Window 5
+ */
+#define XL_W5_STAT_ENB 0x0C
+#define XL_W5_INTR_ENB 0x0A
+#define XL_W5_RECLAIM_THRESH 0x09 /* 3c905B only */
+#define XL_W5_RX_FILTER 0x08
+#define XL_W5_RX_EARLYTHRESH 0x06
+#define XL_W5_TX_AVAILTHRESH 0x02
+#define XL_W5_TX_STARTTHRESH 0x00
+
+/*
+ * RX filter bits
+ */
+#define XL_RXFILTER_INDIVIDUAL 0x01
+#define XL_RXFILTER_ALLMULTI 0x02
+#define XL_RXFILTER_BROADCAST 0x04
+#define XL_RXFILTER_ALLFRAMES 0x08
+#define XL_RXFILTER_MULTIHASH 0x10 /* 3c905B only */
+
+/*
+ * Window 6 (stats)
+ */
+#define XL_W6_TX_BYTES_OK 0x0C
+#define XL_W6_RX_BYTES_OK 0x0A
+#define XL_W6_UPPER_FRAMES_OK 0x09
+#define XL_W6_DEFERRED 0x08
+#define XL_W6_RX_OK 0x07
+#define XL_W6_TX_OK 0x06
+#define XL_W6_RX_OVERRUN 0x05
+#define XL_W6_COL_LATE 0x04
+#define XL_W6_COL_SINGLE 0x03
+#define XL_W6_COL_MULTIPLE 0x02
+#define XL_W6_SQE_ERRORS 0x01
+#define XL_W6_CARRIER_LOST 0x00
+
+/*
+ * Window 7 (bus master control)
+ */
+#define XL_W7_BM_ADDR 0x00
+#define XL_W7_BM_LEN 0x06
+#define XL_W7_BM_STATUS 0x0B
+#define XL_W7_BM_TIMEr 0x0A
+
+/*
+ * bus master control registers
+ */
+#define XL_BM_PKTSTAT 0x20
+#define XL_BM_DOWNLISTPTR 0x24
+#define XL_BM_FRAGADDR 0x28
+#define XL_BM_FRAGLEN 0x2C
+#define XL_BM_TXFREETHRESH 0x2F
+#define XL_BM_UPPKTSTAT 0x30
+#define XL_BM_UPLISTPTR 0x38
+
+
+
+
+
+
+
+
+struct xl_mii_frame {
+ u_int8_t mii_stdelim;
+ u_int8_t mii_opcode;
+ u_int8_t mii_phyaddr;
+ u_int8_t mii_regaddr;
+ u_int8_t mii_turnaround;
+ u_int16_t mii_data;
+};
+
+/*
+ * MII constants
+ */
+#define XL_MII_STARTDELIM 0x01
+#define XL_MII_READOP 0x02
+#define XL_MII_WRITEOP 0x01
+#define XL_MII_TURNAROUND 0x02
+
+
+
+/*
+ * The 3C905B adapters implement a few features that we want to
+ * take advantage of, namely the multicast hash filter. With older
+ * chips, you only have the option of turning on reception of all
+ * multicast frames, which is kind of lame.
+ *
+ * We also use this to decide on a transmit strategy. For the 3c90xB
+ * cards, we can use polled descriptor mode, which reduces CPU overhead.
+ */
+#define XL_TYPE_905B 1
+#define XL_TYPE_90X 2
+
+#define XL_FLAG_FUNCREG 0x0001
+#define XL_FLAG_PHYOK 0x0002
+#define XL_FLAG_EEPROM_OFFSET_30 0x0004
+#define XL_FLAG_WEIRDRESET 0x0008
+#define XL_FLAG_8BITROM 0x0010
+#define XL_FLAG_INVERT_LED_PWR 0x0020
+#define XL_FLAG_INVERT_MII_PWR 0x0040
+#define XL_FLAG_NO_XCVR_PWR 0x0080
+#define XL_FLAG_USE_MMIO 0x0100
+
+#define XL_NO_XCVR_PWR_MAGICBITS 0x0900
+
+
+#define XL_MIN_FRAMELEN 60
+
+#define XL_LAST_FRAG 0x80000000
+
+
+
+
+
+
+
+struct xl_stats
+{
+ /* accumulated stats */
+ u_int16_t xl_carrier_lost;
+ u_int16_t xl_sqe_errs;
+ u_int16_t xl_tx_multi_collision;
+ u_int16_t xl_tx_single_collision;
+ u_int16_t xl_tx_late_collision;
+ u_int16_t xl_rx_overrun;
+ u_int16_t xl_tx_deferred;
+
+ u_int32_t xl_rx_bytes_ok;
+ u_int32_t xl_tx_bytes_ok;
+
+ u_int32_t xl_tx_frames_ok;
+ u_int32_t xl_rx_frames_ok;
+
+ u_int16_t xl_badssd;
+
+ /* non-accumulated stats */
+ u_int16_t intstatus;
+ u_int16_t rxstatus;
+ u_int8_t txstatus;
+ u_int16_t mediastatus;
+
+ u_int32_t txcomplete_ints;
+
+ u_int16_t miianr, miipar, miistatus, miicmd;
+
+ u_int32_t device_interrupts;
+ u_int32_t internalconfig;
+ u_int16_t mac_control;
+
+ u_int16_t smbstatus;
+ u_int32_t dmactl;
+ u_int16_t txfree;
+};
+
+
+
+struct xl_type
+{
+ u_int16_t xl_vid;
+ u_int16_t xl_did;
+ char *xl_name;
+};
+
+
+
+/*
+ * Various supported device vendors/types and their names.
+ */
+static struct xl_type xl_devs[] = {
+ { TC_VENDORID, TC_DEVICEID_BOOMERANG_10BT,
+ "3Com 3c900-TPO Etherlink XL" },
+ { TC_VENDORID, TC_DEVICEID_BOOMERANG_10BT_COMBO,
+ "3Com 3c900-COMBO Etherlink XL" },
+ { TC_VENDORID, TC_DEVICEID_BOOMERANG_10_100BT,
+ "3Com 3c905-TX Fast Etherlink XL" },
+ { TC_VENDORID, TC_DEVICEID_BOOMERANG_100BT4,
+ "3Com 3c905-T4 Fast Etherlink XL" },
+ { TC_VENDORID, TC_DEVICEID_KRAKATOA_10BT,
+ "3Com 3c900B-TPO Etherlink XL" },
+ { TC_VENDORID, TC_DEVICEID_KRAKATOA_10BT_COMBO,
+ "3Com 3c900B-COMBO Etherlink XL" },
+ { TC_VENDORID, TC_DEVICEID_KRAKATOA_10BT_TPC,
+ "3Com 3c900B-TPC Etherlink XL" },
+ { TC_VENDORID, TC_DEVICEID_CYCLONE_10FL,
+ "3Com 3c900B-FL Etherlink XL" },
+ { TC_VENDORID, TC_DEVICEID_HURRICANE_10_100BT,
+ "3Com 3c905B-TX Fast Etherlink XL" },
+ { TC_VENDORID, TC_DEVICEID_CYCLONE_10_100BT4,
+ "3Com 3c905B-T4 Fast Etherlink XL" },
+ { TC_VENDORID, TC_DEVICEID_CYCLONE_10_100FX,
+ "3Com 3c905B-FX/SC Fast Etherlink XL" },
+ { TC_VENDORID, TC_DEVICEID_CYCLONE_10_100_COMBO,
+ "3Com 3c905B-COMBO Fast Etherlink XL" },
+ { TC_VENDORID, TC_DEVICEID_TORNADO_10_100BT,
+ "3Com 3c905C-TX Fast Etherlink XL" },
+ { TC_VENDORID, TC_DEVICEID_TORNADO_10_100BT_920B,
+ "3Com 3c920B-EMB Integrated Fast Etherlink XL" },
+ { TC_VENDORID, TC_DEVICEID_HURRICANE_10_100BT_SERV,
+ "3Com 3c980 Fast Etherlink XL" },
+ { TC_VENDORID, TC_DEVICEID_TORNADO_10_100BT_SERV,
+ "3Com 3c980C Fast Etherlink XL" },
+ { TC_VENDORID, TC_DEVICEID_HURRICANE_SOHO100TX,
+ "3Com 3cSOHO100-TX OfficeConnect" },
+ { TC_VENDORID, TC_DEVICEID_TORNADO_HOMECONNECT,
+ "3Com 3c450-TX HomeConnect" },
+ { TC_VENDORID, TC_DEVICEID_HURRICANE_555,
+ "3Com 3c555 Fast Etherlink XL" },
+ { TC_VENDORID, TC_DEVICEID_HURRICANE_556,
+ "3Com 3c556 Fast Etherlink XL" },
+ { TC_VENDORID, TC_DEVICEID_HURRICANE_556B,
+ "3Com 3c556B Fast Etherlink XL" },
+ { TC_VENDORID, TC_DEVICEID_HURRICANE_575A,
+ "3Com 3c575TX Fast Etherlink XL" },
+ { TC_VENDORID, TC_DEVICEID_HURRICANE_575B,
+ "3Com 3c575B Fast Etherlink XL" },
+ { TC_VENDORID, TC_DEVICEID_HURRICANE_575C,
+ "3Com 3c575C Fast Etherlink XL" },
+ { TC_VENDORID, TC_DEVICEID_HURRICANE_656,
+ "3Com 3c656 Fast Etherlink XL" },
+ { TC_VENDORID, TC_DEVICEID_HURRICANE_656B,
+ "3Com 3c656B Fast Etherlink XL" },
+ { TC_VENDORID, TC_DEVICEID_TORNADO_656C,
+ "3Com 3c656C Fast Etherlink XL" },
+ { 0, 0, NULL }
+};
+
+
+#define XL_TIMEOUT 1000
+
+
+
+
+
+
+/* rx message descriptor entry, ensure the struct is aligned to 8 bytes */
+struct RXMD
+{
+ /* used by hardware */
+ volatile uint32_t next;
+ volatile uint32_t status;
+ volatile uint32_t addr;
+ volatile uint32_t length;
+ /* used by software */
+ struct mbuf *mbuf; /* scratch variable used in the tx ring */
+ struct RXMD *next_md;
+} __attribute__ ((aligned (8), packed));
+
+
+
+
+
+#define NUM_FRAGS 6
+
+/*
+ * tx message descriptor entry, ensure the struct is aligned to 8 bytes
+ */
+
+struct tfrag
+{
+ volatile uint32_t addr;
+ volatile uint32_t length;
+} __attribute__ ((packed));
+
+struct TXMD
+{
+ /* used by hardware */
+ volatile uint32_t next;
+ volatile uint32_t status;
+ struct tfrag txfrags[NUM_FRAGS];
+ /* used by software */
+ struct mbuf *mbuf; /* scratch variable used in the tx ring */
+ struct TXMD *next_md, *chainptr;
+} __attribute__ ((aligned (8), packed));
+
+
+
+
+
+#define NUM_CHAIN_LENGTHS 50
+
+
+
+/*
+ * Per-device data
+ */
+struct elnk_softc
+{
+ struct arpcom arpcom;
+
+ rtems_irq_connect_data irqInfo;
+ rtems_event_set ioevent;
+ unsigned int ioaddr;
+
+ unsigned char *bufferBase, *ringBase;
+
+ struct RXMD *rx_ring, *curr_rx_md;
+ struct TXMD *tx_ring, *last_tx_md, *last_txchain_head;
+
+ rtems_id stat_timer_id;
+ uint32_t stats_update_ticks;
+
+ struct xl_stats xl_stats;
+
+ u_int8_t xl_unit; /* interface number */
+ u_int8_t xl_type;
+ int xl_flags;
+ u_int16_t xl_media;
+ u_int16_t xl_caps;
+ u_int32_t xl_xcvr;
+ u_int8_t xl_stats_no_timeout;
+ u_int16_t xl_tx_thresh;
+
+ int tx_idle;
+
+ short chain_lengths[NUM_CHAIN_LENGTHS];
+ int chlenIndex;
+
+ unsigned short vendorID, deviceID;
+ int acceptBroadcast;
+ int numTxbuffers, numRxbuffers;
+};
+
+static struct elnk_softc elnk_softc[NUM_UNITS];
+static rtems_id rxDaemonTid;
+static rtems_id txDaemonTid;
+static rtems_id chainRecoveryQueue;
+
+
+
+
+
+
+
+#if defined(__i386__)
+
+#define CSR_WRITE_4(sc, reg, val) i386_outport_long( sc->ioaddr + reg, val )
+#define CSR_WRITE_2(sc, reg, val) i386_outport_word( sc->ioaddr + reg, val )
+#define CSR_WRITE_1(sc, reg, val) i386_outport_byte( sc->ioaddr + reg, val )
+
+
+inline unsigned int CSR_READ_4( struct elnk_softc *sc, int reg)
+{
+ unsigned int myval;
+ i386_inport_long( sc->ioaddr + reg, myval );
+ return myval;
+}
+
+inline unsigned short CSR_READ_2( struct elnk_softc *sc, int reg)
+{
+ unsigned short myval;
+ i386_inport_word( sc->ioaddr + reg, myval );
+ return myval;
+}
+
+inline unsigned char CSR_READ_1( struct elnk_softc *sc, int reg)
+{
+ unsigned char myval;
+ i386_inport_byte( sc->ioaddr + reg, myval );
+ return myval;
+}
+
+#endif
+
+#if defined(__PPC__)
+
+#define CSR_WRITE_4(sc, reg, val) outl( val, sc->ioaddr + reg)
+#define CSR_WRITE_2(sc, reg, val) outw( val, sc->ioaddr + reg)
+#define CSR_WRITE_1(sc, reg, val) outb( val, sc->ioaddr + reg)
+
+#define CSR_READ_4(sc, reg) inl(sc->ioaddr + reg)
+#define CSR_READ_2(sc, reg) inw(sc->ioaddr + reg)
+#define CSR_READ_1(sc, reg) inb(sc->ioaddr + reg)
+
+#endif
+
+
+#define XL_SEL_WIN(x) CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_WINSEL | x)
+
+
+
+
+
+
+
+
+
+
+
+/*
+ * Murphy's law says that it's possible the chip can wedge and
+ * the 'command in progress' bit may never clear. Hence, we wait
+ * only a finite amount of time to avoid getting caught in an
+ * infinite loop. Normally this delay routine would be a macro,
+ * but it isn't called during normal operation so we can afford
+ * to make it a function.
+ */
+static void
+xl_wait(struct elnk_softc *sc)
+{
+ register int i;
+
+ for(i = 0; i < XL_TIMEOUT; i++)
+ {
+ if (!(CSR_READ_2(sc, XL_STATUS) & XL_STAT_CMDBUSY))
+ break;
+ }
+
+ if (i == XL_TIMEOUT)
+ printk("etherlink : unit elnk%d command never completed\n", sc->xl_unit );
+ return;
+}
+
+
+
+
+
+
+/*
+ * MII access routines are provided for adapters with external
+ * PHYs (3c905-TX, 3c905-T4, 3c905B-T4) and those with built-in
+ * autoneg logic that's faked up to look like a PHY (3c905B-TX).
+ * Note: if you don't perform the MDIO operations just right,
+ * it's possible to end up with code that works correctly with
+ * some chips/CPUs/processor speeds/bus speeds/etc but not
+ * with others.
+ */
+#define MII_SET(x) \
+ CSR_WRITE_2(sc, XL_W4_PHY_MGMT, \
+ CSR_READ_2(sc, XL_W4_PHY_MGMT) | (x))
+
+#define MII_CLR(x) \
+ CSR_WRITE_2(sc, XL_W4_PHY_MGMT, \
+ CSR_READ_2(sc, XL_W4_PHY_MGMT) & ~(x))
+
+/*
+ * Sync the PHYs by setting data bit and strobing the clock 32 times.
+ */
+static void
+xl_mii_sync(
+ struct elnk_softc *sc)
+{
+ register int i;
+
+ XL_SEL_WIN(4);
+ MII_SET(XL_MII_DIR|XL_MII_DATA);
+
+ for (i = 0; i < 32; i++) {
+ MII_SET(XL_MII_CLK);
+ MII_SET(XL_MII_DATA);
+ MII_CLR(XL_MII_CLK);
+ MII_SET(XL_MII_DATA);
+ }
+
+ return;
+}
+
+/*
+ * Clock a series of bits through the MII.
+ */
+static void
+xl_mii_send(
+ struct elnk_softc *sc,
+ u_int32_t bits,
+ int cnt )
+{
+ int i;
+
+ XL_SEL_WIN(4);
+ MII_CLR(XL_MII_CLK);
+
+ for (i = (0x1 << (cnt - 1)); i; i >>= 1) {
+ if (bits & i) {
+ MII_SET(XL_MII_DATA);
+ } else {
+ MII_CLR(XL_MII_DATA);
+ }
+ MII_CLR(XL_MII_CLK);
+ MII_SET(XL_MII_CLK);
+ }
+}
+
+/*
+ * Read an PHY register through the MII.
+ */
+static int
+xl_mii_readreg(
+ struct elnk_softc *sc,
+ struct xl_mii_frame *frame )
+{
+ int i, ack;
+
+ /*
+ * Set up frame for RX.
+ */
+ frame->mii_stdelim = XL_MII_STARTDELIM;
+ frame->mii_opcode = XL_MII_READOP;
+ frame->mii_turnaround = 0;
+ frame->mii_data = 0;
+
+ /*
+ * Select register window 4.
+ */
+
+ XL_SEL_WIN(4);
+
+ CSR_WRITE_2(sc, XL_W4_PHY_MGMT, 0);
+ /*
+ * Turn on data xmit.
+ */
+ MII_SET(XL_MII_DIR);
+
+ xl_mii_sync(sc);
+
+ /*
+ * Send command/address info.
+ */
+ xl_mii_send(sc, frame->mii_stdelim, 2);
+ xl_mii_send(sc, frame->mii_opcode, 2);
+ xl_mii_send(sc, frame->mii_phyaddr, 5);
+ xl_mii_send(sc, frame->mii_regaddr, 5);
+
+ /* Idle bit */
+ MII_CLR((XL_MII_CLK|XL_MII_DATA));
+ MII_SET(XL_MII_CLK);
+
+ /* Turn off xmit. */
+ MII_CLR(XL_MII_DIR);
+
+ /* Check for ack */
+ MII_CLR(XL_MII_CLK);
+ ack = CSR_READ_2(sc, XL_W4_PHY_MGMT) & XL_MII_DATA;
+ MII_SET(XL_MII_CLK);
+
+ /*
+ * Now try reading data bits. If the ack failed, we still
+ * need to clock through 16 cycles to keep the PHY(s) in sync.
+ */
+ if (ack) {
+ for(i = 0; i < 16; i++) {
+ MII_CLR(XL_MII_CLK);
+ MII_SET(XL_MII_CLK);
+ }
+ goto fail;
+ }
+
+ for (i = 0x8000; i; i >>= 1) {
+ MII_CLR(XL_MII_CLK);
+ if (!ack) {
+ if (CSR_READ_2(sc, XL_W4_PHY_MGMT) & XL_MII_DATA)
+ frame->mii_data |= i;
+ }
+ MII_SET(XL_MII_CLK);
+ }
+
+ fail:
+
+ MII_CLR(XL_MII_CLK);
+ MII_SET(XL_MII_CLK);
+
+ if (ack)
+ return(1);
+ return(0);
+}
+
+/*
+ * Write to a PHY register through the MII.
+ */
+static int
+xl_mii_writereg(
+ struct elnk_softc *sc,
+ struct xl_mii_frame *frame )
+{
+ /*
+ * Set up frame for TX.
+ */
+
+ frame->mii_stdelim = XL_MII_STARTDELIM;
+ frame->mii_opcode = XL_MII_WRITEOP;
+ frame->mii_turnaround = XL_MII_TURNAROUND;
+
+ /*
+ * Select the window 4.
+ */
+ XL_SEL_WIN(4);
+
+ /*
+ * Turn on data output.
+ */
+ MII_SET(XL_MII_DIR);
+
+ xl_mii_sync(sc);
+
+ xl_mii_send(sc, frame->mii_stdelim, 2);
+ xl_mii_send(sc, frame->mii_opcode, 2);
+ xl_mii_send(sc, frame->mii_phyaddr, 5);
+ xl_mii_send(sc, frame->mii_regaddr, 5);
+ xl_mii_send(sc, frame->mii_turnaround, 2);
+ xl_mii_send(sc, frame->mii_data, 16);
+
+ /* Idle bit. */
+ MII_SET(XL_MII_CLK);
+ MII_CLR(XL_MII_CLK);
+
+ /*
+ * Turn off xmit.
+ */
+ MII_CLR(XL_MII_DIR);
+
+ return(0);
+}
+
+static int
+xl_miibus_readreg(
+ struct elnk_softc *sc,
+ int phy,
+ int reg )
+{
+ struct xl_mii_frame frame;
+
+ /*
+ * Pretend that PHYs are only available at MII address 24.
+ * This is to guard against problems with certain 3Com ASIC
+ * revisions that incorrectly map the internal transceiver
+ * control registers at all MII addresses. This can cause
+ * the miibus code to attach the same PHY several times over.
+ */
+ if ((!(sc->xl_flags & XL_FLAG_PHYOK)) && phy != 24)
+ {
+ printk("etherlink : unit elnk%d xl_miibus_readreg returned\n", sc->xl_unit);
+ return(0);
+ }
+
+ memset((char *)&frame, 0, sizeof(frame));
+
+ frame.mii_phyaddr = phy;
+ frame.mii_regaddr = reg;
+ xl_mii_readreg(sc, &frame);
+
+ return(frame.mii_data);
+}
+
+static int
+xl_miibus_writereg(
+ struct elnk_softc *sc,
+ int phy,
+ int reg,
+ int data )
+{
+ struct xl_mii_frame frame;
+
+ if ((!(sc->xl_flags & XL_FLAG_PHYOK)) && phy != 24)
+ {
+ printk("etherlink : unit elnk%d xl_miibus_writereg returned\n", sc->xl_unit);
+ return(0);
+ }
+
+ memset((char *)&frame, 0, sizeof(frame));
+
+ frame.mii_phyaddr = phy;
+ frame.mii_regaddr = reg;
+ frame.mii_data = data;
+
+ xl_mii_writereg(sc, &frame);
+
+ return(0);
+}
+
+
+
+
+
+
+
+
+
+/*
+ * The EEPROM is slow: give it time to come ready after issuing
+ * it a command.
+ */
+static int
+xl_eeprom_wait(struct elnk_softc *sc)
+{
+ int i;
+
+ for (i = 0; i < 100; i++) {
+ if (CSR_READ_2(sc, XL_W0_EE_CMD) & XL_EE_BUSY)
+ DELAY(162);
+ else
+ break;
+ }
+
+ if (i == 100) {
+ printk("etherlink : unit elnk%d eeprom failed to come ready\n", sc->xl_unit);
+ return(1);
+ }
+
+ return(0);
+}
+
+/*
+ * Read a sequence of words from the EEPROM. Note that ethernet address
+ * data is stored in the EEPROM in network byte order.
+ */
+static int
+xl_read_eeprom(
+ struct elnk_softc *sc,
+ caddr_t dest,
+ int off,
+ int cnt,
+ int swap)
+{
+ int err = 0, i;
+ u_int16_t word = 0, *ptr;
+#define EEPROM_5BIT_OFFSET(A) ((((A) << 2) & 0x7F00) | ((A) & 0x003F))
+#define EEPROM_8BIT_OFFSET(A) ((A) & 0x003F)
+ /* WARNING! DANGER!
+ * It's easy to accidentally overwrite the rom content!
+ * Note: the 3c575 uses 8bit EEPROM offsets.
+ */
+ XL_SEL_WIN(0);
+
+ if (xl_eeprom_wait(sc))
+ return(1);
+
+ if (sc->xl_flags & XL_FLAG_EEPROM_OFFSET_30)
+ off += 0x30;
+
+ for (i = 0; i < cnt; i++) {
+ if (sc->xl_flags & XL_FLAG_8BITROM)
+ CSR_WRITE_2(sc, XL_W0_EE_CMD,
+ XL_EE_8BIT_READ | EEPROM_8BIT_OFFSET(off + i));
+ else
+ CSR_WRITE_2(sc, XL_W0_EE_CMD,
+ XL_EE_READ | EEPROM_5BIT_OFFSET(off + i));
+ err = xl_eeprom_wait(sc);
+ if (err)
+ break;
+ word = CSR_READ_2(sc, XL_W0_EE_DATA);
+ ptr = (u_int16_t*)(dest + (i * 2));
+ if (swap)
+ *ptr = ntohs(word);
+ else
+ *ptr = word;
+ }
+
+ return(err ? 1 : 0);
+}
+
+
+
+
+static void
+xl_stats_update(
+ rtems_id timerid,
+ void *xsc)
+{
+ struct elnk_softc *sc = (struct elnk_softc *)xsc;
+ struct ifnet *ifp = &sc->arpcom.ac_if;
+ u_int32_t t1;
+
+ sc->xl_stats.intstatus = CSR_READ_2(sc, XL_STATUS);
+
+ sc->xl_stats.miianr = xl_miibus_readreg(sc, 0x18, MII_ANAR );
+ sc->xl_stats.miipar = xl_miibus_readreg(sc, 0x18, MII_ANLPAR );
+ sc->xl_stats.miistatus = xl_miibus_readreg(sc, 0x18, MII_BMSR );
+ sc->xl_stats.miicmd = xl_miibus_readreg(sc, 0x18, MII_BMCR );
+
+ XL_SEL_WIN(1);
+ sc->xl_stats.rxstatus = CSR_READ_2(sc, XL_W1_RX_STATUS );
+ sc->xl_stats.txstatus = CSR_READ_1(sc, XL_W1_TX_STATUS );
+ sc->xl_stats.smbstatus = CSR_READ_2(sc, 2 );
+
+ XL_SEL_WIN(3);
+ sc->xl_stats.internalconfig = CSR_READ_4(sc, XL_W3_INTERNAL_CFG);
+ sc->xl_stats.mac_control = CSR_READ_2(sc, XL_W3_MAC_CTRL);
+ sc->xl_stats.txfree = CSR_READ_2(sc, XL_W3_FREE_TX );
+
+
+ /* Read all the stats registers. */
+ XL_SEL_WIN(6);
+
+ sc->xl_stats.xl_carrier_lost += CSR_READ_1(sc, XL_W6_CARRIER_LOST);
+ sc->xl_stats.xl_sqe_errs += CSR_READ_1(sc, XL_W6_SQE_ERRORS);
+ sc->xl_stats.xl_tx_multi_collision += CSR_READ_1(sc, XL_W6_COL_MULTIPLE);
+ sc->xl_stats.xl_tx_single_collision += CSR_READ_1(sc, XL_W6_COL_SINGLE);
+ sc->xl_stats.xl_tx_late_collision += CSR_READ_1(sc, XL_W6_COL_LATE);
+ sc->xl_stats.xl_rx_overrun += CSR_READ_1(sc, XL_W6_RX_OVERRUN);
+ sc->xl_stats.xl_tx_deferred += CSR_READ_1(sc, XL_W6_DEFERRED);
+
+ sc->xl_stats.xl_tx_frames_ok += CSR_READ_1(sc, XL_W6_TX_OK);
+ sc->xl_stats.xl_rx_frames_ok += CSR_READ_1(sc, XL_W6_RX_OK);
+
+ sc->xl_stats.xl_rx_bytes_ok += CSR_READ_2(sc, XL_W6_TX_BYTES_OK );
+ sc->xl_stats.xl_tx_bytes_ok += CSR_READ_2(sc, XL_W6_RX_BYTES_OK );
+
+ t1 = CSR_READ_1(sc, XL_W6_UPPER_FRAMES_OK);
+ sc->xl_stats.xl_rx_frames_ok += ((t1 & 0x3) << 8);
+ sc->xl_stats.xl_tx_frames_ok += (((t1 >> 4) & 0x3) << 8);
+
+
+ ifp->if_ierrors += sc->xl_stats.xl_rx_overrun;
+
+ ifp->if_collisions += sc->xl_stats.xl_tx_multi_collision +
+ sc->xl_stats.xl_tx_single_collision +
+ sc->xl_stats.xl_tx_late_collision;
+
+ /*
+ * Boomerang and cyclone chips have an extra stats counter
+ * in window 4 (BadSSD). We have to read this too in order
+ * to clear out all the stats registers and avoid a statsoflow
+ * interrupt.
+ */
+ XL_SEL_WIN(4);
+
+ t1 = CSR_READ_1(sc, XL_W4_UPPERBYTESOK);
+ sc->xl_stats.xl_rx_bytes_ok += ((t1 & 0xf) << 16);
+ sc->xl_stats.xl_tx_bytes_ok += (((t1 >> 4) & 0xf) << 16);
+
+ sc->xl_stats.xl_badssd += CSR_READ_1(sc, XL_W4_BADSSD);
+
+ sc->xl_stats.mediastatus = CSR_READ_2(sc, XL_W4_MEDIA_STATUS );
+ sc->xl_stats.dmactl = CSR_READ_4(sc, XL_DMACTL );
+
+
+ XL_SEL_WIN(7);
+
+ if (!sc->xl_stats_no_timeout)
+ rtems_timer_fire_after( sc->stat_timer_id, sc->stats_update_ticks, xl_stats_update, (void *)sc );
+ return;
+}
+
+
+
+
+
+
+
+static void
+xl_reset(struct elnk_softc *sc)
+{
+ register int i;
+
+ XL_SEL_WIN(0);
+ CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_RESET |
+ ((sc->xl_flags & XL_FLAG_WEIRDRESET) ?
+ XL_RESETOPT_DISADVFD:0));
+
+ for (i = 0; i < XL_TIMEOUT; i++) {
+ DELAY(10);
+ if (!(CSR_READ_2(sc, XL_STATUS) & XL_STAT_CMDBUSY))
+ break;
+ }
+
+ if (i == XL_TIMEOUT)
+ printk("etherlink : unit elnk%d reset didn't complete\n", sc->xl_unit);
+
+ /* Reset TX and RX. */
+ /* Note: the RX reset takes an absurd amount of time
+ * on newer versions of the Tornado chips such as those
+ * on the 3c905CX and newer 3c908C cards. We wait an
+ * extra amount of time so that xl_wait() doesn't complain
+ * and annoy the users.
+ */
+ CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_RX_RESET);
+ DELAY(100000);
+ xl_wait(sc);
+ CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_TX_RESET);
+ xl_wait(sc);
+
+ if (sc->xl_flags & XL_FLAG_INVERT_LED_PWR ||
+ sc->xl_flags & XL_FLAG_INVERT_MII_PWR)
+ {
+ XL_SEL_WIN(2);
+ CSR_WRITE_2(sc, XL_W2_RESET_OPTIONS, CSR_READ_2(sc,
+ XL_W2_RESET_OPTIONS)
+ | ((sc->xl_flags & XL_FLAG_INVERT_LED_PWR)?XL_RESETOPT_INVERT_LED:0)
+ | ((sc->xl_flags & XL_FLAG_INVERT_MII_PWR)?XL_RESETOPT_INVERT_MII:0)
+ );
+ }
+
+ /* Wait a little while for the chip to get its brains in order. */
+ DELAY(100000);
+ return;
+}
+
+
+
+
+static void
+xl_stop(struct elnk_softc *sc)
+{
+ struct ifnet *ifp;
+
+ ifp = &sc->arpcom.ac_if;
+ ifp->if_timer = 0;
+
+ rtems_timer_cancel( sc->stat_timer_id );
+
+ CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_RX_DISABLE);
+ CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_STATS_DISABLE);
+ CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_INTR_ENB);
+ CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_RX_DISCARD);
+ xl_wait(sc);
+ CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_TX_DISABLE);
+ CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_COAX_STOP);
+ DELAY(800);
+
+ CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_INTR_ACK|XL_STAT_INTLATCH);
+ CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_STAT_ENB|0);
+ CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_INTR_ENB|0);
+
+ return;
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+static void
+xl_setcfg(struct elnk_softc *sc)
+{
+ u_int32_t icfg;
+
+ XL_SEL_WIN(3);
+ icfg = CSR_READ_4(sc, XL_W3_INTERNAL_CFG);
+
+ icfg &= ~XL_ICFG_CONNECTOR_MASK;
+
+ if (sc->xl_media & XL_MEDIAOPT_MII || sc->xl_media & XL_MEDIAOPT_BT4)
+ icfg |= (XL_XCVR_MII << XL_ICFG_CONNECTOR_BITS);
+
+ if (sc->xl_media & XL_MEDIAOPT_BTX)
+ icfg |= (XL_XCVR_AUTO << XL_ICFG_CONNECTOR_BITS);
+
+ CSR_WRITE_4(sc, XL_W3_INTERNAL_CFG, icfg);
+ CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_COAX_STOP);
+
+ XL_SEL_WIN(7);
+ return;
+}
+
+
+
+static void
+xl_setmode(
+ struct elnk_softc *sc,
+ int media)
+{
+ u_int32_t icfg;
+ u_int16_t mediastat;
+
+ printk("etherlink : unit elnk%d selecting ", sc->xl_unit);
+
+ XL_SEL_WIN(4);
+ mediastat = CSR_READ_2(sc, XL_W4_MEDIA_STATUS);
+ XL_SEL_WIN(3);
+ icfg = CSR_READ_4(sc, XL_W3_INTERNAL_CFG);
+
+ if (sc->xl_media & XL_MEDIAOPT_BT) {
+ if (IFM_SUBTYPE(media) == IFM_10_T) {
+ printk("10baseT transceiver, ");
+ sc->xl_xcvr = XL_XCVR_10BT;
+ icfg &= ~XL_ICFG_CONNECTOR_MASK;
+ icfg |= (XL_XCVR_10BT << XL_ICFG_CONNECTOR_BITS);
+ mediastat |= XL_MEDIASTAT_LINKBEAT|
+ XL_MEDIASTAT_JABGUARD;
+ mediastat &= ~XL_MEDIASTAT_SQEENB;
+ }
+ }
+
+ if (sc->xl_media & XL_MEDIAOPT_BFX) {
+ if (IFM_SUBTYPE(media) == IFM_100_FX) {
+ printk("100baseFX port, ");
+ sc->xl_xcvr = XL_XCVR_100BFX;
+ icfg &= ~XL_ICFG_CONNECTOR_MASK;
+ icfg |= (XL_XCVR_100BFX << XL_ICFG_CONNECTOR_BITS);
+ mediastat |= XL_MEDIASTAT_LINKBEAT;
+ mediastat &= ~XL_MEDIASTAT_SQEENB;
+ }
+ }
+
+ if (sc->xl_media & (XL_MEDIAOPT_AUI|XL_MEDIAOPT_10FL)) {
+ if (IFM_SUBTYPE(media) == IFM_10_5) {
+ printk("AUI port, ");
+ sc->xl_xcvr = XL_XCVR_AUI;
+ icfg &= ~XL_ICFG_CONNECTOR_MASK;
+ icfg |= (XL_XCVR_AUI << XL_ICFG_CONNECTOR_BITS);
+ mediastat &= ~(XL_MEDIASTAT_LINKBEAT|
+ XL_MEDIASTAT_JABGUARD);
+ mediastat |= ~XL_MEDIASTAT_SQEENB;
+ }
+ if (IFM_SUBTYPE(media) == IFM_10_FL) {
+ printk("10baseFL transceiver, ");
+ sc->xl_xcvr = XL_XCVR_AUI;
+ icfg &= ~XL_ICFG_CONNECTOR_MASK;
+ icfg |= (XL_XCVR_AUI << XL_ICFG_CONNECTOR_BITS);
+ mediastat &= ~(XL_MEDIASTAT_LINKBEAT|
+ XL_MEDIASTAT_JABGUARD);
+ mediastat |= ~XL_MEDIASTAT_SQEENB;
+ }
+ }
+
+ if (sc->xl_media & XL_MEDIAOPT_BNC) {
+ if (IFM_SUBTYPE(media) == IFM_10_2) {
+ printk("BNC port, ");
+ sc->xl_xcvr = XL_XCVR_COAX;
+ icfg &= ~XL_ICFG_CONNECTOR_MASK;
+ icfg |= (XL_XCVR_COAX << XL_ICFG_CONNECTOR_BITS);
+ mediastat &= ~(XL_MEDIASTAT_LINKBEAT|
+ XL_MEDIASTAT_JABGUARD|
+ XL_MEDIASTAT_SQEENB);
+ }
+ }
+
+ if ((media & IFM_GMASK) == IFM_FDX ||
+ IFM_SUBTYPE(media) == IFM_100_FX) {
+ printk("full duplex\n");
+ XL_SEL_WIN(3);
+ CSR_WRITE_1(sc, XL_W3_MAC_CTRL, XL_MACCTRL_DUPLEX);
+ } else {
+ printk("half duplex\n");
+ XL_SEL_WIN(3);
+ CSR_WRITE_1(sc, XL_W3_MAC_CTRL,
+ (CSR_READ_1(sc, XL_W3_MAC_CTRL) & ~XL_MACCTRL_DUPLEX));
+ }
+
+ if (IFM_SUBTYPE(media) == IFM_10_2)
+ CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_COAX_START);
+ else
+ CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_COAX_STOP);
+
+ CSR_WRITE_4(sc, XL_W3_INTERNAL_CFG, icfg);
+ XL_SEL_WIN(4);
+ CSR_WRITE_2(sc, XL_W4_MEDIA_STATUS, mediastat);
+ DELAY(800);
+ XL_SEL_WIN(7);
+
+ return;
+}
+
+
+
+
+
+
+
+static void
+xl_choose_xcvr(
+ struct elnk_softc *sc,
+ int verbose)
+{
+ u_int16_t devid;
+
+ /*
+ * Read the device ID from the EEPROM.
+ * This is what's loaded into the PCI device ID register, so it has
+ * to be correct otherwise we wouldn't have gotten this far.
+ */
+ xl_read_eeprom(sc, (caddr_t)&devid, XL_EE_PRODID, 1, 0);
+
+ switch(devid) {
+ case TC_DEVICEID_BOOMERANG_10BT: /* 3c900-TPO */
+ case TC_DEVICEID_KRAKATOA_10BT: /* 3c900B-TPO */
+ sc->xl_media = XL_MEDIAOPT_BT;
+ sc->xl_xcvr = XL_XCVR_10BT;
+ if (verbose)
+ printk("etherlink : unit elnk%d guessing 10BaseT "
+ "transceiver\n", sc->xl_unit);
+ break;
+ case TC_DEVICEID_BOOMERANG_10BT_COMBO: /* 3c900-COMBO */
+ case TC_DEVICEID_KRAKATOA_10BT_COMBO: /* 3c900B-COMBO */
+ sc->xl_media = XL_MEDIAOPT_BT|XL_MEDIAOPT_BNC|XL_MEDIAOPT_AUI;
+ sc->xl_xcvr = XL_XCVR_10BT;
+ if (verbose)
+ printk("etherlink : unit elnk%d guessing COMBO "
+ "(AUI/BNC/TP)\n", sc->xl_unit);
+ break;
+ case TC_DEVICEID_KRAKATOA_10BT_TPC: /* 3c900B-TPC */
+ sc->xl_media = XL_MEDIAOPT_BT|XL_MEDIAOPT_BNC;
+ sc->xl_xcvr = XL_XCVR_10BT;
+ if (verbose)
+ printk("etherlink : unit elnk%d guessing TPC (BNC/TP)\n", sc->xl_unit);
+ break;
+ case TC_DEVICEID_CYCLONE_10FL: /* 3c900B-FL */
+ sc->xl_media = XL_MEDIAOPT_10FL;
+ sc->xl_xcvr = XL_XCVR_AUI;
+ if (verbose)
+ printk("etherlink : unit elnk%d guessing 10baseFL\n", sc->xl_unit);
+ break;
+ case TC_DEVICEID_BOOMERANG_10_100BT: /* 3c905-TX */
+ case TC_DEVICEID_HURRICANE_555: /* 3c555 */
+ case TC_DEVICEID_HURRICANE_556: /* 3c556 */
+ case TC_DEVICEID_HURRICANE_556B: /* 3c556B */
+ case TC_DEVICEID_HURRICANE_575A: /* 3c575TX */
+ case TC_DEVICEID_HURRICANE_575B: /* 3c575B */
+ case TC_DEVICEID_HURRICANE_575C: /* 3c575C */
+ case TC_DEVICEID_HURRICANE_656: /* 3c656 */
+ case TC_DEVICEID_HURRICANE_656B: /* 3c656B */
+ case TC_DEVICEID_TORNADO_656C: /* 3c656C */
+ case TC_DEVICEID_TORNADO_10_100BT_920B: /* 3c920B-EMB */
+ sc->xl_media = XL_MEDIAOPT_MII;
+ sc->xl_xcvr = XL_XCVR_MII;
+ if (verbose)
+ printk("etherlink : unit elnk%d guessing MII\n", sc->xl_unit);
+ break;
+ case TC_DEVICEID_BOOMERANG_100BT4: /* 3c905-T4 */
+ case TC_DEVICEID_CYCLONE_10_100BT4: /* 3c905B-T4 */
+ sc->xl_media = XL_MEDIAOPT_BT4;
+ sc->xl_xcvr = XL_XCVR_MII;
+ if (verbose)
+ printk("etherlink : unit elnk%d guessing 100BaseT4/MII\n", sc->xl_unit);
+ break;
+ case TC_DEVICEID_HURRICANE_10_100BT: /* 3c905B-TX */
+ case TC_DEVICEID_HURRICANE_10_100BT_SERV:/*3c980-TX */
+ case TC_DEVICEID_TORNADO_10_100BT_SERV: /* 3c980C-TX */
+ case TC_DEVICEID_HURRICANE_SOHO100TX: /* 3cSOHO100-TX */
+ case TC_DEVICEID_TORNADO_10_100BT: /* 3c905C-TX */
+ case TC_DEVICEID_TORNADO_HOMECONNECT: /* 3c450-TX */
+ sc->xl_media = XL_MEDIAOPT_BTX;
+ sc->xl_xcvr = XL_XCVR_AUTO;
+ if (verbose)
+ printk("etherlink : unit elnk%d guessing 10/100 internal\n", sc->xl_unit);
+ break;
+ case TC_DEVICEID_CYCLONE_10_100_COMBO: /* 3c905B-COMBO */
+ sc->xl_media = XL_MEDIAOPT_BTX|XL_MEDIAOPT_BNC|XL_MEDIAOPT_AUI;
+ sc->xl_xcvr = XL_XCVR_AUTO;
+ if (verbose)
+ printk("etherlink : unit elnk%d guessing 10/100 "
+ "plus BNC/AUI\n", sc->xl_unit);
+ break;
+ default:
+ printk("etherlink : unit elnk%d unknown device ID: %x -- "
+ "defaulting to 10baseT\n", sc->xl_unit, devid);
+ sc->xl_media = XL_MEDIAOPT_BT;
+ break;
+ }
+
+ return;
+}
+
+
+
+
+
+
+
+/*
+ * This routine is a kludge to work around possible hardware faults
+ * or manufacturing defects that can cause the media options register
+ * (or reset options register, as it's called for the first generation
+ * 3c90x adapters) to return an incorrect result. I have encountered
+ * one Dell Latitude laptop docking station with an integrated 3c905-TX
+ * which doesn't have any of the 'mediaopt' bits set. This screws up
+ * the attach routine pretty badly because it doesn't know what media
+ * to look for. If we find ourselves in this predicament, this routine
+ * will try to guess the media options values and warn the user of a
+ * possible manufacturing defect with his adapter/system/whatever.
+ */
+static void
+xl_mediacheck(struct elnk_softc *sc)
+{
+
+ xl_choose_xcvr(sc, 1);
+
+ /*
+ * If some of the media options bits are set, assume they are
+ * correct. If not, try to figure it out down below.
+ * XXX I should check for 10baseFL, but I don't have an adapter
+ * to test with.
+ */
+ if (sc->xl_media & (XL_MEDIAOPT_MASK & ~XL_MEDIAOPT_VCO)) {
+ /*
+ * Check the XCVR value. If it's not in the normal range
+ * of values, we need to fake it up here.
+ */
+ if (sc->xl_xcvr <= XL_XCVR_AUTO)
+ return;
+ else {
+ printk("etherlink : unit elnk%d bogus xcvr value "
+ "in EEPROM (%" PRIx32 ")\n", sc->xl_unit, sc->xl_xcvr);
+ printk("etherlink : unit elnk%d choosing new default based "
+ "on card type\n", sc->xl_unit);
+ }
+ } else {
+ if (sc->xl_type == XL_TYPE_905B &&
+ sc->xl_media & XL_MEDIAOPT_10FL)
+ return;
+ printk("etherlink : unit elnk%d WARNING: no media options bits set in "
+ "the media options register!!\n", sc->xl_unit);
+ printk("etherlink : unit elnk%d this could be a manufacturing defect in "
+ "your adapter or system\n", sc->xl_unit);
+ printk("etherlink : unit elnk%d attempting to guess media type; you "
+ "should probably consult your vendor\n", sc->xl_unit);
+ }
+
+ return;
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+static void no_op(const rtems_irq_connect_data* irq)
+{
+ return;
+}
+
+
+
+
+static void
+elnk_start_txchain( struct elnk_softc *sc, struct TXMD *chainhead )
+{
+ xl_wait(sc);
+ CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_DOWN_STALL);
+
+ /* save the address of the TX list */
+ sc->last_txchain_head = chainhead;
+ sc->tx_idle = 0;
+
+ xl_wait(sc);
+
+ CSR_WRITE_4(sc, XL_DOWNLIST_PTR, phys_to_bus( sc->last_txchain_head ));
+ CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_DOWN_UNSTALL);
+}
+
+
+
+
+
+/*
+ * ELNK interrupt handler
+ */
+static rtems_isr
+elnk_interrupt_handler ( struct elnk_softc *sc )
+{
+ struct ifnet *ifp = &sc->arpcom.ac_if;
+ u_int16_t status;
+
+ while( ((status = CSR_READ_2(sc, XL_STATUS)) & XL_INTRS) && status != 0xFFFF)
+ {
+ sc->xl_stats.device_interrupts++;
+
+ CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_INTR_ACK | (status & XL_INTRS));
+
+#if 0
+ printk("etherlink : unit elnk%d intstatus %04x\n", sc->xl_unit, status );
+#endif
+
+ if (status & XL_STAT_UP_COMPLETE)
+ {
+#if 0
+ printk("etherlink : unit elnk%d rx\n", sc->xl_unit );
+#endif
+ /* received packets */
+ rtems_bsdnet_event_send(rxDaemonTid, sc->ioevent);
+ }
+
+ if( (status & XL_STAT_DOWN_COMPLETE) || (status & XL_STAT_TX_COMPLETE) )
+ {
+ /* all packets uploaded to the device */
+ struct TXMD *chaintailmd = NULL;
+
+
+ if( status & XL_STAT_TX_COMPLETE )
+ {
+ /* if we got a tx complete error, count it, then reset the
+ transmitter. Consider the entire chain lost.. */
+
+ ifp->if_oerrors++;
+ sc->xl_stats.txcomplete_ints++;
+
+ printk("etherlink : unit elnk%d transmit error\n", sc->xl_unit );
+
+ /* reset, re-enable fifo */
+
+ xl_wait(sc);
+ CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_TX_DISABLE);
+
+ xl_wait(sc);
+ CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_TX_RESET | 1 );
+
+ xl_wait(sc);
+ CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_TX_ENABLE);
+
+ xl_wait(sc);
+ }
+
+
+ /* send the chain head to the tx task which will recover the
+ whole chain */
+ rtems_message_queue_send( chainRecoveryQueue, &sc->last_txchain_head, sizeof(struct TXMD *));
+
+
+ /* set up the next chain */
+ if( sc->last_txchain_head->chainptr )
+ {
+ /* check the head of the chain of packets we just finished,
+ * if != 0, either this is a chain of 2 or more packets or
+ * its a single packet chain and another chain is ready to
+ * send.
+ */
+ if( (int)sc->last_txchain_head->chainptr == -1 )
+ {
+ /*
+ ** single packet was sent so no indirection to the last
+ ** entry in the chain. since chainptr is != 0, then
+ ** another chain is ready starting from the packet AFTER
+ ** the chain we just finished. - in this case the last
+ ** chain's head == its tail
+ */
+ chaintailmd = sc->last_txchain_head;
+ }
+ else
+ {
+ /*
+ ** otherwise, this is a pointer to the last packet in the
+ ** chain of 2 or more packets. If the chain's last
+ ** packet's chainptr is != 0, then another chain is ready
+ ** to send.
+ */
+ chaintailmd = sc->last_txchain_head->chainptr;
+ if( !chaintailmd->chainptr ) chaintailmd = NULL;
+ }
+ }
+
+ if( chaintailmd )
+ {
+ /* the next MD is the start of another chain */
+ elnk_start_txchain(sc, chaintailmd->next_md );
+ }
+ else
+ {
+ /* otherwise nothing to send, so go idle */
+ sc->tx_idle = -1;
+
+ /* wake up the tx daemon once so we're sure this last chain
+ will be freed */
+ rtems_bsdnet_event_send( txDaemonTid, sc->ioevent );
+#if 0
+ printk("unit elnk%d tx done\n", sc->xl_unit );
+#endif
+ }
+ }
+
+
+ if (status & XL_STAT_ADFAIL)
+ {
+ printk("etherlink : unit elnk%d Catastrophic bus failure\n", sc->xl_unit );
+ }
+ if (status & XL_STAT_STATSOFLOW)
+ {
+ sc->xl_stats_no_timeout = 1;
+ xl_stats_update(sc->stat_timer_id,sc);
+ sc->xl_stats_no_timeout = 0;
+ }
+ }
+
+
+#if 0
+ {
+ uint16_t intstatus, intenable, indenable;
+
+ intstatus = CSR_READ_2(sc, XL_STATUS );
+
+ XL_SEL_WIN(5);
+ intenable = CSR_READ_2(sc, XL_W5_INTR_ENB );
+ indenable = CSR_READ_2(sc, XL_W5_STAT_ENB );
+ XL_SEL_WIN(7);
+ printk("etherlink : unit elnk%d istat %04x, ien %04x, ind %04x\n", sc->xl_unit, intstatus, intenable, indenable );
+ }
+#endif
+}
+
+
+
+
+
+static rtems_isr
+elnk_interrupt_handler_entry(void)
+{
+ int i;
+
+ /*
+ ** Check all the initialized units for interrupt service
+ */
+
+ for(i=0; i< NUM_UNITS; i++ )
+ {
+ if( elnk_softc[i].ioaddr )
+ elnk_interrupt_handler( &elnk_softc[i] );
+ }
+}
+
+
+
+
+
+
+
+
+
+
+
+/*
+ * Initialize the ethernet hardware
+ */
+static void
+elnk_initialize_hardware (struct elnk_softc *sc)
+{
+ unsigned char *cp;
+ int i, j, rxsize, txsize, ringsize;
+
+ /*
+ * Init RX ring
+ */
+ cp = (unsigned char *)malloc( (ringsize = ((rxsize = (sc->numRxbuffers * sizeof(struct RXMD))) +
+ (txsize = (sc->numTxbuffers * sizeof(struct TXMD)))) ) +
+ + CPU_CACHE_ALIGNMENT_FOR_BUFFER);
+ sc->bufferBase = cp;
+ cp += (CPU_CACHE_ALIGNMENT_FOR_BUFFER - (int)cp) & (CPU_CACHE_ALIGNMENT_FOR_BUFFER - 1);
+#if defined(__i386__)
+#ifdef PCI_BRIDGE_DOES_NOT_ENSURE_CACHE_COHERENCY_FOR_DMA
+ if (_CPU_is_paging_enabled())
+ _CPU_change_memory_mapping_attribute
+ (NULL, cp, ringsize, PTE_CACHE_DISABLE | PTE_WRITABLE);
+#endif
+#endif
+ sc->ringBase = cp;
+
+ /* build tx and rx rings */
+
+ sc->rx_ring = (struct RXMD *)sc->ringBase;
+ sc->tx_ring = (struct TXMD *)&sc->ringBase[ rxsize ];
+
+ {
+ struct mbuf *m;
+ struct RXMD *nxtmd;
+ /*
+ * The rx ring is easy as its just an array of RXMD structs. New
+ * mbuf entries are allocated from the stack whenever the rx
+ * daemon forwards an incoming packet into it. Here, we
+ * pre-allocate the rx mbufs for the rx ring entries.
+ */
+ for(i=0 ; i<sc->numRxbuffers; i++)
+ {
+ if( ((uint32_t)&sc->rx_ring[i] & 0x7) )
+ {
+ rtems_panic ("etherlink : unit elnk%d rx ring entry %d not aligned to 8 bytes\n", sc->xl_unit, i );
+ }
+
+ /* allocate an mbuf for each receive descriptor */
+ MGETHDR (m, M_WAIT, MT_DATA);
+ MCLGET (m, M_WAIT);
+ m->m_pkthdr.rcvif = &sc->arpcom.ac_if;
+
+ if( i == sc->numRxbuffers-1 )
+ nxtmd = &sc->rx_ring[0];
+ else
+ nxtmd = &sc->rx_ring[i+1];
+
+ sc->rx_ring[i].next_md = nxtmd;
+ sc->rx_ring[i].mbuf = m;
+
+ st_le32( &sc->rx_ring[i].status, 0);
+ st_le32( &sc->rx_ring[i].next, (uint32_t)phys_to_bus( nxtmd ));
+ st_le32( &sc->rx_ring[i].addr, (uint32_t)phys_to_bus( mtod(m, void *) ));
+ st_le32( &sc->rx_ring[i].length, XL_LAST_FRAG | XL_PACKET_SIZE );
+ }
+ sc->curr_rx_md = &sc->rx_ring[0];
+ }
+
+
+ {
+ struct TXMD *thismd, *nxtmd;
+ /*
+ * The tx ring is more complex. Each MD has an array of fragment
+ * descriptors that are loaded from each packet as they arrive
+ * from the stack. Each packet gets one ring entry, this allows
+ * the lanboard to efficiently assemble the piecemeal packets into
+ * a contiguous unit at transmit time, rather than spending
+ * cputime concatenating them first. Although the next_md fields
+ * form a ring, the DPD next is filled only when packets are added
+ * to the tx chain, thus last entry of a series of packets has the
+ * requisite dpd->next value == 0 to terminate the dma. mbuf
+ * holds the packet info so it can be freed once the packet has
+ * been sent. chainptr is used to link the head & tail of a chain
+ * of 2 or more packets. A chain is formed when the tx daemon
+ * gets 2 or more packets from the stack's queue in a service
+ * period, so higher outgoing loads are handled as efficiently as
+ * possible.
+ */
+
+ for(i=0 ; i<sc->numTxbuffers; i++)
+ {
+ if( ((uint32_t)&sc->tx_ring[i] & 0x7) )
+ {
+ rtems_panic ("etherlink : unit elnk%d tx ring entry %d not aligned to 8 bytes\n", sc->xl_unit, i );
+ }
+
+ if( i == sc->numTxbuffers-1 )
+ nxtmd = &sc->tx_ring[0];
+ else
+ nxtmd = &sc->tx_ring[i+1];
+
+ thismd = &sc->tx_ring[i];
+
+ thismd->next_md = nxtmd;
+ thismd->chainptr = NULL;
+ thismd->mbuf = NULL;
+
+ st_le32( &thismd->status, XL_TXSTAT_DL_COMPLETE );
+ st_le32( &thismd->next, 0);
+
+ for(j=0; j< NUM_FRAGS; j++)
+ {
+ st_le32( &thismd->txfrags[j].addr, 0 );
+ st_le32( &thismd->txfrags[j].length, 0 );
+ }
+ }
+ sc->last_tx_md = &sc->tx_ring[0];
+ }
+
+
+
+
+#ifdef ELNK_DEBUG
+ printk("etherlink : %02x:%02x:%02x:%02x:%02x:%02x name 'elnk%d', io %x, int %d\n",
+ sc->arpcom.ac_enaddr[0], sc->arpcom.ac_enaddr[1],
+ sc->arpcom.ac_enaddr[2], sc->arpcom.ac_enaddr[3],
+ sc->arpcom.ac_enaddr[4], sc->arpcom.ac_enaddr[5],
+ sc->xl_unit,
+ (unsigned)sc->ioaddr, sc->irqInfo.name );
+#endif
+
+
+ sc->irqInfo.hdl = (rtems_irq_hdl)elnk_interrupt_handler_entry;
+ sc->irqInfo.on = no_op;
+ sc->irqInfo.off = no_op;
+ sc->irqInfo.isOn = NULL;
+
+ if( sc->irqInfo.name != 255 )
+ {
+ int st;
+
+#ifdef BSP_SHARED_HANDLER_SUPPORT
+ st = BSP_install_rtems_shared_irq_handler( &sc->irqInfo );
+#else
+ st = BSP_install_rtems_irq_handler( &sc->irqInfo );
+#endif
+ if (!st)
+ rtems_panic ("etherlink : unit elnk%d Interrupt name %d already in use\n", sc->xl_unit, sc->irqInfo.name );
+ }
+ else
+ {
+ printk("etherlink : unit elnk%d Interrupt not specified by device\n", sc->xl_unit );
+ }
+}
+
+
+
+
+
+
+
+
+
+
+
+static void
+elnk_rxDaemon (void *arg)
+{
+ struct elnk_softc *sc;
+ struct ether_header *eh;
+ struct mbuf *m;
+ struct RXMD *rmd;
+ unsigned int i,len, rxstat;
+ rtems_event_set events;
+
+ for (;;)
+ {
+
+ rtems_bsdnet_event_receive( RTEMS_ALL_EVENTS,
+ RTEMS_WAIT|RTEMS_EVENT_ANY,
+ RTEMS_NO_TIMEOUT,
+ &events);
+
+ for(;;)
+ {
+ for(i=0; i< NUM_UNITS; i++ )
+ {
+ sc = &elnk_softc[i];
+ if( sc->ioaddr )
+ {
+ if( events & sc->ioevent )
+ {
+ struct ifnet *ifp = &sc->arpcom.ac_if;
+
+ rmd = sc->curr_rx_md;
+
+ /*
+ ** Read off all the packets we've received on this unit
+ */
+ while( (rxstat = ld_le32(&rmd->status)) )
+ {
+ if (rxstat & XL_RXSTAT_UP_ERROR)
+ {
+ printk("unit %i up error\n", sc->xl_unit );
+ ifp->if_ierrors++;
+ }
+
+ if( (rxstat & XL_RXSTAT_UP_CMPLT) )
+ {
+
+#if 0
+ {
+ char *pkt, *delim;
+ int i;
+ pkt = mtod(rmd->mbuf, char *);
+ printk("unit %i rx pkt (%08x) ", sc->xl_unit, pkt );
+ for(delim="", i=0; i < sizeof(struct ether_header)+8; i++, delim=":")
+ printk("%s%02x", delim, (char) pkt[i] );
+ printk("\n");
+ }
+#endif
+
+ /* pass on the packet in the mbuf */
+ len = ( ld_le32(&rmd->status) & XL_RXSTAT_LENMASK);
+ m = rmd->mbuf;
+ m->m_len = m->m_pkthdr.len = len - sizeof(struct ether_header);
+ eh = mtod(m, struct ether_header *);
+ m->m_data += sizeof(struct ether_header);
+
+ ether_input(ifp, eh, m);
+
+ /* get a new mbuf */
+ MGETHDR (m, M_WAIT, MT_DATA);
+ MCLGET (m, M_WAIT);
+ m->m_pkthdr.rcvif = ifp;
+ rmd->mbuf = m;
+ st_le32( &rmd->status, 0 );
+ st_le32( &rmd->addr, (uint32_t)phys_to_bus(mtod(m, void *)) );
+ }
+ else
+ {
+ /* some kind of packet failure */
+ printk("etherlink : unit elnk%d bad receive status -- packet dropped\n", sc->xl_unit);
+ ifp->if_ierrors++;
+ }
+ /* clear descriptor status */
+ rmd->status = 0;
+
+ rmd = rmd->next_md;
+ }
+
+ sc->curr_rx_md = rmd;
+ }
+ }
+ }
+
+ /*
+ ** If more events are pending, service them before we go back to sleep
+ */
+ if( rtems_event_system_receive( RTEMS_ALL_EVENTS,
+ RTEMS_NO_WAIT | RTEMS_EVENT_ANY,
+ 0,
+ &events ) == RTEMS_UNSATISFIED ) break;
+ }
+ }
+}
+
+
+
+
+
+
+
+
+/*
+ * Driver transmit daemon
+ */
+static void
+elnk_txDaemon (void *arg)
+{
+ struct elnk_softc *sc;
+ struct ifnet *ifp;
+ struct mbuf *m;
+ struct TXMD *lastmd, *nextmd, *firstmd;
+ int chainCount,i;
+ rtems_event_set events;
+
+ for (;;)
+ {
+ /*
+ * Wait for any unit's signal to wake us up
+ */
+ rtems_bsdnet_event_receive( RTEMS_ALL_EVENTS,
+ RTEMS_EVENT_ANY | RTEMS_WAIT,
+ RTEMS_NO_TIMEOUT, &events);
+
+ for(i=0; i< NUM_UNITS; i++ )
+ {
+ sc = &elnk_softc[i];
+ if( sc->ioaddr )
+ {
+ if( events & sc->ioevent )
+ {
+ ifp = &sc->arpcom.ac_if;
+
+ /*
+ * Send packets till queue is empty or tx ring is full
+ */
+
+ chainCount = 0;
+ firstmd = NULL;
+
+ lastmd = sc->last_tx_md;
+
+ for(;;)
+ {
+ /*
+ ** Check the chain recovery queue whenever the tx
+ ** daemon services the stack. Note this routine does
+ ** not assume the context of one of the lanboard units
+ ** because used tx mbufs are no longer associated with
+ ** any unit.
+ */
+ {
+ struct TXMD *chainhead, *chaintail;
+ size_t esize;
+
+ if( rtems_message_queue_receive( chainRecoveryQueue, &chainhead, &esize,
+ RTEMS_NO_WAIT, 0) == RTEMS_SUCCESSFUL )
+ {
+ /* get a pointer to the tail */
+ chaintail = chainhead->chainptr;
+
+ /* if the tail points somewhere, free the entire
+ chain */
+ if( chaintail && (int)chaintail != -1 )
+ {
+ for(;;)
+ {
+ m_freem( chainhead->mbuf );
+ st_le32( &chainhead->status, XL_TXSTAT_DL_COMPLETE );
+ chainhead->mbuf = NULL;
+
+ if( chainhead == chaintail ) break;
+ chainhead = chainhead->next_md;
+ }
+ }
+ else
+ {
+ /* a single packet chain */
+ m_freem( chainhead->mbuf );
+ st_le32( &chainhead->status, XL_TXSTAT_DL_COMPLETE );
+ chainhead->mbuf = NULL;
+ }
+ }
+ }
+
+ nextmd = lastmd->next_md;
+
+ /* stop when ring is full */
+ if( ! (ld_le32(&nextmd->status) & XL_TXSTAT_DL_COMPLETE) )
+ {
+ printk("etherlink : unit elnk%d tx ring full!\n", sc->xl_unit);
+ break;
+ }
+ /* sanity check the next packet descriptor */
+ if( nextmd->mbuf )
+ {
+ printk("etherlink : unit elnk%d tx ring corrupt!\n", sc->xl_unit);
+ break;
+ }
+
+
+
+ IF_DEQUEUE(&ifp->if_snd, m);
+ if( !m ) break;
+
+ {
+ int i;
+
+ nextmd->mbuf = m;
+
+ for(i=0; i< NUM_FRAGS; i++)
+ {
+ st_le32( &nextmd->txfrags[i].length, ((m->m_next)?0:XL_LAST_FRAG) | ( m->m_len & XL_TXSTAT_LENMASK) );
+ st_le32( &nextmd->txfrags[i].addr, (uint32_t)phys_to_bus( m->m_data ) );
+ if ((m = m->m_next) == NULL)
+ break;
+ }
+ if( m )
+ {
+ printk("etherlink : unit elnk%d tx fragments exhausted, truncating packet!\n", sc->xl_unit);
+ st_le32( &nextmd->txfrags[NUM_FRAGS-1].length, XL_LAST_FRAG |
+ ld_le32( &nextmd->txfrags[NUM_FRAGS-1].length) );
+ }
+ }
+
+#if 0
+ {
+ char *pkt = bus_to_phys( ld_le32( &nextmd->txfrags[i].addr )), *delim;
+ int i;
+ printk("unit %d queued pkt (%08x) ", sc->xl_unit, (uint32_t)pkt );
+ for(delim="", i=0; i < sizeof(struct ether_header); i++, delim=":")
+ printk("%s%02x", delim, (char) pkt[i] );
+ printk("\n");
+ }
+#endif
+
+
+ /* this packet will be the new end of the list */
+ st_le32( &nextmd->next, 0);
+ st_le32( &nextmd->status, 0);
+
+ if( !firstmd )
+ {
+ /* keep track of the first packet we add to the chain */
+ firstmd = nextmd;
+
+ /*
+ ** use the chainbuf pointer of the last packet of
+ ** the previous chain as a flag so when a
+ ** dnComplete interrupt indicates the card is
+ ** finished downloading the chain, the isr can
+ ** immediately start the next which always begins
+ ** with the next packet in the ring. Note several
+ ** chains of packets may be assembled this way.
+ */
+ lastmd->chainptr = (struct TXMD *)-1;
+ }
+ else
+ {
+ /* hook this packet to the previous one */
+ st_le32( &lastmd->next, (uint32_t)phys_to_bus( nextmd ));
+ }
+
+ ++chainCount;
+ lastmd = nextmd;
+ }
+
+
+
+
+
+ if( firstmd )
+ {
+ /* only enter if we've queued one or more packets */
+
+ /* save the last descriptor we set up in the chain */
+ sc->last_tx_md = lastmd;
+
+ /*
+ * We've added one or more packets to a chain, flag
+ * the last packet so we get an dnComplete interrupt
+ * when the card finishes accepting the chain
+ */
+ st_le32( &lastmd->status, XL_TXSTAT_DL_INTR );
+
+ /*
+ * point the chain head's chainptr to the tail so we
+ * can jump to the next chain to send inside the isr.
+ * If we're only sending one packet, then don't bother
+ * with the link, as the chainptr value will either be
+ * 0 if theres no next chain or -1 if there is.
+ */
+ if( chainCount > 1 )
+ {
+ firstmd->chainptr = lastmd;
+
+ sc->chain_lengths[sc->chlenIndex]= (short)chainCount;
+ if( ++sc->chlenIndex == NUM_CHAIN_LENGTHS ) sc->chlenIndex = 0;
+ }
+
+ /*
+ ** clear the last packet's chainptr flag. If another
+ ** chain is added later but before this chain is
+ ** finished being sent, this flag on this packet will
+ ** be re-set to -1
+ */
+ lastmd->chainptr = NULL;
+
+#if 0
+ printk("unit %d queued %d pkts, lastpkt status %08X\n",
+ sc->xl_unit,
+ chainCount,
+ (uint32_t)ld_le32( &lastmd->status) );
+#endif
+
+ if( sc->tx_idle == 0 && CSR_READ_4(sc, XL_DOWNLIST_PTR) == 0 )
+ {
+ printk("etherlink : unit elnk%d tx forced!\n", sc->xl_unit);
+ sc->tx_idle = -1;
+ }
+
+ /*
+ ** start sending this chain of packets if tx isn't
+ ** busy, else the dnComplete interrupt will see there
+ ** is another chain waiting and begin it immediately.
+ */
+ if( sc->tx_idle )
+ {
+#if 0
+ printk("etherlink : unit elnk%d tx started %d packets\n", sc->xl_unit, chainCount );
+#endif
+ elnk_start_txchain(sc, firstmd);
+ }
+ }
+
+
+ ifp->if_flags &= ~IFF_OACTIVE;
+ }
+ }
+ }
+ }
+}
+
+
+
+
+
+
+
+
+
+
+
+static void
+elnk_start (struct ifnet *ifp)
+{
+ struct elnk_softc *sc = ifp->if_softc;
+#if 0
+ printk("unit %i tx signaled\n", sc->xl_unit );
+#endif
+ ifp->if_flags |= IFF_OACTIVE;
+ rtems_bsdnet_event_send( txDaemonTid, sc->ioevent );
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+/*
+ * Initialize and start the device
+ */
+static void
+elnk_init (void *arg)
+{
+ int i;
+ struct elnk_softc *sc = arg;
+ struct ifnet *ifp = &sc->arpcom.ac_if;
+
+ if( !(ifp->if_flags & IFF_RUNNING) )
+ {
+ xl_stop(sc);
+ xl_reset(sc);
+ sc->tx_idle = -1;
+
+ {
+ uint32_t cr,sr;
+
+ xl_miibus_writereg(sc, 0x18, MII_BMCR, BMCR_RESET );
+
+ while( (cr = xl_miibus_readreg(sc, 0x18, MII_BMCR )) & BMCR_RESET )
+ {
+ DELAY(100000);
+ }
+
+ xl_miibus_writereg(sc, 0x18, MII_ANAR, ANAR_10 | ANAR_TX | ANAR_10_FD | ANAR_TX_FD ); /* ANAR_T4 */
+ xl_miibus_writereg(sc, 0x18, MII_BMCR, BMCR_STARTNEG | BMCR_AUTOEN );
+
+ for (i=0; ((sr = xl_miibus_readreg(sc, 0x18, MII_BMSR)) & BMSR_ACOMP) == 0 && i < 20; i++)
+ DELAY(10000);
+ }
+
+
+ /*
+ * Set up hardware if its not already been done
+ */
+ if( !sc->irqInfo.hdl )
+ {
+ elnk_initialize_hardware(sc);
+ }
+
+ /*
+ * Enable the card
+ */
+ {
+ u_int8_t rxfilt;
+
+ /* Init our MAC address */
+ XL_SEL_WIN(2);
+ for (i = 0; i < ETHER_ADDR_LEN; i++)
+ {
+ CSR_WRITE_1(sc, XL_W2_STATION_ADDR_LO + i, sc->arpcom.ac_enaddr[i]);
+ }
+
+ {
+ int media = IFM_ETHER|IFM_100_TX|IFM_FDX;
+
+ xl_mediacheck(sc);
+
+ /* Choose a default media. */
+ switch(sc->xl_xcvr) {
+ case XL_XCVR_10BT:
+ media = IFM_ETHER|IFM_10_T;
+ xl_setmode(sc, media);
+ break;
+ case XL_XCVR_AUI:
+ if (sc->xl_type == XL_TYPE_905B &&
+ sc->xl_media == XL_MEDIAOPT_10FL) {
+ media = IFM_ETHER|IFM_10_FL;
+ xl_setmode(sc, media);
+ } else {
+ media = IFM_ETHER|IFM_10_5;
+ xl_setmode(sc, media);
+ }
+ break;
+ case XL_XCVR_COAX:
+ media = IFM_ETHER|IFM_10_2;
+ xl_setmode(sc, media);
+ break;
+ case XL_XCVR_AUTO:
+ case XL_XCVR_100BTX:
+ xl_setcfg(sc);
+ break;
+ case XL_XCVR_MII:
+ printk(
+ "etherlink : unit elnk%d MII media not supported!\n",
+ sc->xl_unit);
+ break;
+ case XL_XCVR_100BFX:
+ media = IFM_ETHER|IFM_100_FX;
+ break;
+ default:
+ printk(
+ "etherlink : unit elnk%d unknown XCVR type: %" PRId32 "\n",
+ sc->xl_unit,
+ sc->xl_xcvr);
+ /*
+ * This will probably be wrong, but it prevents
+ * the ifmedia code from panicking.
+ */
+ media = IFM_ETHER|IFM_10_T;
+ break;
+ }
+
+
+ if (sc->xl_flags & XL_FLAG_NO_XCVR_PWR) {
+ XL_SEL_WIN(0);
+ CSR_WRITE_2(sc, XL_W0_MFG_ID, XL_NO_XCVR_PWR_MAGICBITS);
+ }
+ }
+
+
+
+ XL_SEL_WIN(2);
+ /* Clear the station mask. */
+ for (i = 0; i < 3; i++)
+ CSR_WRITE_2(sc, XL_W2_STATION_MASK_LO + (i * 2), 0);
+
+ /*
+ * Set the TX freethresh value.
+ * Note that this has no effect on 3c905B "cyclone"
+ * cards but is required for 3c900/3c905 "boomerang"
+ * cards in order to enable the download engine.
+ */
+ CSR_WRITE_1(sc, XL_TX_FREETHRESH, XL_PACKET_SIZE >> 8);
+
+ /* Set the TX start threshold for best performance. */
+ sc->xl_tx_thresh = XL_MIN_FRAMELEN;
+ CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_TX_SET_START|sc->xl_tx_thresh);
+
+ /*
+ * If this is a 3c905B, also set the tx reclaim threshold.
+ * This helps cut down on the number of tx reclaim errors
+ * that could happen on a busy network. The chip multiplies
+ * the register value by 16 to obtain the actual threshold
+ * in bytes, so we divide by 16 when setting the value here.
+ * The existing threshold value can be examined by reading
+ * the register at offset 9 in window 5.
+ */
+ if (sc->xl_type == XL_TYPE_905B) {
+ CSR_WRITE_2(sc, XL_COMMAND,
+ XL_CMD_SET_TX_RECLAIM|(XL_PACKET_SIZE >> 4));
+ }
+
+ /* Set RX filter bits. */
+ XL_SEL_WIN(5);
+ rxfilt = CSR_READ_1(sc, XL_W5_RX_FILTER);
+
+ /* Set the individual bit to receive frames for this host only. */
+ rxfilt |= XL_RXFILTER_INDIVIDUAL;
+
+ /* If we want promiscuous mode, set the allframes bit. */
+ if (ifp->if_flags & IFF_PROMISC) {
+ rxfilt |= XL_RXFILTER_ALLFRAMES;
+ CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_RX_SET_FILT|rxfilt);
+ } else {
+ rxfilt &= ~XL_RXFILTER_ALLFRAMES;
+ CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_RX_SET_FILT|rxfilt);
+ }
+
+ /*
+ * Set capture broadcast bit to capture broadcast frames.
+ */
+ if (ifp->if_flags & IFF_BROADCAST) {
+ rxfilt |= XL_RXFILTER_BROADCAST;
+ CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_RX_SET_FILT|rxfilt);
+ } else {
+ rxfilt &= ~XL_RXFILTER_BROADCAST;
+ CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_RX_SET_FILT|rxfilt);
+ }
+
+#if 0
+ /*
+ * Program the multicast filter, if necessary.
+ */
+ if (sc->xl_type == XL_TYPE_905B)
+ xl_setmulti_hash(sc);
+ else
+ xl_setmulti(sc);
+#endif
+ /*
+ * Load the address of the RX list. We have to
+ * stall the upload engine before we can manipulate
+ * the uplist pointer register, then unstall it when
+ * we're finished. We also have to wait for the
+ * stall command to complete before proceeding.
+ * Note that we have to do this after any RX resets
+ * have completed since the uplist register is cleared
+ * by a reset.
+ */
+ CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_UP_STALL);
+ xl_wait(sc);
+ CSR_WRITE_4(sc, XL_UPLIST_PTR, phys_to_bus( sc->curr_rx_md ));
+ CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_UP_UNSTALL);
+ xl_wait(sc);
+
+
+#if 0
+ if (sc->xl_type == XL_TYPE_905B) {
+ /* Set polling interval */
+ CSR_WRITE_1(sc, XL_DOWN_POLL, 64);
+ xl_wait(sc);
+ printk("etherlink : unit elnk%d tx polling enabled\n", sc->xl_unit );
+ }
+#endif
+
+ /*
+ * If the coax transceiver is on, make sure to enable
+ * the DC-DC converter.
+ */
+ XL_SEL_WIN(3);
+ if (sc->xl_xcvr == XL_XCVR_COAX)
+ CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_COAX_START);
+ else
+ CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_COAX_STOP);
+
+ /* increase packet size to allow reception of 802.1q or ISL packets */
+ if (sc->xl_type == XL_TYPE_905B)
+ CSR_WRITE_2(sc, XL_W3_MAXPKTSIZE, XL_PACKET_SIZE);
+ /* Clear out the stats counters. */
+
+ memset( &sc->xl_stats, 0, sizeof(struct xl_stats));
+
+ CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_STATS_DISABLE);
+ sc->xl_stats_no_timeout = 1;
+ xl_stats_update(sc->stat_timer_id,sc);
+ sc->xl_stats_no_timeout = 0;
+ XL_SEL_WIN(4);
+ CSR_WRITE_2(sc, XL_W4_NET_DIAG, XL_NETDIAG_UPPER_BYTES_ENABLE);
+ CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_STATS_ENABLE);
+
+
+ /*
+ * Enable interrupts.
+ */
+ CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_INTR_ACK|0xFF);
+ CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_STAT_ENB|XL_INTRS);
+ CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_INTR_ENB|XL_INTRS);
+
+ /* Set the RX early threshold */
+ CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_RX_SET_THRESH|(XL_PACKET_SIZE >>2));
+ CSR_WRITE_4(sc, XL_DMACTL, XL_DMACTL_UP_RX_EARLY );
+
+ /* Enable receiver and transmitter. */
+ CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_TX_ENABLE);
+ xl_wait(sc);
+ CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_RX_ENABLE);
+ xl_wait(sc);
+
+ /* Select window 7 for normal operations. */
+ XL_SEL_WIN(7);
+
+ /* schedule the stats update timer */
+ rtems_timer_fire_after( sc->stat_timer_id, sc->stats_update_ticks, xl_stats_update, (void *)sc );
+ }
+
+ /*
+ * Tell the world that we're running.
+ */
+ ifp->if_flags |= IFF_RUNNING;
+ }
+}
+
+
+
+
+
+
+
+/*
+ * Stop the device
+ */
+static void
+elnk_stop (struct elnk_softc *sc)
+{
+ struct ifnet *ifp = &sc->arpcom.ac_if;
+ int i;
+
+ /*
+ * Stop the transmitter
+ */
+ xl_stop(sc);
+ xl_reset(sc);
+ sc->tx_idle = -1;
+
+ ifp->if_flags &= ~IFF_RUNNING;
+
+ /*
+ ** Clear out the rx & tx rings
+ */
+ {
+ struct TXMD *chainhead;
+ size_t esize;
+
+ while( rtems_message_queue_receive( chainRecoveryQueue, &chainhead, &esize,
+ RTEMS_NO_WAIT, 0) == RTEMS_SUCCESSFUL );
+ }
+
+ for(i=0 ; i<sc->numRxbuffers; i++)
+ {
+ st_le32( &sc->rx_ring[i].status, 0);
+ st_le32( &sc->rx_ring[i].length, XL_LAST_FRAG | XL_PACKET_SIZE );
+ }
+
+ for(i=0 ; i<sc->numTxbuffers; i++)
+ {
+ st_le32( &sc->tx_ring[i].status, XL_TXSTAT_DL_COMPLETE );
+ st_le32( &sc->tx_ring[i].next, 0);
+ if( sc->tx_ring[i].mbuf )
+ {
+ m_free( sc->tx_ring[i].mbuf );
+ sc->tx_ring[i].mbuf = NULL;
+ }
+ }
+}
+
+
+
+
+/*
+ * Show interface statistics
+ */
+static void
+elnk_stats (struct elnk_softc *sc)
+{
+ printf(" MII PHY data { anr:%04x lpar:%04x stat:%04x ctl:%04x }\n",
+ sc->xl_stats.miianr,
+ sc->xl_stats.miipar,
+ sc->xl_stats.miistatus,
+ sc->xl_stats.miicmd);
+
+ printf(" internalcfg:%08" PRIx32 " macctl:%04x dmactl:%08" PRIx32 "\n",
+ sc->xl_stats.internalconfig,
+ sc->xl_stats.mac_control,
+ sc->xl_stats.dmactl);
+
+ printf(" rxstatus:%04x txstatus:%02x smbstat:%04x\n",
+ sc->xl_stats.rxstatus,
+ sc->xl_stats.txstatus,
+ sc->xl_stats.smbstatus);
+
+ printf(" txfree:%04X intstatus:%04x mediastat:%04x\n",
+ sc->xl_stats.txfree,
+ sc->xl_stats.intstatus,
+ sc->xl_stats.mediastatus);
+
+
+ {
+ int i, totalLengths= 0, numLengths= 0;
+
+ for(i=0; i< NUM_CHAIN_LENGTHS; i++)
+ {
+ if( sc->chain_lengths[i] > -1 )
+ {
+ totalLengths += sc->chain_lengths[i];
+ ++numLengths;
+ }
+ }
+
+ printf(" interrupts:%-9" PRIu32 " txcmp_ints:%-5" PRIu32 " avg_chain_len:%-4d\n",
+ sc->xl_stats.device_interrupts,
+ sc->xl_stats.txcomplete_ints,
+ numLengths ? (totalLengths / numLengths) : -1 );
+ }
+
+ printf(" carrier_lost:%-5d sqe_errs:%-5d\n",
+ sc->xl_stats.xl_carrier_lost,
+ sc->xl_stats.xl_sqe_errs);
+
+ printf(" tx_multi_collision:%-5d tx_single_collision:%-5d\n",
+ sc->xl_stats.xl_tx_multi_collision,
+ sc->xl_stats.xl_tx_single_collision);
+
+ printf(" tx_late_collision:%-5d rx_overrun:%-5d\n",
+ sc->xl_stats.xl_tx_late_collision,
+ sc->xl_stats.xl_rx_overrun);
+
+ printf(" tx_deferred:%-5d badssd:%-5d\n",
+ sc->xl_stats.xl_tx_deferred,
+ sc->xl_stats.xl_badssd);
+
+ printf(" rx_frames_ok:%-9" PRIu32 " tx_frames_ok:%-9" PRIu32 "\n",
+ sc->xl_stats.xl_rx_frames_ok,
+ sc->xl_stats.xl_tx_frames_ok);
+
+ printf(" rx_bytes_ok:%-9" PRIu32 " tx_bytes_ok:%-9" PRIu32 "\n",
+ sc->xl_stats.xl_rx_bytes_ok,
+ sc->xl_stats.xl_tx_bytes_ok );
+}
+
+
+
+
+
+
+
+/*
+ * Driver ioctl handler
+ */
+static int
+elnk_ioctl (struct ifnet *ifp, ioctl_command_t command, caddr_t data)
+{
+ struct elnk_softc *sc = ifp->if_softc;
+ int error = 0;
+
+ switch (command) {
+ case SIOCGIFADDR:
+ case SIOCSIFADDR:
+ ether_ioctl (ifp, command, data);
+ break;
+
+ case SIOCSIFFLAGS:
+ switch (ifp->if_flags & (IFF_UP | IFF_RUNNING)) {
+ case IFF_RUNNING:
+ elnk_stop (sc);
+ break;
+
+ case IFF_UP:
+ elnk_init (sc);
+ break;
+
+ case IFF_UP | IFF_RUNNING:
+ elnk_stop (sc);
+ elnk_init (sc);
+ break;
+
+ default:
+ break;
+ }
+ break;
+
+ case SIO_RTEMS_SHOW_STATS:
+ elnk_stats (sc);
+ break;
+
+ /*
+ * FIXME: All sorts of multicast commands need to be added here!
+ */
+ default:
+ error = EINVAL;
+ break;
+ }
+
+ return error;
+}
+
+
+
+
+
+
+
+
+#if 0
+static int iftap(struct ifnet *ifp, struct ether_header *eh, struct mbuf *m )
+{
+ int i;
+ char *delim, *pkt;
+
+ printk("unit %i, src ", ifp->if_unit );
+ for(delim= "", i=0; i< ETHER_ADDR_LEN; i++, delim=":")
+ printk("%s%02x", delim, (char) eh->ether_shost[i] );
+
+ printk(" dest ");
+
+ for(delim= "", i=0; i< ETHER_ADDR_LEN; i++, delim=":")
+ printk("%s%02x", delim, (char) eh->ether_dhost[i] );
+ printk(" pkt ");
+
+ pkt = (char *)eh;
+ for(delim="", i=0; i < sizeof(struct ether_header); i++, delim=":")
+ printk("%s%02x", delim, (char) pkt[i] );
+
+ printk("\n");
+ return 0;
+}
+#endif
+
+
+
+struct el_boards
+{
+ int pbus,pdev,pfun, vid, did, tindex;
+};
+
+/* Prototype to avoid warning. This must be a global symbol. */
+int rtems_elnk_driver_attach(struct rtems_bsdnet_ifconfig *config, int attach);
+
+/*
+ * Attach an ELNK driver to the system
+ */
+int
+rtems_elnk_driver_attach (struct rtems_bsdnet_ifconfig *config, int attach)
+{
+ struct elnk_softc *sc;
+ struct ifnet *ifp;
+ char *unitName;
+ int unitNumber;
+ int mtu, i;
+ unsigned char cvalue;
+ struct el_boards sysboards[NUM_UNITS];
+ int numFound = 0;
+ int pbus, pdev, pfun;
+#if defined(__i386__)
+ uint32_t value;
+ uint8_t interrupt;
+#endif
+#if defined(__PPC__)
+ uint32_t lvalue;
+#endif
+
+
+ /*
+ * Get the instance number for the board we're going to configure
+ * from the user.
+ */
+ if( (unitNumber = rtems_bsdnet_parse_driver_name( config, &unitName)) == -1 )
+ {
+ return 0;
+ }
+
+ if( strcmp(unitName, DRIVER_PREFIX) )
+ {
+ printk("etherlink : invalid unit name '%s'\n", unitName );
+ return 0;
+ }
+
+ if ((unitNumber < 1) || (unitNumber > NUM_UNITS))
+ {
+ printk("etherlink : unit %i is invalid, must be (1 <= n <= %d)\n", unitNumber, NUM_UNITS);
+ return 0;
+ }
+
+
+ {
+ int done= 0, unum;
+
+ /*
+ * Run thru the list of boards, finding all that are present in
+ * the system. Sort by slot,dev - and then use the unitNumber-1
+ * to index the list and select the device. Yucky.
+ */
+ for( i=0; !done && xl_devs[i].xl_vid; i++)
+ {
+ for(unum= 1; !done &&
+ pci_find_device( xl_devs[i].xl_vid, xl_devs[i].xl_did, unum-1,
+ &sysboards[numFound].pbus,
+ &sysboards[numFound].pdev,
+ &sysboards[numFound].pfun)==0; unum++)
+ {
+ if( numFound == NUM_UNITS )
+ {
+ printk("etherlink : Maximum of %d units found, extra devices ignored.\n", NUM_UNITS );
+ done=-1;
+ }
+ else
+ {
+ sysboards[numFound].vid = xl_devs[i].xl_vid;
+ sysboards[numFound].did = xl_devs[i].xl_did;
+ sysboards[numFound].tindex = i;
+ ++numFound;
+ }
+ }
+ }
+
+ if( ! numFound )
+ {
+ printk("etherlink : No Etherlink devices found\n");
+ return 0;
+ }
+
+ if( unitNumber-1 >= numFound )
+ {
+ printk("etherlink : device '%s' not found\n", config->name );
+ return 0;
+ }
+
+ /*
+ * Got the list of etherlink boards in the system, now sort by
+ * slot,device. bubble sorts aren't all that wonderful, but this
+ * is a short & infrequently sorted list.
+ */
+ if( numFound > 1 )
+ {
+ struct el_boards tboard;
+ int didsort;
+
+ do
+ {
+ didsort = 0;
+
+ for(i=1; i<numFound; i++)
+ {
+ if( sysboards[i-1].pbus > sysboards[i].pbus ||
+ (sysboards[i-1].pbus == sysboards[i].pbus && sysboards[i-1].pdev > sysboards[i].pdev) )
+ {
+ memcpy(&tboard, &sysboards[i-1], sizeof(struct el_boards));
+ memcpy(&sysboards[i-1], &sysboards[i], sizeof(struct el_boards));
+ memcpy(&sysboards[i], &tboard, sizeof(struct el_boards));
+ didsort++;
+ }
+ }
+ }
+ while( didsort );
+ }
+
+ /*
+ ** board list is sorted, now select the unit
+ */
+
+ pbus = sysboards[unitNumber-1].pbus;
+ pdev = sysboards[unitNumber-1].pdev;
+ pfun = sysboards[unitNumber-1].pfun;
+ }
+
+ sc = &elnk_softc[unitNumber - 1];
+ ifp = &sc->arpcom.ac_if;
+ if (ifp->if_softc != NULL)
+ {
+ printk("etherlink : unit %i already in use.\n", unitNumber );
+ return 0;
+ }
+
+ /*
+ ** Save various things
+ */
+ sc->xl_unit = unitNumber;
+ sc->xl_type = sysboards[ unitNumber-1 ].tindex;
+
+ sc->vendorID = sysboards[numFound].vid;
+ sc->deviceID = sysboards[numFound].did;
+
+ sc->numRxbuffers = (config->rbuf_count) ? config->rbuf_count : RX_RING_SIZE;
+ sc->numTxbuffers = (config->xbuf_count) ? config->xbuf_count : TX_RING_SIZE;
+
+
+ for(i=0; i< NUM_CHAIN_LENGTHS; i++) sc->chain_lengths[i]= -1;
+ sc->chlenIndex = 0;
+
+
+ if (config->mtu)
+ mtu = config->mtu;
+ else
+ mtu = ETHERMTU;
+
+ sc->acceptBroadcast = !config->ignore_broadcast;
+
+
+
+#ifdef ELNK_DEBUG
+ printk("etherlink : device '%s', name 'elnk%d', pci %02x:%02x.%02x, %d rx/%d tx buffers\n",
+ xl_devs[sc->xl_type].xl_name, sc->xl_unit,
+ pbus, pdev, pfun,
+ sc->numRxbuffers, sc->numTxbuffers);
+#endif
+
+
+ /*
+ ** Create this unit's stats timer
+ */
+ if( rtems_timer_create( rtems_build_name( 'X', 'L', 't', (char)(sc->xl_unit & 255)),
+ &sc->stat_timer_id ) != RTEMS_SUCCESSFUL )
+ {
+ printk("etherlink : unit elnk%d unable to create stats timer\n", sc->xl_unit );
+ return 0;
+ }
+
+ /* update stats 1 times/second if things aren't incrementing fast
+ * enough to trigger stats interrupts
+ */
+ sc->stats_update_ticks = rtems_clock_get_ticks_per_second();
+
+ /*
+ ** Get this unit's rx/tx event
+ */
+ sc->ioevent = unit_signals[unitNumber-1];
+
+
+#if defined(__i386__)
+ pci_read_config_dword(pbus, pdev, pfun, 16, &value);
+ sc->ioaddr = value & ~IO_MASK;
+
+ pci_read_config_byte(pbus, pdev, pfun, 60, &interrupt);
+ cvalue = interrupt;
+#endif
+#if defined(__PPC__)
+ /*
+ ** Prep the board
+ */
+ pci_write_config_word(pbus, pdev, pfun,
+ PCI_COMMAND,
+ (uint16_t)( PCI_COMMAND_IO |
+ PCI_COMMAND_MASTER |
+ PCI_COMMAND_INVALIDATE |
+ PCI_COMMAND_WAIT ) );
+ /*
+ * Get the device's base address
+ */
+ pci_read_config_dword(pbus, pdev, pfun,
+ PCI_BASE_ADDRESS_0,
+ &lvalue);
+
+ sc->ioaddr = (uint32_t)lvalue & PCI_BASE_ADDRESS_IO_MASK;
+ /*
+ ** Store the interrupt name, we'll use it later when we initialize
+ ** the board.
+ */
+ pci_read_config_byte(pbus, pdev, pfun,
+ PCI_INTERRUPT_LINE,
+ &cvalue);
+#endif
+
+ memset(&sc->irqInfo,0,sizeof(rtems_irq_connect_data));
+ sc->irqInfo.name = cvalue;
+
+
+ /*
+ ** Establish basic board config, set node address from config or
+ ** board eeprom, do stuff with additional device properties
+ */
+
+ {
+ uint8_t pci_latency;
+ uint8_t new_latency = 248;
+
+ /* Check the PCI latency value. On the 3c590 series the latency timer
+ must be set to the maximum value to avoid data corruption that occurs
+ when the timer expires during a transfer. This bug exists the Vortex
+ chip only. */
+#if defined(__i386__)
+ pci_read_config_byte(pbus, pdev, pfun, 0x0d, &pci_latency);
+#endif
+#if defined(__PPC__)
+ pci_read_config_byte(pbus,pdev,pfun, PCI_LATENCY_TIMER, &pci_latency);
+#endif
+ if (pci_latency < new_latency)
+ {
+ printk("etherlink : unit elnk%d Overriding PCI latency, timer (CFLT) setting of %d, new value is %d.\n", sc->xl_unit, pci_latency, new_latency );
+#if defined(__i386__)
+ pci_write_config_byte(pbus, pdev, pfun, 0x0d, new_latency);
+#endif
+#if defined(__PPC__)
+ pci_write_config_byte(pbus,pdev,pfun, PCI_LATENCY_TIMER, new_latency);
+#endif
+ }
+ }
+
+ /* Reset the adapter. */
+ xl_reset(sc);
+
+
+ {
+ u_int16_t xcvr[2];
+ u_char eaddr[ETHER_ADDR_LEN];
+
+ sc->xl_flags = 0;
+ if (sc->deviceID == TC_DEVICEID_HURRICANE_555)
+ sc->xl_flags |= XL_FLAG_EEPROM_OFFSET_30 | XL_FLAG_PHYOK;
+ if (sc->deviceID == TC_DEVICEID_HURRICANE_556 ||
+ sc->deviceID == TC_DEVICEID_HURRICANE_556B)
+ sc->xl_flags |= XL_FLAG_FUNCREG | XL_FLAG_PHYOK |
+ XL_FLAG_EEPROM_OFFSET_30 | XL_FLAG_WEIRDRESET |
+ XL_FLAG_INVERT_LED_PWR | XL_FLAG_INVERT_MII_PWR;
+ if (sc->deviceID == TC_DEVICEID_HURRICANE_555 ||
+ sc->deviceID == TC_DEVICEID_HURRICANE_556)
+ sc->xl_flags |= XL_FLAG_8BITROM;
+ if (sc->deviceID == TC_DEVICEID_HURRICANE_556B)
+ sc->xl_flags |= XL_FLAG_NO_XCVR_PWR;
+
+ if (sc->deviceID == TC_DEVICEID_HURRICANE_575A ||
+ sc->deviceID == TC_DEVICEID_HURRICANE_575B ||
+ sc->deviceID == TC_DEVICEID_HURRICANE_575C ||
+ sc->deviceID == TC_DEVICEID_HURRICANE_656B ||
+ sc->deviceID == TC_DEVICEID_TORNADO_656C)
+ sc->xl_flags |= XL_FLAG_FUNCREG | XL_FLAG_PHYOK |
+ XL_FLAG_EEPROM_OFFSET_30 | XL_FLAG_8BITROM;
+ if (sc->deviceID == TC_DEVICEID_HURRICANE_656)
+ sc->xl_flags |= XL_FLAG_FUNCREG | XL_FLAG_PHYOK;
+ if (sc->deviceID == TC_DEVICEID_HURRICANE_575B)
+ sc->xl_flags |= XL_FLAG_INVERT_LED_PWR;
+ if (sc->deviceID == TC_DEVICEID_HURRICANE_575C)
+ sc->xl_flags |= XL_FLAG_INVERT_MII_PWR;
+ if (sc->deviceID == TC_DEVICEID_TORNADO_656C)
+ sc->xl_flags |= XL_FLAG_INVERT_MII_PWR;
+ if (sc->deviceID == TC_DEVICEID_HURRICANE_656 ||
+ sc->deviceID == TC_DEVICEID_HURRICANE_656B)
+ sc->xl_flags |= XL_FLAG_INVERT_MII_PWR |
+ XL_FLAG_INVERT_LED_PWR;
+ if (sc->deviceID == TC_DEVICEID_TORNADO_10_100BT_920B)
+ sc->xl_flags |= XL_FLAG_PHYOK;
+
+
+ if (config->hardware_address)
+ {
+ memcpy(sc->arpcom.ac_enaddr, config->hardware_address, ETHER_ADDR_LEN);
+ }
+ else
+ {
+ if (xl_read_eeprom(sc, (caddr_t)&eaddr, XL_EE_OEM_ADR0, 3, 1))
+ {
+ printk("etherlink : unit elnk%d Failed to read station address\n", sc->xl_unit );
+ return 0;
+ }
+ memcpy((char *)&sc->arpcom.ac_enaddr, eaddr, ETHER_ADDR_LEN);
+ }
+
+ /*
+ * Figure out the card type. 3c905B adapters have the
+ * 'supportsNoTxLength' bit set in the capabilities
+ * word in the EEPROM.
+ */
+ xl_read_eeprom(sc, (caddr_t)&sc->xl_caps, XL_EE_CAPS, 1, 0);
+ if (sc->xl_caps & XL_CAPS_NO_TXLENGTH)
+ sc->xl_type = XL_TYPE_905B;
+ else
+ sc->xl_type = XL_TYPE_90X;
+
+
+ /*
+ * Now we have to see what sort of media we have.
+ * This includes probing for an MII interace and a
+ * possible PHY.
+ */
+ XL_SEL_WIN(3);
+ sc->xl_media = CSR_READ_2(sc, XL_W3_MEDIA_OPT);
+
+ xl_read_eeprom(sc, (char *)&xcvr, XL_EE_ICFG_0, 2, 0);
+ sc->xl_xcvr = xcvr[0] | xcvr[1] << 16;
+ sc->xl_xcvr &= XL_ICFG_CONNECTOR_MASK;
+ sc->xl_xcvr >>= XL_ICFG_CONNECTOR_BITS;
+
+#if 0
+ printk("etherlink : unit elnk%d EEPROM set xcvr to 0x%x\n", sc->xl_unit, sc->xl_xcvr);
+#endif
+
+ {
+ char msg[255];
+ int i;
+
+ struct _availmedia
+ {
+ int bit;
+ char *name;
+ } _am[]= {{ XL_MEDIAOPT_BT4, "100BaseT4" },
+ { XL_MEDIAOPT_BTX, "100BaseTX" },
+ { XL_MEDIAOPT_BFX, "100BaseFX" },
+ { XL_MEDIAOPT_BT, "10BaseT" },
+ { XL_MEDIAOPT_BNC, "10Base2" },
+ { XL_MEDIAOPT_AUI, "10mbps AUI"},
+ { XL_MEDIAOPT_MII, "MII"},
+ { 0, NULL }};
+
+ msg[0]= 0;
+ for( i=0; _am[i].bit; i++)
+ {
+ if( sc->xl_media & _am[i].bit )
+ sprintf( &msg[strlen(msg)], ",%s", _am[i].name );
+ }
+ if( !strlen(msg) ) strcpy( &msg[1], "<no media bits>");
+
+ printk("etherlink : unit elnk%d available media : %s\n", sc->xl_unit, &msg[1]);
+ }
+
+ XL_SEL_WIN(7);
+ }
+
+
+
+ /*
+ * Set up network interface
+ */
+ ifp->if_softc = sc;
+ ifp->if_name = unitName;
+ ifp->if_unit = sc->xl_unit;
+ ifp->if_mtu = mtu;
+ ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX;
+ if (ifp->if_snd.ifq_maxlen == 0)
+ ifp->if_snd.ifq_maxlen = ifqmaxlen;
+ ifp->if_init = elnk_init;
+ ifp->if_start = elnk_start;
+ ifp->if_ioctl = elnk_ioctl;
+ ifp->if_output = ether_output;
+
+#if 0
+ ifp->if_tap = iftap;
+#endif
+
+ /*
+ * Attach the interface
+ */
+ if_attach (ifp);
+ ether_ifattach (ifp);
+
+#ifdef ELNK_DEBUG
+ printk( "etherlink : unit elnk%d driver attached\n", sc->xl_unit );
+#endif
+
+ /*
+ * Start driver tasks if this is the first unit initialized
+ */
+ if (txDaemonTid == 0)
+ {
+ if( rtems_message_queue_create( rtems_build_name('X','L','c','r'),
+ sc->numTxbuffers+1,
+ sizeof(struct TXMD *),
+ RTEMS_FIFO | RTEMS_LOCAL,
+ &chainRecoveryQueue ) != RTEMS_SUCCESSFUL )
+ {
+ rtems_panic( "etherlink : Unable to create TX buffer recovery queue\n" );
+ }
+
+
+ rxDaemonTid = rtems_bsdnet_newproc( "XLrx", 4096,
+ elnk_rxDaemon, NULL);
+
+ txDaemonTid = rtems_bsdnet_newproc( "XLtx", 4096,
+ elnk_txDaemon, NULL);
+#ifdef ELNK_DEBUG
+ printk( "etherlink : driver tasks created\n" );
+#endif
+ }
+
+ return 1;
+};
+
+#endif /* ELNK_SUPPORTED */
+
+/* eof */
diff --git a/bsps/shared/net/greth2.c b/bsps/shared/net/greth2.c
new file mode 100644
index 0000000000..20be83ee48
--- /dev/null
+++ b/bsps/shared/net/greth2.c
@@ -0,0 +1,1196 @@
+/*
+ * Gaisler Research ethernet MAC driver
+ * adapted from Opencores driver by Marko Isomaki
+ *
+ * The license and distribution terms for this file may be
+ * found in the file LICENSE in this distribution or at
+ * http://www.rtems.org/license/LICENSE.
+ *
+ * 2007-09-07, Ported GBIT support from 4.6.5
+ */
+
+#define __INSIDE_RTEMS_BSD_TCPIP_STACK__
+
+#include <rtems.h>
+#include <bsp.h>
+
+#ifdef GRETH_SUPPORTED
+
+#include <inttypes.h>
+#include <errno.h>
+#include <rtems/bspIo.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <rtems/error.h>
+#include <rtems/rtems_bsdnet.h>
+#include <libchip/greth.h>
+
+#include <sys/param.h>
+#include <sys/mbuf.h>
+
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <netinet/if_ether.h>
+
+#ifdef malloc
+#undef malloc
+#endif
+#ifdef free
+#undef free
+#endif
+
+/* #define GRETH_DEBUG */
+
+#ifdef CPU_U32_FIX
+extern void ipalign(struct mbuf *m);
+#endif
+
+/* Used when reading from memory written by GRETH DMA unit */
+#ifndef GRETH_MEM_LOAD
+#define GRETH_MEM_LOAD(addr) (*(volatile unsigned int *)(addr))
+#endif
+
+/*
+ * Number of OCs supported by this driver
+ */
+#define NOCDRIVER 1
+
+/*
+ * Receive buffer size -- Allow for a full ethernet packet including CRC
+ */
+#define RBUF_SIZE 1518
+
+#define ET_MINLEN 64 /* minimum message length */
+
+/*
+ * RTEMS event used by interrupt handler to signal driver tasks.
+ * This must not be any of the events used by the network task synchronization.
+ */
+#define INTERRUPT_EVENT RTEMS_EVENT_1
+
+/*
+ * RTEMS event used to start transmit daemon.
+ * This must not be the same as INTERRUPT_EVENT.
+ */
+#define START_TRANSMIT_EVENT RTEMS_EVENT_2
+
+ /* event to send when tx buffers become available */
+#define GRETH_TX_WAIT_EVENT RTEMS_EVENT_3
+
+#if (MCLBYTES < RBUF_SIZE)
+# error "Driver must have MCLBYTES > RBUF_SIZE"
+#endif
+
+/* 4s Autonegotiation Timeout */
+#ifndef GRETH_AUTONEGO_TIMEOUT_MS
+#define GRETH_AUTONEGO_TIMEOUT_MS 4000
+#endif
+const struct timespec greth_tan = {
+ GRETH_AUTONEGO_TIMEOUT_MS/1000,
+ (GRETH_AUTONEGO_TIMEOUT_MS % 1000) *1000000
+};
+
+/* For optimizing the autonegotiation time */
+#define GRETH_AUTONEGO_PRINT_TIME
+
+/* Ethernet buffer descriptor */
+
+typedef struct _greth_rxtxdesc {
+ volatile uint32_t ctrl; /* Length and status */
+ uint32_t *addr; /* Buffer pointer */
+} greth_rxtxdesc;
+
+
+/*
+ * Per-device data
+ */
+struct greth_softc
+{
+
+ struct arpcom arpcom;
+
+ greth_regs *regs;
+
+ int acceptBroadcast;
+ rtems_id daemonTid;
+
+ unsigned int tx_ptr;
+ unsigned int tx_dptr;
+ unsigned int tx_cnt;
+ unsigned int rx_ptr;
+ unsigned int txbufs;
+ unsigned int rxbufs;
+ greth_rxtxdesc *txdesc;
+ greth_rxtxdesc *rxdesc;
+ struct mbuf **rxmbuf;
+ struct mbuf **txmbuf;
+ rtems_vector_number vector;
+
+ /* TX descriptor interrupt generation */
+ int tx_int_gen;
+ int tx_int_gen_cur;
+ struct mbuf *next_tx_mbuf;
+ int max_fragsize;
+
+ /*Status*/
+ struct phy_device_info phydev;
+ int fd;
+ int sp;
+ int gb;
+ int gbit_mac;
+ int auto_neg;
+ struct timespec auto_neg_time;
+
+ /*
+ * Statistics
+ */
+ unsigned long rxInterrupts;
+
+ unsigned long rxPackets;
+ unsigned long rxLengthError;
+ unsigned long rxNonOctet;
+ unsigned long rxBadCRC;
+ unsigned long rxOverrun;
+
+ unsigned long txInterrupts;
+
+ unsigned long txDeferred;
+ unsigned long txHeartbeat;
+ unsigned long txLateCollision;
+ unsigned long txRetryLimit;
+ unsigned long txUnderrun;
+
+};
+
+static struct greth_softc greth;
+
+int greth_process_tx_gbit(struct greth_softc *sc);
+int greth_process_tx(struct greth_softc *sc);
+
+static char *almalloc(int sz)
+{
+ char *tmp;
+ tmp = calloc(1,2*sz);
+ tmp = (char *) (((uintptr_t)tmp+sz) & ~(sz -1));
+ return(tmp);
+}
+
+/* GRETH interrupt handler */
+
+static void greth_interrupt_handler (void *arg)
+{
+ uint32_t status;
+ uint32_t ctrl;
+ rtems_event_set events = 0;
+ struct greth_softc *greth = arg;
+
+ /* read and clear interrupt cause */
+ status = greth->regs->status;
+ greth->regs->status = status;
+ ctrl = greth->regs->ctrl;
+
+ /* Frame received? */
+ if ((ctrl & GRETH_CTRL_RXIRQ) && (status & (GRETH_STATUS_RXERR | GRETH_STATUS_RXIRQ)))
+ {
+ greth->rxInterrupts++;
+ /* Stop RX-Error and RX-Packet interrupts */
+ ctrl &= ~GRETH_CTRL_RXIRQ;
+ events |= INTERRUPT_EVENT;
+ }
+
+ if ( (ctrl & GRETH_CTRL_TXIRQ) && (status & (GRETH_STATUS_TXERR | GRETH_STATUS_TXIRQ)) )
+ {
+ greth->txInterrupts++;
+ ctrl &= ~GRETH_CTRL_TXIRQ;
+ events |= GRETH_TX_WAIT_EVENT;
+ }
+
+ /* Clear interrupt sources */
+ greth->regs->ctrl = ctrl;
+
+ /* Send the event(s) */
+ if ( events )
+ rtems_bsdnet_event_send (greth->daemonTid, events);
+}
+
+static uint32_t read_mii(uint32_t phy_addr, uint32_t reg_addr)
+{
+ while (greth.regs->mdio_ctrl & GRETH_MDIO_BUSY) {}
+ greth.regs->mdio_ctrl = (phy_addr << 11) | (reg_addr << 6) | GRETH_MDIO_READ;
+ while (greth.regs->mdio_ctrl & GRETH_MDIO_BUSY) {}
+ if (!(greth.regs->mdio_ctrl & GRETH_MDIO_LINKFAIL))
+ return((greth.regs->mdio_ctrl >> 16) & 0xFFFF);
+ else {
+ printf("greth: failed to read mii\n");
+ return (0);
+ }
+}
+
+static void write_mii(uint32_t phy_addr, uint32_t reg_addr, uint32_t data)
+{
+ while (greth.regs->mdio_ctrl & GRETH_MDIO_BUSY) {}
+ greth.regs->mdio_ctrl =
+ ((data & 0xFFFF) << 16) | (phy_addr << 11) | (reg_addr << 6) | GRETH_MDIO_WRITE;
+ while (greth.regs->mdio_ctrl & GRETH_MDIO_BUSY) {}
+}
+
+static void print_init_info(struct greth_softc *sc)
+{
+ printf("greth: driver attached\n");
+ if ( sc->auto_neg == -1 ){
+ printf("Auto negotiation timed out. Selecting default config\n");
+ }
+ printf("**** PHY ****\n");
+ printf("Vendor: %x Device: %x Revision: %d\n",sc->phydev.vendor, sc->phydev.device, sc->phydev.rev);
+ printf("Current Operating Mode: ");
+ if (sc->gb) {
+ printf("1000 Mbit ");
+ } else if (sc->sp) {
+ printf("100 Mbit ");
+ } else {
+ printf("10 Mbit ");
+ }
+ if (sc->fd) {
+ printf("Full Duplex\n");
+ } else {
+ printf("Half Duplex\n");
+ }
+#ifdef GRETH_AUTONEGO_PRINT_TIME
+ if ( sc->auto_neg ) {
+ printf("Autonegotiation Time: %ldms\n", sc->auto_neg_time.tv_sec*1000 +
+ sc->auto_neg_time.tv_nsec/1000000);
+ }
+#endif
+}
+
+
+/*
+ * Initialize the ethernet hardware
+ */
+static void
+greth_initialize_hardware (struct greth_softc *sc)
+{
+ struct mbuf *m;
+ int i;
+ int phyaddr;
+ int phyctrl;
+ int phystatus;
+ int tmp1;
+ int tmp2;
+ struct timespec tstart, tnow;
+
+ greth_regs *regs;
+
+ regs = sc->regs;
+
+ /* Reset the controller. */
+ greth.rxInterrupts = 0;
+ greth.rxPackets = 0;
+
+ regs->ctrl = 0;
+ regs->ctrl = GRETH_CTRL_RST; /* Reset ON */
+ regs->ctrl = 0; /* Reset OFF */
+
+ /* Check if mac is gbit capable*/
+ sc->gbit_mac = (regs->ctrl >> 27) & 1;
+
+ /* Get the phy address which assumed to have been set
+ correctly with the reset value in hardware*/
+ phyaddr = (regs->mdio_ctrl >> 11) & 0x1F;
+
+ /* get phy control register default values */
+ while ((phyctrl = read_mii(phyaddr, 0)) & 0x8000) {}
+
+ /* reset PHY and wait for completion */
+ write_mii(phyaddr, 0, 0x8000 | phyctrl);
+
+ while ((read_mii(phyaddr, 0)) & 0x8000) {}
+ phystatus = read_mii(phyaddr, 1);
+
+ /* Disable Gbit auto-neg advertisement if MAC does not support it */
+
+ if ((!sc->gbit_mac) && (phystatus & 0x100)) write_mii(phyaddr, 9, 0);
+
+ /* Restart auto-negotiation if available */
+ if (phystatus & 0x08) {
+ write_mii(phyaddr, 0, phyctrl | 0x1200);
+ phyctrl = read_mii(phyaddr, 0);
+ }
+
+ /* Check if PHY is autoneg capable and then determine operating mode,
+ otherwise force it to 10 Mbit halfduplex */
+ sc->gb = 0;
+ sc->fd = 0;
+ sc->sp = 0;
+ sc->auto_neg = 0;
+ _Timespec_Set_to_zero(&sc->auto_neg_time);
+ if ((phyctrl >> 12) & 1) {
+ /*wait for auto negotiation to complete*/
+ sc->auto_neg = 1;
+ if (rtems_clock_get_uptime(&tstart) != RTEMS_SUCCESSFUL)
+ printk("rtems_clock_get_uptime failed\n");
+ while (!(((phystatus = read_mii(phyaddr, 1)) >> 5) & 1)) {
+ if (rtems_clock_get_uptime(&tnow) != RTEMS_SUCCESSFUL)
+ printk("rtems_clock_get_uptime failed\n");
+ _Timespec_Subtract(&tstart, &tnow, &sc->auto_neg_time);
+ if (_Timespec_Greater_than(&sc->auto_neg_time, &greth_tan)) {
+ sc->auto_neg = -1; /* Failed */
+ tmp1 = read_mii(phyaddr, 0);
+ sc->gb = ((phyctrl >> 6) & 1) && !((phyctrl >> 13) & 1);
+ sc->sp = !((phyctrl >> 6) & 1) && ((phyctrl >> 13) & 1);
+ sc->fd = (phyctrl >> 8) & 1;
+ goto auto_neg_done;
+ }
+ /* Wait about 30ms, time is PHY dependent */
+ rtems_task_wake_after(rtems_clock_get_ticks_per_second()/32);
+ }
+ sc->phydev.adv = read_mii(phyaddr, 4);
+ sc->phydev.part = read_mii(phyaddr, 5);
+ if ((phystatus >> 8) & 1) {
+ sc->phydev.extadv = read_mii(phyaddr, 9);
+ sc->phydev.extpart = read_mii(phyaddr, 10);
+ if ( (sc->phydev.extadv & GRETH_MII_EXTADV_1000FD) &&
+ (sc->phydev.extpart & GRETH_MII_EXTPRT_1000FD)) {
+ sc->gb = 1;
+ sc->fd = 1;
+ }
+ else if ( (sc->phydev.extadv & GRETH_MII_EXTADV_1000HD) &&
+ (sc->phydev.extpart & GRETH_MII_EXTPRT_1000HD)) {
+ sc->gb = 1;
+ sc->fd = 0;
+ }
+ }
+ if ((sc->gb == 0) || ((sc->gb == 1) && (sc->gbit_mac == 0))) {
+ if ( (sc->phydev.adv & GRETH_MII_100TXFD) &&
+ (sc->phydev.part & GRETH_MII_100TXFD)) {
+ sc->sp = 1;
+ sc->fd = 1;
+ }
+ else if ( (sc->phydev.adv & GRETH_MII_100TXHD) &&
+ (sc->phydev.part & GRETH_MII_100TXHD)) {
+ sc->sp = 1;
+ sc->fd = 0;
+ }
+ else if ( (sc->phydev.adv & GRETH_MII_10FD) &&
+ (sc->phydev.part & GRETH_MII_10FD)) {
+ sc->fd = 1;
+ }
+ }
+ }
+auto_neg_done:
+ sc->phydev.vendor = 0;
+ sc->phydev.device = 0;
+ sc->phydev.rev = 0;
+ phystatus = read_mii(phyaddr, 1);
+
+ /*Read out PHY info if extended registers are available */
+ if (phystatus & 1) {
+ tmp1 = read_mii(phyaddr, 2);
+ tmp2 = read_mii(phyaddr, 3);
+
+ sc->phydev.vendor = (tmp1 << 6) | ((tmp2 >> 10) & 0x3F);
+ sc->phydev.rev = tmp2 & 0xF;
+ sc->phydev.device = (tmp2 >> 4) & 0x3F;
+ }
+
+ /* Force to 10 mbit half duplex if the 10/100 MAC is used with a 1000 PHY*/
+ /*check if marvell 88EE1111 PHY. Needs special reset handling */
+ if ((phystatus & 1) && (sc->phydev.vendor == 0x005043) && (sc->phydev.device == 0x0C)) {
+ if (((sc->gb) && !(sc->gbit_mac)) || !((phyctrl >> 12) & 1)) {
+ write_mii(phyaddr, 0, sc->sp << 13);
+ write_mii(phyaddr, 0, 0x8000);
+ sc->gb = 0;
+ sc->sp = 0;
+ sc->fd = 0;
+ }
+ } else {
+ if (((sc->gb) && !(sc->gbit_mac)) || !((phyctrl >> 12) & 1)) {
+ write_mii(phyaddr, 0, sc->sp << 13);
+ sc->gb = 0;
+ sc->sp = 0;
+ sc->fd = 0;
+ }
+ }
+ while ((read_mii(phyaddr, 0)) & 0x8000) {}
+
+ regs->ctrl = 0;
+ regs->ctrl = GRETH_CTRL_RST; /* Reset ON */
+ regs->ctrl = 0;
+
+ /* Initialize rx/tx descriptor pointers */
+ sc->txdesc = (greth_rxtxdesc *) almalloc(1024);
+ sc->rxdesc = (greth_rxtxdesc *) almalloc(1024);
+ sc->tx_ptr = 0;
+ sc->tx_dptr = 0;
+ sc->tx_cnt = 0;
+ sc->rx_ptr = 0;
+ regs->txdesc = (uintptr_t) sc->txdesc;
+ regs->rxdesc = (uintptr_t) sc->rxdesc;
+
+ sc->rxmbuf = calloc(sc->rxbufs, sizeof(*sc->rxmbuf));
+ sc->txmbuf = calloc(sc->txbufs, sizeof(*sc->txmbuf));
+
+ for (i = 0; i < sc->txbufs; i++)
+ {
+ sc->txdesc[i].ctrl = 0;
+ if (!(sc->gbit_mac)) {
+ sc->txdesc[i].addr = malloc(GRETH_MAXBUF_LEN);
+ }
+#ifdef GRETH_DEBUG
+ /* printf("TXBUF: %08x\n", (int) sc->txdesc[i].addr); */
+#endif
+ }
+ for (i = 0; i < sc->rxbufs; i++)
+ {
+
+ MGETHDR (m, M_WAIT, MT_DATA);
+ MCLGET (m, M_WAIT);
+ if (sc->gbit_mac)
+ m->m_data += 2;
+ m->m_pkthdr.rcvif = &sc->arpcom.ac_if;
+ sc->rxmbuf[i] = m;
+ sc->rxdesc[i].addr = (uint32_t *) mtod(m, uint32_t *);
+ sc->rxdesc[i].ctrl = GRETH_RXD_ENABLE | GRETH_RXD_IRQ;
+#ifdef GRETH_DEBUG
+/* printf("RXBUF: %08x\n", (int) sc->rxdesc[i].addr); */
+#endif
+ }
+ sc->rxdesc[sc->rxbufs - 1].ctrl |= GRETH_RXD_WRAP;
+
+ /* set ethernet address. */
+ regs->mac_addr_msb =
+ sc->arpcom.ac_enaddr[0] << 8 | sc->arpcom.ac_enaddr[1];
+
+ uint32_t mac_addr_lsb;
+ mac_addr_lsb = sc->arpcom.ac_enaddr[2];
+ mac_addr_lsb <<= 8;
+ mac_addr_lsb |= sc->arpcom.ac_enaddr[3];
+ mac_addr_lsb <<= 8;
+ mac_addr_lsb |= sc->arpcom.ac_enaddr[4];
+ mac_addr_lsb <<= 8;
+ mac_addr_lsb |= sc->arpcom.ac_enaddr[5];
+ regs->mac_addr_lsb = mac_addr_lsb;
+
+ if ( sc->rxbufs < 10 ) {
+ sc->tx_int_gen = sc->tx_int_gen_cur = 1;
+ }else{
+ sc->tx_int_gen = sc->tx_int_gen_cur = sc->txbufs/2;
+ }
+ sc->next_tx_mbuf = NULL;
+
+ if ( !sc->gbit_mac )
+ sc->max_fragsize = 1;
+
+ /* clear all pending interrupts */
+ regs->status = 0xffffffff;
+
+ /* install interrupt handler */
+ rtems_interrupt_handler_install(sc->vector, "greth", RTEMS_INTERRUPT_SHARED,
+ greth_interrupt_handler, sc);
+
+ regs->ctrl |= GRETH_CTRL_RXEN | (sc->fd << 4) | GRETH_CTRL_RXIRQ | (sc->sp << 7) | (sc->gb << 8);
+
+ print_init_info(sc);
+}
+
+#ifdef CPU_U32_FIX
+
+/*
+ * Routine to align the received packet so that the ip header
+ * is on a 32-bit boundary. Necessary for cpu's that do not
+ * allow unaligned loads and stores and when the 32-bit DMA
+ * mode is used.
+ *
+ * Transfers are done on word basis to avoid possibly slow byte
+ * and half-word writes.
+ */
+
+void ipalign(struct mbuf *m)
+{
+ unsigned int *first, *last, data;
+ unsigned int tmp;
+
+ if ((((int) m->m_data) & 2) && (m->m_len)) {
+ last = (unsigned int *) ((((int) m->m_data) + m->m_len + 8) & ~3);
+ first = (unsigned int *) (((int) m->m_data) & ~3);
+ tmp = GRETH_MEM_LOAD(first);
+ tmp = tmp << 16;
+ first++;
+ do {
+ /* When snooping is not available the LDA instruction must be used
+ * to avoid the cache to return an illegal value.
+ * Load with forced cache miss
+ */
+ data = GRETH_MEM_LOAD(first);
+ *first = tmp | (data >> 16);
+ tmp = data << 16;
+ first++;
+ } while (first <= last);
+
+ m->m_data = (caddr_t)(((int) m->m_data) + 2);
+ }
+}
+#endif
+
+static void
+greth_Daemon (void *arg)
+{
+ struct ether_header *eh;
+ struct greth_softc *dp = (struct greth_softc *) &greth;
+ struct ifnet *ifp = &dp->arpcom.ac_if;
+ struct mbuf *m;
+ unsigned int len, len_status, bad;
+ rtems_event_set events;
+ rtems_interrupt_level level;
+ int first;
+#ifdef CPU_U32_FIX
+ unsigned int tmp;
+#endif
+
+ for (;;)
+ {
+ rtems_bsdnet_event_receive (INTERRUPT_EVENT | GRETH_TX_WAIT_EVENT,
+ RTEMS_WAIT | RTEMS_EVENT_ANY,
+ RTEMS_NO_TIMEOUT, &events);
+
+ if ( events & GRETH_TX_WAIT_EVENT ){
+ /* TX interrupt.
+ * We only end up here when all TX descriptors has been used,
+ * and
+ */
+ if ( dp->gbit_mac )
+ greth_process_tx_gbit(dp);
+ else
+ greth_process_tx(dp);
+
+ /* If we didn't get a RX interrupt we don't process it */
+ if ( (events & INTERRUPT_EVENT) == 0 )
+ continue;
+ }
+
+#ifdef GRETH_ETH_DEBUG
+ printf ("r\n");
+#endif
+ first=1;
+ /* Scan for Received packets */
+again:
+ while (!((len_status =
+ GRETH_MEM_LOAD(&dp->rxdesc[dp->rx_ptr].ctrl)) & GRETH_RXD_ENABLE))
+ {
+ bad = 0;
+ if (len_status & GRETH_RXD_TOOLONG)
+ {
+ dp->rxLengthError++;
+ bad = 1;
+ }
+ if (len_status & GRETH_RXD_DRIBBLE)
+ {
+ dp->rxNonOctet++;
+ bad = 1;
+ }
+ if (len_status & GRETH_RXD_CRCERR)
+ {
+ dp->rxBadCRC++;
+ bad = 1;
+ }
+ if (len_status & GRETH_RXD_OVERRUN)
+ {
+ dp->rxOverrun++;
+ bad = 1;
+ }
+ if (len_status & GRETH_RXD_LENERR)
+ {
+ dp->rxLengthError++;
+ bad = 1;
+ }
+ if (!bad)
+ {
+ /* pass on the packet in the receive buffer */
+ len = len_status & 0x7FF;
+ m = dp->rxmbuf[dp->rx_ptr];
+#ifdef GRETH_DEBUG
+ int i;
+ printf("RX: 0x%08x, Len: %d : ", (int) m->m_data, len);
+ for (i=0; i<len; i++)
+ printf("%x%x", (m->m_data[i] >> 4) & 0x0ff, m->m_data[i] & 0x0ff);
+ printf("\n");
+#endif
+ m->m_len = m->m_pkthdr.len =
+ len - sizeof (struct ether_header);
+
+ eh = mtod (m, struct ether_header *);
+
+ m->m_data += sizeof (struct ether_header);
+#ifdef CPU_U32_FIX
+ if(!dp->gbit_mac) {
+ /* OVERRIDE CACHED ETHERNET HEADER FOR NON-SNOOPING SYSTEMS */
+ tmp = GRETH_MEM_LOAD((uintptr_t)eh);
+ tmp = GRETH_MEM_LOAD(4+(uintptr_t)eh);
+ tmp = GRETH_MEM_LOAD(8+(uintptr_t)eh);
+ tmp = GRETH_MEM_LOAD(12+(uintptr_t)eh);
+ (void)tmp;
+ ipalign(m); /* Align packet on 32-bit boundary */
+ }
+#endif
+
+ ether_input (ifp, eh, m);
+ MGETHDR (m, M_WAIT, MT_DATA);
+ MCLGET (m, M_WAIT);
+ if (dp->gbit_mac)
+ m->m_data += 2;
+ dp->rxmbuf[dp->rx_ptr] = m;
+ m->m_pkthdr.rcvif = ifp;
+ dp->rxdesc[dp->rx_ptr].addr =
+ (uint32_t *) mtod (m, uint32_t *);
+ dp->rxPackets++;
+ }
+ if (dp->rx_ptr == dp->rxbufs - 1) {
+ dp->rxdesc[dp->rx_ptr].ctrl = GRETH_RXD_ENABLE | GRETH_RXD_IRQ | GRETH_RXD_WRAP;
+ } else {
+ dp->rxdesc[dp->rx_ptr].ctrl = GRETH_RXD_ENABLE | GRETH_RXD_IRQ;
+ }
+ rtems_interrupt_disable(level);
+ dp->regs->ctrl |= GRETH_CTRL_RXEN;
+ rtems_interrupt_enable(level);
+ dp->rx_ptr = (dp->rx_ptr + 1) % dp->rxbufs;
+ }
+
+ /* Always scan twice to avoid deadlock */
+ if ( first ){
+ first=0;
+ rtems_interrupt_disable(level);
+ dp->regs->ctrl |= GRETH_CTRL_RXIRQ;
+ rtems_interrupt_enable(level);
+ goto again;
+ }
+
+ }
+
+}
+
+static int inside = 0;
+static int
+sendpacket (struct ifnet *ifp, struct mbuf *m)
+{
+ struct greth_softc *dp = ifp->if_softc;
+ unsigned char *temp;
+ struct mbuf *n;
+ unsigned int len;
+ rtems_interrupt_level level;
+
+ /*printf("Send packet entered\n");*/
+ if (inside) printf ("error: sendpacket re-entered!!\n");
+ inside = 1;
+
+ /*
+ * Is there a free descriptor available?
+ */
+ if (GRETH_MEM_LOAD(&dp->txdesc[dp->tx_ptr].ctrl) & GRETH_TXD_ENABLE){
+ /* No. */
+ inside = 0;
+ return 1;
+ }
+
+ /* Remember head of chain */
+ n = m;
+
+ len = 0;
+ temp = (unsigned char *) GRETH_MEM_LOAD(&dp->txdesc[dp->tx_ptr].addr);
+#ifdef GRETH_DEBUG
+ printf("TXD: 0x%08x : BUF: 0x%08x\n", (int) m->m_data, (int) temp);
+#endif
+ for (;;)
+ {
+#ifdef GRETH_DEBUG
+ int i;
+ printf("MBUF: 0x%08x : ", (int) m->m_data);
+ for (i=0;i<m->m_len;i++)
+ printf("%x%x", (m->m_data[i] >> 4) & 0x0ff, m->m_data[i] & 0x0ff);
+ printf("\n");
+#endif
+ len += m->m_len;
+ if (len <= RBUF_SIZE)
+ memcpy ((void *) temp, (char *) m->m_data, m->m_len);
+ temp += m->m_len;
+ if ((m = m->m_next) == NULL)
+ break;
+ }
+
+ m_freem (n);
+
+ /* don't send long packets */
+
+ if (len <= GRETH_MAXBUF_LEN) {
+ if (dp->tx_ptr < dp->txbufs-1) {
+ dp->txdesc[dp->tx_ptr].ctrl = GRETH_TXD_ENABLE | len;
+ } else {
+ dp->txdesc[dp->tx_ptr].ctrl =
+ GRETH_TXD_WRAP | GRETH_TXD_ENABLE | len;
+ }
+ dp->tx_ptr = (dp->tx_ptr + 1) % dp->txbufs;
+ rtems_interrupt_disable(level);
+ dp->regs->ctrl = dp->regs->ctrl | GRETH_CTRL_TXEN;
+ rtems_interrupt_enable(level);
+
+ }
+ inside = 0;
+
+ return 0;
+}
+
+
+static int
+sendpacket_gbit (struct ifnet *ifp, struct mbuf *m)
+{
+ struct greth_softc *dp = ifp->if_softc;
+ unsigned int len;
+
+ unsigned int ctrl;
+ int frags;
+ struct mbuf *mtmp;
+ int int_en;
+ rtems_interrupt_level level;
+
+ if (inside) printf ("error: sendpacket re-entered!!\n");
+ inside = 1;
+
+ len = 0;
+#ifdef GRETH_DEBUG
+ printf("TXD: 0x%08x\n", (int) m->m_data);
+#endif
+ /* Get number of fragments too see if we have enough
+ * resources.
+ */
+ frags=1;
+ mtmp=m;
+ while(mtmp->m_next){
+ frags++;
+ mtmp = mtmp->m_next;
+ }
+
+ if ( frags > dp->max_fragsize )
+ dp->max_fragsize = frags;
+
+ if ( frags > dp->txbufs ){
+ inside = 0;
+ printf("GRETH: MBUF-chain cannot be sent. Increase descriptor count.\n");
+ return -1;
+ }
+
+ if ( frags > (dp->txbufs-dp->tx_cnt) ){
+ inside = 0;
+ /* Return number of fragments */
+ return frags;
+ }
+
+
+ /* Enable interrupt from descriptor every tx_int_gen
+ * descriptor. Typically every 16 descriptor. This
+ * is only to reduce the number of interrupts during
+ * heavy load.
+ */
+ dp->tx_int_gen_cur-=frags;
+ if ( dp->tx_int_gen_cur <= 0 ){
+ dp->tx_int_gen_cur = dp->tx_int_gen;
+ int_en = GRETH_TXD_IRQ;
+ }else{
+ int_en = 0;
+ }
+
+ /* At this stage we know that enough descriptors are available */
+ for (;;)
+ {
+
+#ifdef GRETH_DEBUG
+ int i;
+ printf("MBUF: 0x%08x, Len: %d : ", (int) m->m_data, m->m_len);
+ for (i=0; i<m->m_len; i++)
+ printf("%x%x", (m->m_data[i] >> 4) & 0x0ff, m->m_data[i] & 0x0ff);
+ printf("\n");
+#endif
+ len += m->m_len;
+ dp->txdesc[dp->tx_ptr].addr = (uint32_t *)m->m_data;
+
+ /* Wrap around? */
+ if (dp->tx_ptr < dp->txbufs-1) {
+ ctrl = GRETH_TXD_ENABLE;
+ }else{
+ ctrl = GRETH_TXD_ENABLE | GRETH_TXD_WRAP;
+ }
+
+ /* Enable Descriptor */
+ if ((m->m_next) == NULL) {
+ dp->txdesc[dp->tx_ptr].ctrl = ctrl | int_en | m->m_len;
+ break;
+ }else{
+ dp->txdesc[dp->tx_ptr].ctrl = GRETH_TXD_MORE | ctrl | int_en | m->m_len;
+ }
+
+ /* Next */
+ dp->txmbuf[dp->tx_ptr] = m;
+ dp->tx_ptr = (dp->tx_ptr + 1) % dp->txbufs;
+ dp->tx_cnt++;
+ m = m->m_next;
+ }
+ dp->txmbuf[dp->tx_ptr] = m;
+ dp->tx_ptr = (dp->tx_ptr + 1) % dp->txbufs;
+ dp->tx_cnt++;
+
+ /* Tell Hardware about newly enabled descriptor */
+ rtems_interrupt_disable(level);
+ dp->regs->ctrl = dp->regs->ctrl | GRETH_CTRL_TXEN;
+ rtems_interrupt_enable(level);
+
+ inside = 0;
+
+ return 0;
+}
+
+int greth_process_tx_gbit(struct greth_softc *sc)
+{
+ struct ifnet *ifp = &sc->arpcom.ac_if;
+ struct mbuf *m;
+ rtems_interrupt_level level;
+ int first=1;
+
+ /*
+ * Send packets till queue is empty
+ */
+ for (;;){
+ /* Reap Sent packets */
+ while((sc->tx_cnt > 0) && !(GRETH_MEM_LOAD(&sc->txdesc[sc->tx_dptr].ctrl) & GRETH_TXD_ENABLE)) {
+ m_free(sc->txmbuf[sc->tx_dptr]);
+ sc->tx_dptr = (sc->tx_dptr + 1) % sc->txbufs;
+ sc->tx_cnt--;
+ }
+
+ if ( sc->next_tx_mbuf ){
+ /* Get packet we tried but faild to transmit last time */
+ m = sc->next_tx_mbuf;
+ sc->next_tx_mbuf = NULL; /* Mark packet taken */
+ }else{
+ /*
+ * Get the next mbuf chain to transmit from Stack.
+ */
+ IF_DEQUEUE (&ifp->if_snd, m);
+ if (!m){
+ /* Hardware has sent all schedule packets, this
+ * makes the stack enter at greth_start next time
+ * a packet is to be sent.
+ */
+ ifp->if_flags &= ~IFF_OACTIVE;
+ break;
+ }
+ }
+
+ /* Are there free descriptors available? */
+ /* Try to send packet, if it a negative number is returned. */
+ if ( (sc->tx_cnt >= sc->txbufs) || sendpacket_gbit(ifp, m) ){
+ /* Not enough resources */
+
+ /* Since we have taken the mbuf out of the "send chain"
+ * we must remember to use that next time we come back.
+ * or else we have dropped a packet.
+ */
+ sc->next_tx_mbuf = m;
+
+ /* Not enough resources, enable interrupt for transmissions
+ * this way we will be informed when more TX-descriptors are
+ * available.
+ */
+ if ( first ){
+ first = 0;
+ rtems_interrupt_disable(level);
+ ifp->if_flags |= IFF_OACTIVE;
+ sc->regs->ctrl |= GRETH_CTRL_TXIRQ;
+ rtems_interrupt_enable(level);
+
+ /* We must check again to be sure that we didn't
+ * miss an interrupt (if a packet was sent just before
+ * enabling interrupts)
+ */
+ continue;
+ }
+
+ return -1;
+ }else{
+ /* Sent Ok, proceed to process more packets if available */
+ }
+ }
+ return 0;
+}
+
+int greth_process_tx(struct greth_softc *sc)
+{
+ struct ifnet *ifp = &sc->arpcom.ac_if;
+ struct mbuf *m;
+ rtems_interrupt_level level;
+ int first=1;
+
+ /*
+ * Send packets till queue is empty
+ */
+ for (;;){
+ if ( sc->next_tx_mbuf ){
+ /* Get packet we tried but failed to transmit last time */
+ m = sc->next_tx_mbuf;
+ sc->next_tx_mbuf = NULL; /* Mark packet taken */
+ }else{
+ /*
+ * Get the next mbuf chain to transmit from Stack.
+ */
+ IF_DEQUEUE (&ifp->if_snd, m);
+ if (!m){
+ /* Hardware has sent all schedule packets, this
+ * makes the stack enter at greth_start next time
+ * a packet is to be sent.
+ */
+ ifp->if_flags &= ~IFF_OACTIVE;
+ break;
+ }
+ }
+
+ /* Try to send packet, failed if it a non-zero number is returned. */
+ if ( sendpacket(ifp, m) ){
+ /* Not enough resources */
+
+ /* Since we have taken the mbuf out of the "send chain"
+ * we must remember to use that next time we come back.
+ * or else we have dropped a packet.
+ */
+ sc->next_tx_mbuf = m;
+
+ /* Not enough resources, enable interrupt for transmissions
+ * this way we will be informed when more TX-descriptors are
+ * available.
+ */
+ if ( first ){
+ first = 0;
+ rtems_interrupt_disable(level);
+ ifp->if_flags |= IFF_OACTIVE;
+ sc->regs->ctrl |= GRETH_CTRL_TXIRQ;
+ rtems_interrupt_enable(level);
+
+ /* We must check again to be sure that we didn't
+ * miss an interrupt (if a packet was sent just before
+ * enabling interrupts)
+ */
+ continue;
+ }
+
+ return -1;
+ }else{
+ /* Sent Ok, proceed to process more packets if available */
+ }
+ }
+ return 0;
+}
+
+static void
+greth_start (struct ifnet *ifp)
+{
+ struct greth_softc *sc = ifp->if_softc;
+
+ if ( ifp->if_flags & IFF_OACTIVE )
+ return;
+
+ if ( sc->gbit_mac ){
+ /* No use trying to handle this if we are waiting on GRETH
+ * to send the previously scheduled packets.
+ */
+
+ greth_process_tx_gbit(sc);
+ }else{
+ greth_process_tx(sc);
+ }
+}
+
+/*
+ * Initialize and start the device
+ */
+static void
+greth_init (void *arg)
+{
+ struct greth_softc *sc = arg;
+ struct ifnet *ifp = &sc->arpcom.ac_if;
+
+ if (sc->daemonTid == 0) {
+
+ /*
+ * Start driver tasks
+ */
+ sc->daemonTid = rtems_bsdnet_newproc ("DCrxtx", 4096,
+ greth_Daemon, sc);
+
+ /*
+ * Set up GRETH hardware
+ */
+ greth_initialize_hardware (sc);
+
+ }
+
+ /*
+ * Tell the world that we're running.
+ */
+ ifp->if_flags |= IFF_RUNNING;
+}
+
+/*
+ * Stop the device
+ */
+static void
+greth_stop (struct greth_softc *sc)
+{
+ struct ifnet *ifp = &sc->arpcom.ac_if;
+
+ ifp->if_flags &= ~IFF_RUNNING;
+
+ sc->regs->ctrl = 0; /* RX/TX OFF */
+ sc->regs->ctrl = GRETH_CTRL_RST; /* Reset ON */
+ sc->regs->ctrl = 0; /* Reset OFF */
+
+ sc->next_tx_mbuf = NULL;
+}
+
+
+/*
+ * Show interface statistics
+ */
+static void
+greth_stats (struct greth_softc *sc)
+{
+ printf (" Rx Interrupts:%-8lu", sc->rxInterrupts);
+ printf (" Rx Packets:%-8lu", sc->rxPackets);
+ printf (" Length:%-8lu", sc->rxLengthError);
+ printf (" Non-octet:%-8lu\n", sc->rxNonOctet);
+ printf (" Bad CRC:%-8lu", sc->rxBadCRC);
+ printf (" Overrun:%-8lu", sc->rxOverrun);
+ printf (" Tx Interrupts:%-8lu", sc->txInterrupts);
+ printf (" Maximal Frags:%-8d", sc->max_fragsize);
+ printf (" GBIT MAC:%-8d", sc->gbit_mac);
+}
+
+/*
+ * Driver ioctl handler
+ */
+static int
+greth_ioctl (struct ifnet *ifp, ioctl_command_t command, caddr_t data)
+{
+ struct greth_softc *sc = ifp->if_softc;
+ int error = 0;
+
+ switch (command)
+ {
+ case SIOCGIFADDR:
+ case SIOCSIFADDR:
+ ether_ioctl (ifp, command, data);
+ break;
+
+ case SIOCSIFFLAGS:
+ switch (ifp->if_flags & (IFF_UP | IFF_RUNNING))
+ {
+ case IFF_RUNNING:
+ greth_stop (sc);
+ break;
+
+ case IFF_UP:
+ greth_init (sc);
+ break;
+
+ case IFF_UP | IFF_RUNNING:
+ greth_stop (sc);
+ greth_init (sc);
+ break;
+ default:
+ break;
+ }
+ break;
+
+ case SIO_RTEMS_SHOW_STATS:
+ greth_stats (sc);
+ break;
+
+ /*
+ * FIXME: All sorts of multicast commands need to be added here!
+ */
+ default:
+ error = EINVAL;
+ break;
+ }
+
+ return error;
+}
+
+/*
+ * Attach an GRETH driver to the system
+ */
+int
+rtems_greth_driver_attach (struct rtems_bsdnet_ifconfig *config,
+ greth_configuration_t *chip)
+{
+ struct greth_softc *sc;
+ struct ifnet *ifp;
+ int mtu;
+ int unitNumber;
+ char *unitName;
+
+ /* parse driver name */
+ if ((unitNumber = rtems_bsdnet_parse_driver_name (config, &unitName)) < 0)
+ return 0;
+
+ sc = &greth;
+ ifp = &sc->arpcom.ac_if;
+ memset (sc, 0, sizeof (*sc));
+
+ if (config->hardware_address)
+ {
+ memcpy (sc->arpcom.ac_enaddr, config->hardware_address,
+ ETHER_ADDR_LEN);
+ }
+ else
+ {
+ memset (sc->arpcom.ac_enaddr, 0x08, ETHER_ADDR_LEN);
+ }
+
+ if (config->mtu)
+ mtu = config->mtu;
+ else
+ mtu = ETHERMTU;
+
+ sc->acceptBroadcast = !config->ignore_broadcast;
+ sc->regs = chip->base_address;
+ sc->vector = chip->vector;
+ sc->txbufs = chip->txd_count;
+ sc->rxbufs = chip->rxd_count;
+
+ /*
+ * Set up network interface values
+ */
+ ifp->if_softc = sc;
+ ifp->if_unit = unitNumber;
+ ifp->if_name = unitName;
+ ifp->if_mtu = mtu;
+ ifp->if_init = greth_init;
+ ifp->if_ioctl = greth_ioctl;
+ ifp->if_start = greth_start;
+ ifp->if_output = ether_output;
+ ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX;
+ if (ifp->if_snd.ifq_maxlen == 0)
+ ifp->if_snd.ifq_maxlen = ifqmaxlen;
+
+ /*
+ * Attach the interface
+ */
+ if_attach (ifp);
+ ether_ifattach (ifp);
+
+#ifdef GRETH_DEBUG
+ printf ("GRETH : driver has been attached\n");
+#endif
+ return 1;
+};
+
+#endif
diff --git a/bsps/shared/net/i82586.c b/bsps/shared/net/i82586.c
new file mode 100644
index 0000000000..c79af66e0d
--- /dev/null
+++ b/bsps/shared/net/i82586.c
@@ -0,0 +1,2198 @@
+/* $NetBSD: i82586.c,v 1.38 2001/07/07 05:35:39 thorpej Exp $ */
+
+/*-
+ * Copyright (c) 1998 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Paul Kranenburg and Charles M. Hannum.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * 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.
+ */
+
+/*-
+ * Copyright (c) 1997 Paul Kranenburg.
+ * Copyright (c) 1992, 1993, University of Vermont and State
+ * Agricultural College.
+ * Copyright (c) 1992, 1993, Garrett A. Wollman.
+ *
+ * Portions:
+ * Copyright (c) 1994, 1995, Rafal K. Boni
+ * Copyright (c) 1990, 1991, William F. Jolitz
+ * Copyright (c) 1990, The Regents of the University of California
+ *
+ * RTEMS:
+ * Copyright (c) 2001, Chris Johns, Cybertec Pty Ltd,
+ * http://www.cybertec.com.au/.
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of Vermont
+ * and State Agricultural College and Garrett A. Wollman, by William F.
+ * Jolitz, and by the University of California, Berkeley, Lawrence
+ * Berkeley Laboratory, and its contributors.
+ * 4. Neither the names of the Universities nor the names of the authors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 UNIVERSITY OR AUTHORS 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.
+ */
+
+/*
+ * Intel 82586 Ethernet chip
+ * Register, bit, and structure definitions.
+ *
+ * Original StarLAN driver written by Garrett Wollman with reference to the
+ * Clarkson Packet Driver code for this chip written by Russ Nelson and others.
+ *
+ * BPF support code taken from hpdev/if_le.c, supplied with tcpdump.
+ *
+ * 3C507 support is loosely based on code donated to NetBSD by Rafal Boni.
+ *
+ * Majorly cleaned up and 3C507 code merged by Charles Hannum.
+ *
+ * Converted to SUN ie driver by Charles D. Cranor,
+ * October 1994, January 1995.
+ * This sun version based on i386 version 1.30.
+ */
+
+/*
+ * The i82586 is a very painful chip, found in sun3's, sun-4/100's
+ * sun-4/200's, and VME based suns. The byte order is all wrong for a
+ * SUN, making life difficult. Programming this chip is mostly the same,
+ * but certain details differ from system to system. This driver is
+ * written so that different "ie" interfaces can be controled by the same
+ * driver.
+ */
+
+/*
+Mode of operation:
+
+ We run the 82586 in a standard Ethernet mode. We keep NFRAMES
+ received frame descriptors around for the receiver to use, and
+ NRXBUF associated receive buffer descriptors, both in a circular
+ list. Whenever a frame is received, we rotate both lists as
+ necessary. (The 586 treats both lists as a simple queue.) We also
+ keep a transmit command around so that packets can be sent off
+ quickly.
+
+ We configure the adapter in AL-LOC = 1 mode, which means that the
+ Ethernet/802.3 MAC header is placed at the beginning of the receive
+ buffer rather than being split off into various fields in the RFD.
+ This also means that we must include this header in the transmit
+ buffer as well.
+
+ By convention, all transmit commands, and only transmit commands,
+ shall have the I (IE_CMD_INTR) bit set in the command. This way,
+ when an interrupt arrives at i82586_intr(), it is immediately possible
+ to tell what precisely caused it. ANY OTHER command-sending
+ routines should run at splnet(), and should post an acknowledgement
+ to every interrupt they generate.
+
+ To save the expense of shipping a command to 82586 every time we
+ want to send a frame, we use a linked list of commands consisting
+ of alternate XMIT and NOP commands. The links of these elements
+ are manipulated (in iexmit()) such that the NOP command loops back
+ to itself whenever the following XMIT command is not yet ready to
+ go. Whenever an XMIT is ready, the preceding NOP link is pointed
+ at it, while its own link field points to the following NOP command.
+ Thus, a single transmit command sets off an interlocked traversal
+ of the xmit command chain, with the host processor in control of
+ the synchronization.
+*/
+
+#define __INSIDE_RTEMS_BSD_TCPIP_STACK__
+
+#include <rtems.h>
+#include <rtems/error.h>
+#include <rtems/rtems_bsdnet.h>
+
+#include <ctype.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <inttypes.h>
+
+#include <sys/param.h>
+#include <sys/mbuf.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+
+#include <net/ethernet.h>
+#include <net/if.h>
+#include <net/if_types.h>
+#include <net/if_dl.h>
+
+#include <netinet/in.h>
+#include <netinet/if_ether.h>
+
+#include "i82586reg.h"
+#include <libchip/i82586var.h>
+
+#if defined(ALIGNBYTES) && defined(ALIGN)
+/* FIXME: Redefine because some versions of
+ * RTEMS newlib and the BSDs ship a broken ALIGN */
+#undef ALIGN
+#define ALIGN(p) (((uintptr_t)(p) + ALIGNBYTES) & ~ALIGNBYTES)
+#else
+#define ALIGN(p) (p)
+#endif
+
+/*
+ * A global way to change all async cmd requests at once. For RTEMS and running
+ * as tasks I wanted to see if the tx race condition is effected by this.
+ */
+#define ASYNC_OPTION (1)
+
+void i82586_reset (struct ie_softc *, int);
+void i82586_watchdog (struct ifnet *);
+void i82586_init (void *);
+int i82586_ioctl (struct ifnet *, ioctl_command_t cmd, caddr_t data);
+void i82586_start (struct ifnet *);
+
+void i82586_stop (struct ifnet *, int);
+int i82586_rint (struct ie_softc *, int);
+int i82586_tint (struct ie_softc *, int);
+
+int i82586_mediachange (struct ifnet *);
+void i82586_mediastatus (struct ifnet *, struct ifmediareq *);
+
+static void i82586_tx_task(void *arg);
+static void i82586_start_tx(struct ie_softc *sc);
+
+static int ie_readframe (struct ie_softc *, int);
+static struct mbuf *ieget (struct ie_softc *, int, int);
+static int i82586_get_rbd_list (struct ie_softc *, u_int16_t*,
+ u_int16_t*, int *);
+static void i82586_release_rbd_list (struct ie_softc *,
+ u_int16_t, u_int16_t);
+static int i82586_drop_frames (struct ie_softc *);
+static int i82586_chk_rx_ring (struct ie_softc *);
+
+static __inline__ void ie_ack (struct ie_softc *, u_int);
+static __inline__ void iexmit (struct ie_softc *);
+static void i82586_start_transceiver (struct ie_softc *);
+
+static void i82586_count_errors (struct ie_softc *);
+static void i82586_rx_errors (struct ie_softc *, int, int);
+static void i82586_setup_bufs (struct ie_softc *);
+static void setup_simple_command (struct ie_softc *, int, int);
+static int ie_cfg_setup (struct ie_softc *, int, int, int);
+static int ie_ia_setup (struct ie_softc *, int);
+static void ie_run_tdr (struct ie_softc *, int);
+static int ie_mc_setup (struct ie_softc *, int);
+static void ie_mc_reset (struct ie_softc *);
+static int i82586_start_cmd (struct ie_softc *, int, int, int, int);
+static int i82586_cmd_wait (struct ie_softc *);
+
+#if I82586_DEBUG
+static void print_softie(struct ie_softc *sc);
+static void print_rbd (struct ie_softc *, int);
+#endif
+
+#define min(l,r) ((l) < (r) ? (l) : (r))
+#define max(l,r) ((l) > (r) ? (l) : (r))
+
+#define delay(p) rtems_task_wake_after (RTEMS_MICROSECONDS_TO_TICKS (p))
+
+#define i82586_WAKE_EVENT RTEMS_EVENT_1
+#define i82586_TX_EVENT RTEMS_EVENT_2
+
+static char *
+bitmask_snprintf(unsigned long value, const char *format, char *buf, int blen)
+{
+ char *b = buf;
+ int bit = 31;
+
+ while (bit-- > *format)
+ value <<= 1;
+
+ format++;
+
+ while (*format)
+ {
+ if (value & 0x80000000)
+ while (isalnum((unsigned char)*format))
+ *b++ = *format;
+ else
+ *b++ = '0';
+
+ *b++ = ',';
+
+ while (bit-- > *format)
+ value <<= 1;
+
+ format++;
+ }
+
+ *b = '\0';
+ return buf;
+}
+
+/*
+ * Front-ends call this function to attach to the MI driver.
+ *
+ * The front-end has responsibility for managing the ICP and ISCP
+ * structures. Both of these are opaque to us. Also, the front-end
+ * chooses a location for the SCB which is expected to be addressable
+ * (through `sc->scb') as an offset against the shared-memory bus handle.
+ *
+ * The following MD interface function must be setup by the front-end
+ * before calling here:
+ *
+ * hwreset - board dependent reset
+ * hwinit - board dependent initialization
+ * chan_attn - channel attention
+ * intrhook - board dependent interrupt processing
+ * memcopyin - shared memory copy: board to KVA
+ * memcopyout - shared memory copy: KVA to board
+ * ie_bus_read16 - read a sixteen-bit i82586 pointer
+ * ie_bus_write16 - write a sixteen-bit i82586 pointer
+ * ie_bus_write24 - write a twenty-four-bit i82586 pointer
+ *
+ */
+int
+i82586_attach(struct rtems_bsdnet_ifconfig *config, int attaching)
+{
+ struct ie_softc *sc;
+ struct ifnet *ifp;
+ char *name;
+ int unit;
+ int mtu;
+
+ /*
+ * Parse driver name
+ */
+
+ if ((unit = rtems_bsdnet_parse_driver_name (config, &name)) < 0)
+ return 0;
+
+ sc = config->drv_ctrl;
+ ifp = &sc->arpcom.ac_if;
+
+#if I82586_DEBUG
+ sc->sc_debug = 0; /*IED_TINT | IED_XMIT; */
+#endif
+
+ if (attaching)
+ {
+ if (ifp->if_softc)
+ {
+ printf ("Driver `%s' already in use.\n", config->name);
+ return 0;
+ }
+
+ /*
+ * Process options
+ */
+
+ memcpy (sc->arpcom.ac_enaddr, config->hardware_address, ETHER_ADDR_LEN);
+
+ if (config->mtu)
+ mtu = config->mtu;
+ else
+ mtu = ETHERMTU;
+
+ ifp->if_softc = sc;
+ ifp->if_unit = unit;
+ ifp->if_name = name;
+ ifp->if_mtu = mtu;
+ ifp->if_init = i82586_init;
+ ifp->if_ioctl = i82586_ioctl;
+ ifp->if_start = i82586_start;
+ ifp->if_output = ether_output;
+ ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
+
+ if (ifp->if_snd.ifq_maxlen == 0)
+ ifp->if_snd.ifq_maxlen = ifqmaxlen;
+
+ /* Attach the interface. */
+ if_attach(ifp);
+ ether_ifattach(ifp);
+ return 1;
+ }
+ return 0;
+}
+
+
+/*
+ * Interrupt Acknowledge. Mask in native byte-order.
+ */
+static __inline__ void
+ie_ack(struct ie_softc *sc, u_int mask)
+{
+ u_int status;
+
+ IE_BUS_BARRIER(sc, 0, 0, BUS_SPACE_BARRIER_READ);
+ status = sc->ie_bus_read16(sc, IE_SCB_STATUS(sc->scb));
+ i82586_start_cmd(sc, status & mask, 0, 0, 0);
+ if (sc->intrhook)
+ sc->intrhook(sc, INTR_ACK);
+}
+
+
+/*
+ * Read data off the interface, and turn it into an mbuf chain.
+ *
+ * This code is DRAMATICALLY different from the previous version; this
+ * version tries to allocate the entire mbuf chain up front, given the
+ * length of the data available. This enables us to allocate mbuf
+ * clusters in many situations where before we would have had a long
+ * chain of partially-full mbufs. This should help to speed up the
+ * operation considerably. (Provided that it works, of course.)
+ */
+static __inline struct mbuf *
+ieget(struct ie_softc *sc, int head, int totlen)
+{
+ struct mbuf *m, *m0, *newm;
+ int len, resid;
+ int thisrboff, thismboff;
+ struct ether_header eh;
+
+ /*
+ * Snarf the Ethernet header.
+ */
+ (sc->memcopyin)(sc, &eh, IE_RBUF_ADDR(sc, head),
+ sizeof(struct ether_header));
+
+ resid = totlen;
+
+ MGETHDR(m0, M_DONTWAIT, MT_DATA);
+ if (m0 == 0)
+ return (0);
+ m0->m_pkthdr.rcvif = &sc->arpcom.ac_if;
+ m0->m_pkthdr.len = totlen;
+ len = MHLEN;
+ m = m0;
+
+ /*
+ * This loop goes through and allocates mbufs for all the data we will
+ * be copying in. It does not actually do the copying yet.
+ */
+ while (totlen > 0) {
+ if (totlen >= MINCLSIZE) {
+ MCLGET(m, M_DONTWAIT);
+ if ((m->m_flags & M_EXT) == 0)
+ goto bad;
+ len = MCLBYTES;
+ }
+
+ if (m == m0) {
+ caddr_t newdata = (caddr_t)
+ ALIGN(m->m_data + sizeof(struct ether_header)) -
+ sizeof(struct ether_header);
+ len -= newdata - m->m_data;
+ m->m_data = newdata;
+ }
+
+ m->m_len = len = min(totlen, len);
+
+ totlen -= len;
+ if (totlen > 0) {
+ MGET(newm, M_DONTWAIT, MT_DATA);
+ if (newm == 0)
+ goto bad;
+ len = MLEN;
+ m = m->m_next = newm;
+ }
+ }
+
+ m = m0;
+ thismboff = 0;
+
+ /*
+ * Copy the Ethernet header into the mbuf chain.
+ */
+ memcpy(mtod(m, caddr_t), &eh, sizeof(struct ether_header));
+ thismboff = sizeof(struct ether_header);
+ thisrboff = sizeof(struct ether_header);
+ resid -= sizeof(struct ether_header);
+
+ /*
+ * Now we take the mbuf chain (hopefully only one mbuf most of the
+ * time) and stuff the data into it. There are no possible failures
+ * at or after this point.
+ */
+ while (resid > 0) {
+ int thisrblen = IE_RBUF_SIZE - thisrboff,
+ thismblen = m->m_len - thismboff;
+ len = min(thisrblen, thismblen);
+
+ (sc->memcopyin)(sc, mtod(m, caddr_t) + thismboff,
+ IE_RBUF_ADDR(sc,head) + thisrboff,
+ (u_int)len);
+ resid -= len;
+
+ if (len == thismblen) {
+ m = m->m_next;
+ thismboff = 0;
+ } else
+ thismboff += len;
+
+ if (len == thisrblen) {
+ if (++head == sc->nrxbuf)
+ head = 0;
+ thisrboff = 0;
+ } else
+ thisrboff += len;
+ }
+
+ /*
+ * Unless something changed strangely while we were doing the copy,
+ * we have now copied everything in from the shared memory.
+ * This means that we are done.
+ */
+ return (m0);
+
+ bad:
+ m_freem(m0);
+ return (0);
+}
+
+/*
+ * Setup all necessary artifacts for an XMIT command, and then pass the XMIT
+ * command to the chip to be executed.
+ */
+static __inline__ void
+iexmit(struct ie_softc *sc)
+{
+ int off;
+ int cur, prev;
+
+ cur = sc->xctail;
+
+#if I82586_DEBUG
+ I82586_TRACE(sc, I82586_TX_EMIT, cur);
+#endif
+
+#if I82586_DEBUG
+ if (sc->sc_debug & IED_XMIT)
+ printf("%s: xmit buffer %d\n", sc->arpcom.ac_if.if_name, cur);
+#endif
+
+ /*
+ * Setup the transmit command.
+ */
+ sc->ie_bus_write16(sc, IE_CMD_XMIT_DESC(sc->xmit_cmds, cur),
+ IE_XBD_ADDR(sc->xbds, cur));
+
+ sc->ie_bus_write16(sc, IE_CMD_XMIT_STATUS(sc->xmit_cmds, cur), 0);
+
+ if (sc->do_xmitnopchain) {
+ /*
+ * Gate this XMIT command to the following NOP
+ */
+ sc->ie_bus_write16(sc, IE_CMD_XMIT_LINK(sc->xmit_cmds, cur),
+ IE_CMD_NOP_ADDR(sc->nop_cmds, cur));
+ sc->ie_bus_write16(sc, IE_CMD_XMIT_CMD(sc->xmit_cmds, cur),
+ IE_CMD_XMIT | IE_CMD_INTR);
+
+ /*
+ * Loopback at following NOP
+ */
+ sc->ie_bus_write16(sc, IE_CMD_NOP_STATUS(sc->nop_cmds, cur), 0);
+ sc->ie_bus_write16(sc, IE_CMD_NOP_LINK(sc->nop_cmds, cur),
+ IE_CMD_NOP_ADDR(sc->nop_cmds, cur));
+
+ /*
+ * Gate preceding NOP to this XMIT command
+ */
+ prev = (cur + NTXBUF - 1) % NTXBUF;
+ sc->ie_bus_write16(sc, IE_CMD_NOP_STATUS(sc->nop_cmds, prev), 0);
+ sc->ie_bus_write16(sc, IE_CMD_NOP_LINK(sc->nop_cmds, prev),
+ IE_CMD_XMIT_ADDR(sc->xmit_cmds, cur));
+
+ off = IE_SCB_STATUS(sc->scb);
+ IE_BUS_BARRIER(sc, off, 2, BUS_SPACE_BARRIER_READ);
+ if ((sc->ie_bus_read16(sc, off) & IE_CUS_ACTIVE) == 0) {
+ printf("iexmit: CU not active\n");
+ i82586_start_transceiver(sc);
+ }
+ } else {
+ sc->ie_bus_write16(sc, IE_CMD_XMIT_LINK(sc->xmit_cmds,cur),
+ 0xffff);
+
+ sc->ie_bus_write16(sc, IE_CMD_XMIT_CMD(sc->xmit_cmds, cur),
+ IE_CMD_XMIT | IE_CMD_INTR | IE_CMD_LAST);
+
+ off = IE_SCB_CMDLST(sc->scb);
+ sc->ie_bus_write16(sc, off, IE_CMD_XMIT_ADDR(sc->xmit_cmds, cur));
+ IE_BUS_BARRIER(sc, off, 2, BUS_SPACE_BARRIER_READ);
+
+ if (i82586_start_cmd(sc, IE_CUC_START, 0, 0, ASYNC_OPTION))
+ printf("%s: iexmit: start xmit command timed out\n",
+ sc->arpcom.ac_if.if_name);
+ }
+
+ sc->arpcom.ac_if.if_timer = 5;
+}
+
+
+/*
+ * Device timeout/watchdog routine.
+ * Entered if the device neglects to generate an interrupt after a
+ * transmit has been started on it.
+ */
+void
+i82586_watchdog(struct ifnet *ifp)
+{
+ struct ie_softc *sc = ifp->if_softc;
+ printf("%s: device timeout\n", ifp->if_name);
+ ++ifp->if_oerrors;
+ i82586_reset(sc, 1);
+}
+
+static int
+i82586_cmd_wait(struct ie_softc *sc)
+{
+ /* spin on i82586 command acknowledge; wait at most 0.9 (!) seconds */
+ int i, off;
+ u_int16_t cmd;
+
+ for (i = 0; i < 900000; i++) {
+ /* Read the command word */
+ off = IE_SCB_CMD(sc->scb);
+
+ IE_BUS_BARRIER(sc, off, 2, BUS_SPACE_BARRIER_READ);
+ if ((cmd = sc->ie_bus_read16(sc, off)) == 0)
+ return (0);
+ delay(1);
+ }
+
+ off = IE_SCB_STATUS(sc->scb);
+ printf("i82586_cmd_wait: timo(%ssync): scb status: 0x%x, cmd: 0x%x\n",
+ sc->async_cmd_inprogress?"a":"",
+ sc->ie_bus_read16(sc, off), cmd);
+
+ return (1); /* Timeout */
+}
+
+/*
+ * Send a command to the controller and wait for it to either complete
+ * or be accepted, depending on the command. If the command pointer
+ * is null, then pretend that the command is not an action command.
+ * If the command pointer is not null, and the command is an action
+ * command, wait for one of the MASK bits to turn on in the command's
+ * status field.
+ * If ASYNC is set, we just call the chip's attention and return.
+ * We may have to wait for the command's acceptance later though.
+ */
+static int
+i82586_start_cmd(struct ie_softc *sc, int cmd, int iecmdbuf, int mask, int async)
+{
+ int i;
+ int off;
+
+ if (sc->async_cmd_inprogress != 0) {
+ /*
+ * If previous command was issued asynchronously, wait
+ * for it now.
+ */
+ if (i82586_cmd_wait(sc) != 0)
+ return (1);
+ sc->async_cmd_inprogress = 0;
+ }
+
+ off = IE_SCB_CMD(sc->scb);
+ sc->ie_bus_write16(sc, off, cmd);
+ IE_BUS_BARRIER(sc, off, 2, BUS_SPACE_BARRIER_WRITE);
+ (sc->chan_attn)(sc, CARD_RESET);
+
+ if (async != 0) {
+ sc->async_cmd_inprogress = 1;
+ return (0);
+ }
+
+ if (IE_ACTION_COMMAND(cmd) && iecmdbuf) {
+ int status;
+ /*
+ * Now spin-lock waiting for status. This is not a very nice
+ * thing to do, and can kill performance pretty well...
+ * According to the packet driver, the minimum timeout
+ * should be .369 seconds.
+ */
+ for (i = 0; i < 369000; i++) {
+ /* Read the command status */
+ off = IE_CMD_COMMON_STATUS(iecmdbuf);
+ IE_BUS_BARRIER(sc, off, 2, BUS_SPACE_BARRIER_READ);
+ status = sc->ie_bus_read16(sc, off);
+ if (status & mask)
+ return (0);
+ delay(1);
+ }
+
+ } else {
+ /*
+ * Otherwise, just wait for the command to be accepted.
+ */
+ return (i82586_cmd_wait(sc));
+ }
+
+ /* Timeout */
+ return (1);
+}
+
+
+/*
+ * Transfer accumulated chip error counters to IF.
+ */
+static __inline void
+i82586_count_errors(struct ie_softc *sc)
+{
+ int scb = sc->scb;
+
+ sc->arpcom.ac_if.if_ierrors +=
+ sc->ie_bus_read16(sc, IE_SCB_ERRCRC(scb)) +
+ sc->ie_bus_read16(sc, IE_SCB_ERRALN(scb)) +
+ sc->ie_bus_read16(sc, IE_SCB_ERRRES(scb)) +
+ sc->ie_bus_read16(sc, IE_SCB_ERROVR(scb));
+
+ /* Clear error counters */
+ sc->ie_bus_write16(sc, IE_SCB_ERRCRC(scb), 0);
+ sc->ie_bus_write16(sc, IE_SCB_ERRALN(scb), 0);
+ sc->ie_bus_write16(sc, IE_SCB_ERRRES(scb), 0);
+ sc->ie_bus_write16(sc, IE_SCB_ERROVR(scb), 0);
+}
+
+
+static void
+i82586_rx_errors(struct ie_softc *sc, int fn, int status)
+{
+ char bits[128];
+
+ printf("%s: rx error (frame# %d): %s\n", sc->arpcom.ac_if.if_name, fn,
+ bitmask_snprintf(status, IE_FD_STATUSBITS, bits, sizeof(bits)));
+}
+
+/*
+ * i82586 interrupt entry point.
+ */
+rtems_isr
+i82586_intr(rtems_vector_number vec, void *arg)
+{
+ struct ie_softc *sc = arg;
+
+#if I82586_DEBUG
+ static unsigned long icnt = 0;
+ I82586_TRACE(sc, I82586_INTS_REQ, icnt++);
+#endif
+
+ /*
+ * Implementation dependent interrupt handling. It must at least
+ * disabled interrupts from the i82586. It is hoped this can
+ * happen somewhere outside the i82586.
+ */
+ if (sc->intrhook)
+ (sc->intrhook)(sc, INTR_ENTER);
+
+ /*
+ * Wake the task to handle the interrupt. It will
+ * enabled the interrupts when it has finished.
+ */
+ rtems_bsdnet_event_send (sc->intr_task, i82586_WAKE_EVENT);
+}
+
+/*
+ * i82586 interrupt task. The task is actually an extension of the interrupt
+ * with a context switch in the middle. The RTEMS TCP/IP stack requires a task
+ * be used to talk to the stack as a network semaphore is claimed. This
+ * cannot happen during an interrupt.
+ */
+static void
+i82586_intr_task(void *arg)
+{
+ struct ie_softc *sc = arg;
+ rtems_event_set events;
+ u_int status;
+ int off;
+ int reset;
+
+ /*
+ * Not sure this is a good idea but as a out path exists and
+ * roads lead to it, it seems ok.
+ */
+ for (;;) {
+ rtems_bsdnet_event_receive (i82586_WAKE_EVENT,
+ RTEMS_WAIT | RTEMS_EVENT_ANY,
+ 0, &events);
+
+ off = IE_SCB_STATUS(sc->scb);
+ IE_BUS_BARRIER(sc, off, 2, BUS_SPACE_BARRIER_READ);
+ status = sc->ie_bus_read16(sc, off) & IE_ST_WHENCE;
+
+#if I82586_DEBUG
+ I82586_TRACE(sc, I82586_INTS_IN, status);
+#endif
+
+ reset = 0;
+
+ while ((status & IE_ST_WHENCE) != 0) {
+#if I82586_DEBUG
+ if (sc->sc_debug)
+ printf ("%s: -------\n%s: scbstatus=0x%x\n",
+ sc->arpcom.ac_if.if_name, sc->arpcom.ac_if.if_name, status);
+#endif
+
+#if 1
+ /* Ack interrupts FIRST in case we receive more during the ISR. */
+ ie_ack(sc, status & IE_ST_WHENCE);
+#endif
+
+ i82586_start_cmd(sc, status & IE_ST_WHENCE, 0, 0, ASYNC_OPTION);
+
+ if (status & (IE_ST_FR | IE_ST_RNR))
+ if (i82586_rint(sc, status) != 0) {
+ reset = 1;
+ break;
+ }
+
+ if (status & IE_ST_CX)
+ if (i82586_tint(sc, status) != 0) {
+ reset = 1;
+ break;
+ }
+
+#if I82586_DEBUG
+ if ((status & IE_ST_CNA) && (sc->sc_debug & IED_CNA))
+ printf("%s: cna; status=0x%x\n", sc->arpcom.ac_if.if_name, status);
+#endif
+
+#if 0
+ if (sc->intrhook)
+ (sc->intrhook)(sc, INTR_LOOP);
+#endif
+
+#if 1
+ /*
+ * Interrupt ACK was posted asynchronously; wait for
+ * completion here before reading SCB status again.
+ *
+ * If ACK fails, try to reset the chip, in hopes that
+ * it helps.
+ */
+ if (i82586_cmd_wait(sc) != 0) {
+ reset = 1;
+ break;
+ }
+#endif
+
+ IE_BUS_BARRIER(sc, off, 2, BUS_SPACE_BARRIER_READ);
+ status = sc->ie_bus_read16(sc, off);
+#if I82586_DEBUG
+ I82586_TRACE(sc, I82586_INTS_LOOPS, status);
+#endif
+ }
+
+ if (reset) {
+#if I82586_DEBUG
+ printf("%s: intr reset; status=0x%x\n", sc->arpcom.ac_if.if_name, status);
+#endif
+ i82586_cmd_wait(sc);
+ i82586_reset(sc, 1);
+ }
+
+#if I82586_DEBUG
+ I82586_TRACE(sc, I82586_INTS_OUT, status);
+#endif
+
+ if (sc->intrhook)
+ (sc->intrhook)(sc, INTR_EXIT);
+ }
+}
+
+/*
+ * Process a received-frame interrupt.
+ */
+int
+i82586_rint(struct ie_softc *sc, int scbstatus)
+{
+ static int timesthru = 1024;
+ int i, status, off;
+
+#if I82586_DEBUG
+ I82586_TRACE(sc, I82586_RX_INT, scbstatus);
+
+ if (sc->sc_debug & IED_RINT)
+ printf("%s: rint: status 0x%x\n",
+ sc->arpcom.ac_if.if_name, scbstatus);
+#endif
+
+ for (;;) {
+ int drop = 0;
+
+ i = sc->rfhead;
+ off = IE_RFRAME_STATUS(sc->rframes, i);
+ IE_BUS_BARRIER(sc, off, 2, BUS_SPACE_BARRIER_READ);
+ status = sc->ie_bus_read16(sc, off);
+
+#if I82586_DEBUG
+ if (sc->sc_debug & IED_RINT)
+ printf("%s: rint: frame(%d) status 0x%x\n",
+ sc->arpcom.ac_if.if_name, i, status);
+#endif
+ if ((status & IE_FD_COMPLETE) == 0) {
+ if ((status & IE_FD_OK) != 0) {
+ printf("%s: rint: weird: ",
+ sc->arpcom.ac_if.if_name);
+ i82586_rx_errors(sc, i, status);
+#if I82586_DEBUG
+ I82586_TRACE(sc, I82586_RX_ERR, status);
+#endif
+ break;
+ }
+ if (--timesthru == 0) {
+ /* Account the accumulated errors */
+ i82586_count_errors(sc);
+ timesthru = 1024;
+ }
+ break;
+ } else if ((status & IE_FD_OK) == 0) {
+ /*
+ * If the chip is configured to automatically
+ * discard bad frames, the only reason we can
+ * get here is an "out-of-resource" condition.
+ */
+ i82586_rx_errors(sc, i, status);
+ drop = 1;
+
+#if I82586_DEBUG
+ I82586_TRACE(sc, I82586_RX_DROP, status);
+#endif
+ }
+
+#if I82586_DEBUG
+ if ((status & IE_FD_BUSY) != 0)
+ printf("%s: rint: frame(%d) busy; status=0x%x\n",
+ sc->arpcom.ac_if.if_name, i, status);
+#endif
+
+ /*
+ * Advance the RFD list, since we're done with
+ * this descriptor.
+ */
+
+ /* Clear frame status */
+ sc->ie_bus_write16(sc, off, 0);
+
+ /* Put fence at this frame (the head) */
+ off = IE_RFRAME_LAST(sc->rframes, i);
+ sc->ie_bus_write16(sc, off, IE_FD_EOL|IE_FD_SUSP);
+
+ /* and clear RBD field */
+ off = IE_RFRAME_BUFDESC(sc->rframes, i);
+ sc->ie_bus_write16(sc, off, 0xffff);
+
+ /* Remove fence from current tail */
+ off = IE_RFRAME_LAST(sc->rframes, sc->rftail);
+ sc->ie_bus_write16(sc, off, 0);
+
+ if (++sc->rftail == sc->nframes)
+ sc->rftail = 0;
+ if (++sc->rfhead == sc->nframes)
+ sc->rfhead = 0;
+
+ /* Pull the frame off the board */
+ if (drop) {
+ i82586_drop_frames(sc);
+ if ((status & IE_FD_RNR) != 0)
+ sc->rnr_expect = 1;
+ sc->arpcom.ac_if.if_ierrors++;
+ } else if (ie_readframe(sc, i) != 0)
+ return (1);
+ }
+
+ if ((scbstatus & IE_ST_RNR) != 0) {
+
+ /*
+ * Receiver went "Not Ready". We try to figure out
+ * whether this was an expected event based on past
+ * frame status values.
+ */
+
+ if ((scbstatus & IE_RUS_SUSPEND) != 0) {
+ /*
+ * We use the "suspend on last frame" flag.
+ * Send a RU RESUME command in response, since
+ * we should have dealt with all completed frames
+ * by now.
+ */
+ printf("RINT: SUSPENDED; scbstatus=0x%x\n",
+ scbstatus);
+ if (i82586_start_cmd(sc, IE_RUC_RESUME, 0, 0, 0) == 0)
+ return (0);
+ printf("%s: RU RESUME command timed out\n",
+ sc->arpcom.ac_if.if_name);
+ return (1); /* Ask for a reset */
+ }
+
+ if (sc->rnr_expect != 0) {
+ /*
+ * The RNR condition was announced in the previously
+ * completed frame. Assume the receive ring is Ok,
+ * so restart the receiver without further delay.
+ */
+ i82586_start_transceiver(sc);
+ sc->rnr_expect = 0;
+ return (0);
+
+ } else if ((scbstatus & IE_RUS_NOSPACE) != 0) {
+ /*
+ * We saw no previous IF_FD_RNR flag.
+ * We check our ring invariants and, if ok,
+ * just restart the receiver at the current
+ * point in the ring.
+ */
+ if (i82586_chk_rx_ring(sc) != 0)
+ return (1);
+
+ i82586_start_transceiver(sc);
+ sc->arpcom.ac_if.if_ierrors++;
+ return (0);
+ } else
+ printf("%s: receiver not ready; scbstatus=0x%x\n",
+ sc->arpcom.ac_if.if_name, scbstatus);
+
+ sc->arpcom.ac_if.if_ierrors++;
+ return (1); /* Ask for a reset */
+ }
+
+ return (0);
+}
+
+/*
+ * Process a command-complete interrupt. These are only generated by the
+ * transmission of frames. This routine is deceptively simple, since most
+ * of the real work is done by i82586_start().
+ */
+int
+i82586_tint(struct ie_softc *sc, int scbstatus)
+{
+ struct ifnet *ifp = &sc->arpcom.ac_if;
+ int status;
+
+#if I82586_DEBUG
+ I82586_TRACE(sc, I82586_TX_INT, sc->xmit_busy);
+#endif
+
+#if I82586_DEBUG
+ if (sc->xmit_busy <= 0) {
+ printf("i82586_tint: (%d), WEIRD: xmit_busy=%d, xctail=%d, xchead=%d\n",
+ sc->trace_flow_in / 2, sc->xmit_busy, sc->xctail, sc->xchead);
+ I82586_TRACE(sc, I82586_TX_BAD, sc->xctail);
+ return (0);
+ }
+#endif
+
+ ifp->if_timer = 0;
+ ifp->if_flags &= ~IFF_OACTIVE;
+
+ status = sc->ie_bus_read16(sc, IE_CMD_XMIT_STATUS(sc->xmit_cmds,
+ sc->xctail));
+
+#if I82586_DEBUG
+ if (sc->sc_debug & IED_TINT)
+ printf("%s: tint: SCB status 0x%x; xmit status 0x%x\n",
+ sc->arpcom.ac_if.if_name, scbstatus, status);
+#endif
+
+ if ((status & IE_STAT_COMPL) == 0 || (status & IE_STAT_BUSY)) {
+ printf("i82586_tint: (%d) command still busy; status=0x%x; tail=%d\n",
+ sc->trace_flow_in / 2, status, sc->xctail);
+ printf("iestatus = 0x%x\n", scbstatus);
+/* sc->sc_debug = IED_ALL; */
+ }
+
+ if (status & IE_STAT_OK) {
+ ifp->if_opackets++;
+ ifp->if_collisions += (status & IE_XS_MAXCOLL);
+ } else {
+ ifp->if_oerrors++;
+ /*
+ * Check SQE and DEFERRED?
+ * What if more than one bit is set?
+ */
+ if (status & IE_STAT_ABORT)
+ printf("%s: send aborted\n", sc->arpcom.ac_if.if_name);
+ else if (status & IE_XS_NOCARRIER)
+ printf("%s: no carrier\n", sc->arpcom.ac_if.if_name);
+ else if (status & IE_XS_LOSTCTS)
+ printf("%s: lost CTS\n", sc->arpcom.ac_if.if_name);
+ else if (status & IE_XS_UNDERRUN)
+ printf("%s: DMA underrun\n", sc->arpcom.ac_if.if_name);
+ else if (status & IE_XS_EXCMAX) {
+ printf("%s: too many collisions\n",
+ sc->arpcom.ac_if.if_name);
+ sc->arpcom.ac_if.if_collisions += 16;
+ }
+ }
+
+ /*
+ * If multicast addresses were added or deleted while transmitting,
+ * ie_mc_reset() set the want_mcsetup flag indicating that we
+ * should do it.
+ */
+ if (sc->want_mcsetup) {
+ ie_mc_setup(sc, IE_XBUF_ADDR(sc, sc->xctail));
+ sc->want_mcsetup = 0;
+ }
+
+ /* Done with the buffer. */
+ sc->xmit_busy--;
+ sc->xctail = (sc->xctail + 1) % NTXBUF;
+
+ /* Start the next packet, if any, transmitting. */
+ if (sc->xmit_busy > 0)
+ iexmit(sc);
+
+ i82586_start_tx(sc);
+ return (0);
+}
+
+/*
+ * Get a range of receive buffer descriptors that represent one packet.
+ */
+static int
+i82586_get_rbd_list(struct ie_softc *sc, u_int16_t *start, u_int16_t *end, int *pktlen)
+{
+ int off, rbbase = sc->rbds;
+ int rbindex, count = 0;
+ int plen = 0;
+ int rbdstatus;
+
+ *start = rbindex = sc->rbhead;
+
+ do {
+ off = IE_RBD_STATUS(rbbase, rbindex);
+ IE_BUS_BARRIER(sc, off, 2, BUS_SPACE_BARRIER_READ);
+ rbdstatus = sc->ie_bus_read16(sc, off);
+ if ((rbdstatus & IE_RBD_USED) == 0) {
+ /*
+ * This means we are somehow out of sync. So, we
+ * reset the adapter.
+ */
+#if I82586_DEBUG
+ print_rbd(sc, rbindex);
+#endif
+ printf("%s: receive descriptors out of sync at %d\n",
+ sc->arpcom.ac_if.if_name, rbindex);
+ return (0);
+ }
+ plen += (rbdstatus & IE_RBD_CNTMASK);
+
+ if (++rbindex == sc->nrxbuf)
+ rbindex = 0;
+
+ ++count;
+ } while ((rbdstatus & IE_RBD_LAST) == 0);
+ *end = rbindex;
+ *pktlen = plen;
+ return (count);
+}
+
+
+/*
+ * Release a range of receive buffer descriptors after we've copied the packet.
+ */
+static void
+i82586_release_rbd_list(struct ie_softc *sc, u_int16_t start, u_int16_t end)
+{
+ int off, rbbase = sc->rbds;
+ int rbindex = start;
+
+ do {
+ /* Clear buffer status */
+ off = IE_RBD_STATUS(rbbase, rbindex);
+ sc->ie_bus_write16(sc, off, 0);
+ if (++rbindex == sc->nrxbuf)
+ rbindex = 0;
+ } while (rbindex != end);
+
+ /* Mark EOL at new tail */
+ rbindex = ((rbindex == 0) ? sc->nrxbuf : rbindex) - 1;
+ off = IE_RBD_BUFLEN(rbbase, rbindex);
+ sc->ie_bus_write16(sc, off, IE_RBUF_SIZE|IE_RBD_EOL);
+
+ /* Remove EOL from current tail */
+ off = IE_RBD_BUFLEN(rbbase, sc->rbtail);
+ sc->ie_bus_write16(sc, off, IE_RBUF_SIZE);
+
+ /* New head & tail pointer */
+/* hmm, why have both? head is always (tail + 1) % NRXBUF */
+ sc->rbhead = end;
+ sc->rbtail = rbindex;
+}
+
+/*
+ * Drop the packet at the head of the RX buffer ring.
+ * Called if the frame descriptor reports an error on this packet.
+ * Returns 1 if the buffer descriptor ring appears to be corrupt;
+ * and 0 otherwise.
+ */
+static int
+i82586_drop_frames(struct ie_softc *sc)
+{
+ u_int16_t bstart, bend;
+ int pktlen;
+
+ if (i82586_get_rbd_list(sc, &bstart, &bend, &pktlen) == 0)
+ return (1);
+ i82586_release_rbd_list(sc, bstart, bend);
+ return (0);
+}
+
+/*
+ * Check the RX frame & buffer descriptor lists for our invariants,
+ * i.e.: EOL bit set iff. it is pointed at by the r*tail pointer.
+ *
+ * Called when the receive unit has stopped unexpectedly.
+ * Returns 1 if an inconsistency is detected; 0 otherwise.
+ *
+ * The Receive Unit is expected to be NOT RUNNING.
+ */
+static int
+i82586_chk_rx_ring(struct ie_softc *sc)
+{
+ int n, off, val;
+
+ for (n = 0; n < sc->nrxbuf; n++) {
+ off = IE_RBD_BUFLEN(sc->rbds, n);
+ val = sc->ie_bus_read16(sc, off);
+ if ((n == sc->rbtail) ^ ((val & IE_RBD_EOL) != 0)) {
+ /* `rbtail' and EOL flag out of sync */
+ printf("%s: rx buffer descriptors out of sync at %d\n",
+ sc->arpcom.ac_if.if_name, n);
+ return (1);
+ }
+
+ /* Take the opportunity to clear the status fields here ? */
+ }
+
+ for (n = 0; n < sc->nframes; n++) {
+ off = IE_RFRAME_LAST(sc->rframes, n);
+ val = sc->ie_bus_read16(sc, off);
+ if ((n == sc->rftail) ^ ((val & (IE_FD_EOL|IE_FD_SUSP)) != 0)) {
+ /* `rftail' and EOL flag out of sync */
+ printf("%s: rx frame list out of sync at %d\n",
+ sc->arpcom.ac_if.if_name, n);
+ return (1);
+ }
+ }
+
+ return (0);
+}
+
+
+/*
+ * Read frame NUM from unit UNIT (pre-cached as IE).
+ *
+ * This routine reads the RFD at NUM, and copies in the buffers from the list
+ * of RBD, then rotates the RBD list so that the receiver doesn't start
+ * complaining. Trailers are DROPPED---there's no point in wasting time
+ * on confusing code to deal with them. Hopefully, this machine will
+ * never ARP for trailers anyway.
+ */
+static int
+ie_readframe(struct ie_softc *sc, int num) /* frame number to read */
+{
+ struct ether_header *eh;
+ struct mbuf *m;
+ u_int16_t bstart, bend;
+ int pktlen;
+
+ if (i82586_get_rbd_list(sc, &bstart, &bend, &pktlen) == 0) {
+ sc->arpcom.ac_if.if_ierrors++;
+ return (1);
+ }
+
+ m = ieget(sc, bstart, pktlen);
+ i82586_release_rbd_list(sc, bstart, bend);
+
+ if (m == 0) {
+ sc->arpcom.ac_if.if_ierrors++;
+ return (0);
+ }
+
+ /*
+ * Remove the mac header. This is different from the NetBSD
+ * stack.
+ */
+ eh = mtod(m, struct ether_header *);
+ m->m_data += sizeof (struct ether_header);
+ m->m_len -= sizeof (struct ether_header);
+ m->m_pkthdr.len -= sizeof (struct ether_header);
+
+#if I82586_DEBUG
+ if (sc->sc_debug & IED_READFRAME) {
+
+ printf("%s: frame from ether %s type 0x%x len %d\n",
+ sc->arpcom.ac_if.if_name,
+ ether_sprintf(eh->ether_shost),
+ (u_int)ntohs(eh->ether_type),
+ pktlen);
+ }
+#endif
+
+#if NBPFILTER > 0
+ /* Check for a BPF filter; if so, hand it up. */
+ if (sc->arpcom.ac_if.if_bpf != 0)
+ /* Pass it up. */
+ bpf_mtap(sc->arpcom.ac_if.if_bpf, m);
+#endif /* NBPFILTER > 0 */
+
+ /*
+ * Finally pass this packet up to higher layers.
+ */
+ ether_input (&sc->arpcom.ac_if, eh, m);
+ sc->arpcom.ac_if.if_ipackets++;
+
+#if I82586_DEBUG
+ I82586_TRACE(sc, I82586_RX_OK, sc->arpcom.ac_if.if_ipackets);
+#endif
+
+ return (0);
+}
+
+/*
+ * Start transmission on an interface.
+ */
+void
+i82586_start(struct ifnet *ifp)
+{
+ struct ie_softc *sc = ifp->if_softc;
+
+ if ((ifp->if_flags & (IFF_RUNNING | IFF_OACTIVE)) != IFF_RUNNING)
+ return;
+
+#if I82586_DEBUG
+ I82586_TRACE(sc, I82586_TX_REQ, sc->xmit_busy);
+#endif
+
+ rtems_bsdnet_event_send (sc->tx_task, i82586_TX_EVENT);
+}
+
+static void
+i82586_tx_task(void *arg)
+{
+ struct ie_softc *sc = arg;
+ rtems_event_set events;
+
+ for (;;) {
+ rtems_bsdnet_event_receive (i82586_TX_EVENT,
+ RTEMS_WAIT | RTEMS_EVENT_ANY,
+ 0, &events);
+
+#if I82586_DEBUG
+ I82586_TRACE(sc, I82586_TX_EVT, sc->xmit_busy);
+
+ if (sc->sc_debug)
+ printf ("%s: =======\n", sc->arpcom.ac_if.if_name);
+#endif
+
+ i82586_start_tx(sc);
+ }
+}
+
+static void
+i82586_start_tx(struct ie_softc *sc)
+{
+ struct ifnet *ifp = &sc->arpcom.ac_if;
+ struct mbuf *m0, *m;
+ int buffer, head, xbase;
+ u_short len;
+ int s;
+
+#if I82586_DEBUG
+ I82586_TRACE(sc, I82586_START_TX, sc->xmit_busy);
+#endif
+
+ if ((ifp->if_flags & (IFF_RUNNING | IFF_OACTIVE)) != IFF_RUNNING)
+ {
+#if I82586_DEBUG
+ I82586_TRACE(sc, I82586_TX_ACTIVE, ifp->if_snd.ifq_len);
+#endif
+ return;
+ }
+
+ for (;;) {
+ if (sc->xmit_busy == NTXBUF) {
+ ifp->if_flags |= IFF_OACTIVE;
+ break;
+ }
+
+ head = sc->xchead;
+ xbase = sc->xbds;
+
+ IF_DEQUEUE(&ifp->if_snd, m0);
+ if (m0 == 0)
+ break;
+
+ /* We need to use m->m_pkthdr.len, so require the header */
+ if ((m0->m_flags & M_PKTHDR) == 0)
+ panic("i82586_start: no header mbuf");
+
+#if NBPFILTER > 0
+ /* Tap off here if there is a BPF listener. */
+ if (ifp->if_bpf)
+ bpf_mtap(ifp->if_bpf, m0);
+#endif
+
+#if I82586_DEBUG
+ if (sc->sc_debug & IED_ENQ)
+ printf("%s: fill buffer %d\n", sc->arpcom.ac_if.if_name,
+ sc->xchead);
+#endif
+
+ if (m0->m_pkthdr.len > IE_TBUF_SIZE)
+ printf("%s: tbuf overflow\n", sc->arpcom.ac_if.if_name);
+
+ buffer = IE_XBUF_ADDR(sc, head);
+ for (m = m0; m != 0; m = m->m_next) {
+ (sc->memcopyout)(sc, mtod(m,caddr_t), buffer, m->m_len);
+ buffer += m->m_len;
+ }
+
+ len = max(m0->m_pkthdr.len, ETHER_MIN_LEN);
+ m_freem(m0);
+
+ /*
+ * Setup the transmit buffer descriptor here, while we
+ * know the packet's length.
+ */
+ sc->ie_bus_write16(sc, IE_XBD_FLAGS(xbase, head),
+ len | IE_TBD_EOL);
+ sc->ie_bus_write16(sc, IE_XBD_NEXT(xbase, head), 0xffff);
+ sc->ie_bus_write24(sc, IE_XBD_BUF(xbase, head),
+ IE_XBUF_ADDR(sc, head));
+
+ if (++head == NTXBUF)
+ head = 0;
+ sc->xchead = head;
+
+#if I82586_DEBUG
+ I82586_TRACE(sc, I82586_TX_START, sc->xmit_busy);
+#endif
+
+ s = splnet();
+ /* Start the first packet transmitting. */
+ if (sc->xmit_busy == 0)
+ iexmit(sc);
+
+ sc->xmit_busy++;
+
+ splx(s);
+ }
+}
+
+/*
+ * Probe IE's ram setup [ Move all this into MD front-end!? ]
+ * Use only if SCP and ISCP represent offsets into shared ram space.
+ */
+int
+i82586_proberam(struct ie_softc *sc)
+{
+ int result, off;
+
+ /* Put in 16-bit mode */
+ off = IE_SCP_BUS_USE(sc->scp);
+ sc->ie_bus_write16(sc, off, 0);
+ IE_BUS_BARRIER(sc, off, 2, BUS_SPACE_BARRIER_WRITE);
+
+ /* Set the ISCP `busy' bit */
+ off = IE_ISCP_BUSY(sc->iscp);
+ sc->ie_bus_write16(sc, off, 1);
+ IE_BUS_BARRIER(sc, off, 2, BUS_SPACE_BARRIER_WRITE);
+
+ if (sc->hwreset)
+ (sc->hwreset)(sc, CHIP_PROBE);
+
+ (sc->chan_attn) (sc, CHIP_PROBE);
+
+ delay(100); /* wait a while... */
+
+ /* Read back the ISCP `busy' bit; it should be clear by now */
+ off = IE_ISCP_BUSY(sc->iscp);
+ IE_BUS_BARRIER(sc, off, 2, BUS_SPACE_BARRIER_READ);
+ result = sc->ie_bus_read16(sc, off) == 0;
+
+ /* Acknowledge any interrupts we may have caused. */
+ ie_ack(sc, IE_ST_WHENCE);
+
+ return (result);
+}
+
+void
+i82586_reset(struct ie_softc *sc, int hard)
+{
+ int s = splnet();
+
+ if (hard)
+ printf("%s: reset\n", sc->arpcom.ac_if.if_name);
+
+ /* Clear OACTIVE in case we're called from watchdog (frozen xmit). */
+ sc->arpcom.ac_if.if_timer = 0;
+ sc->arpcom.ac_if.if_flags &= ~IFF_OACTIVE;
+
+ /*
+ * Stop i82586 dead in its tracks.
+ */
+ if (i82586_start_cmd(sc, IE_RUC_ABORT | IE_CUC_ABORT, 0, 0, 0))
+ printf("%s: abort commands timed out\n", sc->arpcom.ac_if.if_name);
+
+ /*
+ * This can really slow down the i82586_reset() on some cards, but it's
+ * necessary to unwedge other ones (eg, the Sun VME ones) from certain
+ * lockups.
+ */
+ if (hard && sc->hwreset)
+ (sc->hwreset)(sc, CARD_RESET);
+
+ delay(100);
+ ie_ack(sc, IE_ST_WHENCE);
+
+ if ((sc->arpcom.ac_if.if_flags & IFF_UP) != 0) {
+ i82586_init(&sc->arpcom.ac_if);
+ }
+
+ splx(s);
+}
+
+static void
+setup_simple_command(struct ie_softc *sc, int cmd, int cmdbuf)
+{
+ /* Setup a simple command */
+ sc->ie_bus_write16(sc, IE_CMD_COMMON_STATUS(cmdbuf), 0);
+ sc->ie_bus_write16(sc, IE_CMD_COMMON_CMD(cmdbuf), cmd | IE_CMD_LAST);
+ sc->ie_bus_write16(sc, IE_CMD_COMMON_LINK(cmdbuf), 0xffff);
+
+ /* Assign the command buffer to the SCB command list */
+ sc->ie_bus_write16(sc, IE_SCB_CMDLST(sc->scb), cmdbuf);
+}
+
+/*
+ * Run the time-domain reflectometer.
+ */
+static void
+ie_run_tdr(struct ie_softc *sc, int cmd)
+{
+ uint32_t result;
+
+ setup_simple_command(sc, IE_CMD_TDR, cmd);
+ sc->ie_bus_write16(sc, IE_CMD_TDR_TIME(cmd), 0);
+
+ if (i82586_start_cmd(sc, IE_CUC_START, cmd, IE_STAT_COMPL, 0) ||
+ (sc->ie_bus_read16(sc, IE_CMD_COMMON_STATUS(cmd)) & IE_STAT_OK) == 0)
+ result = 0x10000; /* XXX */
+ else
+ result = sc->ie_bus_read16(sc, IE_CMD_TDR_TIME(cmd));
+
+ /* Squash any pending interrupts */
+ ie_ack(sc, IE_ST_WHENCE);
+
+ if (result & IE_TDR_SUCCESS)
+ return;
+
+ if (result & 0x10000)
+ printf("%s: TDR command failed\n", sc->arpcom.ac_if.if_name);
+ else if (result & IE_TDR_XCVR)
+ printf("%s: transceiver problem\n", sc->arpcom.ac_if.if_name);
+ else if (result & IE_TDR_OPEN)
+ printf("%s: TDR detected incorrect termination %" PRId32 " clocks away\n",
+ sc->arpcom.ac_if.if_name, result & IE_TDR_TIME);
+ else if (result & IE_TDR_SHORT)
+ printf("%s: TDR detected a short circuit %" PRId32 " clocks away\n",
+ sc->arpcom.ac_if.if_name, result & IE_TDR_TIME);
+ else
+ printf("%s: TDR returned unknown status 0x%" PRIx32 "\n",
+ sc->arpcom.ac_if.if_name, result);
+}
+
+
+/*
+ * i82586_setup_bufs: set up the buffers
+ *
+ * We have a block of KVA at sc->buf_area which is of size sc->buf_area_sz.
+ * this is to be used for the buffers. The chip indexs its control data
+ * structures with 16 bit offsets, and it indexes actual buffers with
+ * 24 bit addresses. So we should allocate control buffers first so that
+ * we don't overflow the 16 bit offset field. The number of transmit
+ * buffers is fixed at compile time.
+ *
+ */
+static void
+i82586_setup_bufs(struct ie_softc *sc)
+{
+ int ptr = sc->buf_area; /* memory pool */
+ int n, r;
+
+ /*
+ * step 0: zero memory and figure out how many recv buffers and
+ * frames we can have.
+ */
+ ptr = (ptr + 3) & ~3; /* set alignment and stick with it */
+
+
+ /*
+ * step 1: lay out data structures in the shared-memory area
+ */
+
+ /* The no-op commands; used if "nop-chaining" is in effect */
+ sc->nop_cmds = ptr;
+ ptr += NTXBUF * IE_CMD_NOP_SZ;
+
+ /* The transmit commands */
+ sc->xmit_cmds = ptr;
+ ptr += NTXBUF * IE_CMD_XMIT_SZ;
+
+ /* The transmit buffers descriptors */
+ sc->xbds = ptr;
+ ptr += NTXBUF * IE_XBD_SZ;
+
+ /* The transmit buffers */
+ sc->xbufs = ptr;
+ ptr += NTXBUF * IE_TBUF_SIZE;
+
+ ptr = (ptr + 3) & ~3; /* re-align.. just in case */
+
+ /* Compute free space for RECV stuff */
+ n = sc->buf_area_sz - (ptr - sc->buf_area);
+
+ /* Compute size of one RECV frame */
+ r = IE_RFRAME_SZ + ((IE_RBD_SZ + IE_RBUF_SIZE) * B_PER_F);
+
+ sc->nframes = n / r;
+
+ if (sc->nframes <= 0)
+ panic("ie: bogus buffer calc\n");
+
+ sc->nrxbuf = sc->nframes * B_PER_F;
+
+ /* The receice frame descriptors */
+ sc->rframes = ptr;
+ ptr += sc->nframes * IE_RFRAME_SZ;
+
+ /* The receive buffer descriptors */
+ sc->rbds = ptr;
+ ptr += sc->nrxbuf * IE_RBD_SZ;
+
+ /* The receive buffers */
+ sc->rbufs = ptr;
+ ptr += sc->nrxbuf * IE_RBUF_SIZE;
+
+#if I82586_DEBUG
+ printf("%s: %d frames %d bufs\n", sc->arpcom.ac_if.if_name, sc->nframes,
+ sc->nrxbuf);
+#endif
+
+ /*
+ * step 2: link together the recv frames and set EOL on last one
+ */
+ for (n = 0; n < sc->nframes; n++) {
+ int m = (n == sc->nframes - 1) ? 0 : n + 1;
+
+ /* Clear status */
+ sc->ie_bus_write16(sc, IE_RFRAME_STATUS(sc->rframes,n), 0);
+
+ /* RBD link = NULL */
+ sc->ie_bus_write16(sc, IE_RFRAME_BUFDESC(sc->rframes,n),
+ 0xffff);
+
+ /* Make a circular list */
+ sc->ie_bus_write16(sc, IE_RFRAME_NEXT(sc->rframes,n),
+ IE_RFRAME_ADDR(sc->rframes,m));
+
+ /* Mark last as EOL */
+ sc->ie_bus_write16(sc, IE_RFRAME_LAST(sc->rframes,n),
+ ((m==0)? (IE_FD_EOL|IE_FD_SUSP) : 0));
+ }
+
+ /*
+ * step 3: link the RBDs and set EOL on last one
+ */
+ for (n = 0; n < sc->nrxbuf; n++) {
+ int m = (n == sc->nrxbuf - 1) ? 0 : n + 1;
+
+ /* Clear status */
+ sc->ie_bus_write16(sc, IE_RBD_STATUS(sc->rbds,n), 0);
+
+ /* Make a circular list */
+ sc->ie_bus_write16(sc, IE_RBD_NEXT(sc->rbds,n),
+ IE_RBD_ADDR(sc->rbds,m));
+
+ /* Link to data buffers */
+ sc->ie_bus_write24(sc, IE_RBD_BUFADDR(sc->rbds, n),
+ IE_RBUF_ADDR(sc, n));
+ sc->ie_bus_write16(sc, IE_RBD_BUFLEN(sc->rbds,n),
+ IE_RBUF_SIZE | ((m==0)?IE_RBD_EOL:0));
+ }
+
+ /*
+ * step 4: all xmit no-op commands loopback onto themselves
+ */
+ for (n = 0; n < NTXBUF; n++) {
+ sc->ie_bus_write16(sc, IE_CMD_NOP_STATUS(sc->nop_cmds, n), 0);
+
+ sc->ie_bus_write16(sc, IE_CMD_NOP_CMD(sc->nop_cmds, n),
+ IE_CMD_NOP);
+
+ sc->ie_bus_write16(sc, IE_CMD_NOP_LINK(sc->nop_cmds, n),
+ IE_CMD_NOP_ADDR(sc->nop_cmds, n));
+ }
+
+
+ /*
+ * step 6: set the head and tail pointers on receive to keep track of
+ * the order in which RFDs and RBDs are used.
+ */
+
+ /* Pointers to last packet sent and next available transmit buffer. */
+ sc->xchead = sc->xctail = 0;
+
+ /* Clear transmit-busy flag and set number of free transmit buffers. */
+ sc->xmit_busy = 0;
+
+ /*
+ * Pointers to first and last receive frame.
+ * The RFD pointed to by rftail is the only one that has EOL set.
+ */
+ sc->rfhead = 0;
+ sc->rftail = sc->nframes - 1;
+
+ /*
+ * Pointers to first and last receive descriptor buffer.
+ * The RBD pointed to by rbtail is the only one that has EOL set.
+ */
+ sc->rbhead = 0;
+ sc->rbtail = sc->nrxbuf - 1;
+
+/* link in recv frames * and buffer into the scb. */
+#if I82586_DEBUG
+ printf("%s: reserved %d bytes\n",
+ sc->arpcom.ac_if.if_name, ptr - sc->buf_area);
+#endif
+}
+
+static int
+ie_cfg_setup(struct ie_softc *sc, int cmd, int promiscuous, int manchester)
+{
+ int cmdresult, status;
+ u_int8_t buf[IE_CMD_CFG_SZ]; /* XXX malloc? */
+
+ *IE_CMD_CFG_CNT(buf) = 0x0c;
+ *IE_CMD_CFG_FIFO(buf) = 8;
+ *IE_CMD_CFG_SAVEBAD(buf) = 0x40;
+ *IE_CMD_CFG_ADDRLEN(buf) = 0x2e;
+ *IE_CMD_CFG_PRIORITY(buf) = 0;
+ *IE_CMD_CFG_IFS(buf) = 0x60;
+ *IE_CMD_CFG_SLOT_LOW(buf) = 0;
+ *IE_CMD_CFG_SLOT_HIGH(buf) = 0xf2;
+ *IE_CMD_CFG_PROMISC(buf) = (!!promiscuous) | (manchester << 2);
+ *IE_CMD_CFG_CRSCDT(buf) = 0;
+ *IE_CMD_CFG_MINLEN(buf) = 64;
+ *IE_CMD_CFG_JUNK(buf) = 0xff;
+ sc->memcopyout(sc, buf, cmd, IE_CMD_CFG_SZ);
+ setup_simple_command(sc, IE_CMD_CONFIG, cmd);
+ IE_BUS_BARRIER(sc, cmd, IE_CMD_CFG_SZ, BUS_SPACE_BARRIER_WRITE);
+
+ cmdresult = i82586_start_cmd(sc, IE_CUC_START, cmd, IE_STAT_COMPL, 0);
+ status = sc->ie_bus_read16(sc, IE_CMD_COMMON_STATUS(cmd));
+ if (cmdresult != 0) {
+ printf("%s: configure command timed out; status %x\n",
+ sc->arpcom.ac_if.if_name, status);
+ return (0);
+ }
+ if ((status & IE_STAT_OK) == 0) {
+ printf("%s: configure command failed; status %x\n",
+ sc->arpcom.ac_if.if_name, status);
+ return (0);
+ }
+
+ /* Squash any pending interrupts */
+ ie_ack(sc, IE_ST_WHENCE);
+ return (1);
+}
+
+static int
+ie_ia_setup(struct ie_softc *sc, int cmdbuf)
+{
+ int cmdresult, status;
+
+ setup_simple_command(sc, IE_CMD_IASETUP, cmdbuf);
+
+ (sc->memcopyout)(sc, sc->arpcom.ac_enaddr,
+ IE_CMD_IAS_EADDR(cmdbuf), ETHER_ADDR_LEN);
+
+ cmdresult = i82586_start_cmd(sc, IE_CUC_START, cmdbuf, IE_STAT_COMPL, 0);
+ status = sc->ie_bus_read16(sc, IE_CMD_COMMON_STATUS(cmdbuf));
+ if (cmdresult != 0) {
+ printf("%s: individual address command timed out; status %x\n",
+ sc->arpcom.ac_if.if_name, status);
+ return (0);
+ }
+ if ((status & IE_STAT_OK) == 0) {
+ printf("%s: individual address command failed; status %x\n",
+ sc->arpcom.ac_if.if_name, status);
+ return (0);
+ }
+
+ /* Squash any pending interrupts */
+ ie_ack(sc, IE_ST_WHENCE);
+ return (1);
+}
+
+/*
+ * Run the multicast setup command.
+ * Called at splnet().
+ */
+static int
+ie_mc_setup(struct ie_softc *sc, int cmdbuf)
+{
+ int cmdresult, status;
+
+ if (sc->mcast_count == 0)
+ return (1);
+
+ setup_simple_command(sc, IE_CMD_MCAST, cmdbuf);
+
+ (sc->memcopyout)(sc, (caddr_t)sc->mcast_addrs,
+ IE_CMD_MCAST_MADDR(cmdbuf),
+ sc->mcast_count * ETHER_ADDR_LEN);
+
+ sc->ie_bus_write16(sc, IE_CMD_MCAST_BYTES(cmdbuf),
+ sc->mcast_count * ETHER_ADDR_LEN);
+
+ /* Start the command */
+ cmdresult = i82586_start_cmd(sc, IE_CUC_START, cmdbuf, IE_STAT_COMPL, 0);
+ status = sc->ie_bus_read16(sc, IE_CMD_COMMON_STATUS(cmdbuf));
+ if (cmdresult != 0) {
+ printf("%s: multicast setup command timed out; status %x\n",
+ sc->arpcom.ac_if.if_name, status);
+ return (0);
+ }
+ if ((status & IE_STAT_OK) == 0) {
+ printf("%s: multicast setup command failed; status %x\n",
+ sc->arpcom.ac_if.if_name, status);
+ return (0);
+ }
+
+ /* Squash any pending interrupts */
+ ie_ack(sc, IE_ST_WHENCE);
+ return (1);
+}
+
+/*
+ * This routine takes the environment generated by check_ie_present() and adds
+ * to it all the other structures we need to operate the adapter. This
+ * includes executing the CONFIGURE, IA-SETUP, and MC-SETUP commands, starting
+ * the receiver unit, and clearing interrupts.
+ *
+ * THIS ROUTINE MUST BE CALLED AT splnet() OR HIGHER.
+ */
+void
+i82586_init(void *arg)
+{
+ struct ie_softc *sc = arg;
+ struct ifnet *ifp = &sc->arpcom.ac_if;
+ int cmd;
+
+ sc->async_cmd_inprogress = 0;
+ sc->xmit_busy = 0;
+
+#if I82586_DEBUG
+ memset(sc->trace_flow, 0, sizeof(sc->trace_flow));
+ sc->trace_flow_wrap = 0;
+#endif
+ sc->trace_flow_in = 0;
+
+ cmd = sc->buf_area;
+
+#if I82586_DEBUG
+ printf ("%s: sc_debug at 0x%08x\n", sc->arpcom.ac_if.if_name, (unsigned int) &sc->sc_debug);
+#endif
+
+ /*
+ * Send the configure command first.
+ */
+ if (ie_cfg_setup(sc, cmd, sc->promisc, 0) == 0)
+ return;
+
+ /*
+ * Send the Individual Address Setup command.
+ */
+ if (ie_ia_setup(sc, cmd) == 0)
+ return;
+
+ /*
+ * Run the time-domain reflectometer.
+ */
+ ie_run_tdr(sc, cmd);
+
+ /*
+ * Set the multi-cast filter, if any
+ */
+ if (ie_mc_setup(sc, cmd) == 0)
+ return;
+
+ /*
+ * If no tasks exist, create them. Need to add something to allow
+ * different names for the different devices.
+ */
+ if (sc->intr_task == 0)
+ sc->intr_task = rtems_bsdnet_newproc ("IEi0", 2048, i82586_intr_task, sc);
+ if (sc->tx_task == 0)
+ sc->tx_task = rtems_bsdnet_newproc ("IEt0", 2048, i82586_tx_task, sc);
+
+
+ /*
+ * Acknowledge any interrupts we have generated thus far.
+ */
+ ie_ack(sc, IE_ST_WHENCE);
+
+ /*
+ * Set up the transmit and recv buffers.
+ */
+ i82586_setup_bufs(sc);
+
+ if (sc->hwinit)
+ (sc->hwinit)(sc);
+
+ ifp->if_flags |= IFF_RUNNING;
+ ifp->if_flags &= ~IFF_OACTIVE;
+
+ if (NTXBUF < 2)
+ sc->do_xmitnopchain = 0;
+
+ i82586_start_transceiver(sc);
+}
+
+/*
+ * Start the RU and possibly the CU unit
+ */
+static void
+i82586_start_transceiver(struct ie_softc *sc)
+{
+#if I82586_DEBUG
+ I82586_TRACE(sc, I82586_RX_START, 0);
+#endif
+
+ /*
+ * Start RU at current position in frame & RBD lists.
+ */
+ sc->ie_bus_write16(sc, IE_RFRAME_BUFDESC(sc->rframes,sc->rfhead),
+ IE_RBD_ADDR(sc->rbds, sc->rbhead));
+
+ sc->ie_bus_write16(sc, IE_SCB_RCVLST(sc->scb),
+ IE_RFRAME_ADDR(sc->rframes,sc->rfhead));
+
+ if (sc->do_xmitnopchain) {
+ /* Stop transmit command chain */
+ if (i82586_start_cmd(sc, IE_CUC_SUSPEND|IE_RUC_SUSPEND, 0, 0, 0))
+ printf("%s: CU/RU stop command timed out\n",
+ sc->arpcom.ac_if.if_name);
+
+ /* Start the receiver & transmitter chain */
+ /* sc->scb->ie_command_list =
+ IEADDR(sc->nop_cmds[(sc->xctail+NTXBUF-1) % NTXBUF]);*/
+ sc->ie_bus_write16(sc, IE_SCB_CMDLST(sc->scb),
+ IE_CMD_NOP_ADDR(
+ sc->nop_cmds,
+ (sc->xctail + NTXBUF - 1) % NTXBUF));
+
+ if (i82586_start_cmd(sc, IE_CUC_START|IE_RUC_START, 0, 0, 0))
+ printf("%s: CU/RU command timed out\n",
+ sc->arpcom.ac_if.if_name);
+ } else {
+ if (i82586_start_cmd(sc, IE_RUC_START, 0, 0, 0))
+ printf("%s: RU command timed out\n",
+ sc->arpcom.ac_if.if_name);
+ }
+}
+
+void
+i82586_stop(struct ifnet *ifp, int disable)
+{
+ struct ie_softc *sc = ifp->if_softc;
+
+ if (i82586_start_cmd(sc, IE_RUC_SUSPEND | IE_CUC_SUSPEND, 0, 0, 0))
+ printf("%s: iestop: disable commands timed out\n",
+ sc->arpcom.ac_if.if_name);
+}
+
+int
+i82586_ioctl(struct ifnet *ifp, ioctl_command_t cmd, caddr_t data)
+{
+ struct ie_softc *sc = ifp->if_softc;
+/* struct ifreq *ifr = (struct ifreq *)data; */
+ int s;
+ int error = 0;
+
+ s = splnet();
+ switch(cmd) {
+ case SIOCGIFMEDIA:
+ case SIOCSIFMEDIA:
+ break;
+ case SIO_RTEMS_SHOW_STATS:
+#if I82586_DEBUG
+ print_softie(sc);
+#endif
+ break;
+ default:
+ error = ether_ioctl(ifp, cmd, data);
+ if (error == ENETRESET) {
+ /*
+ * Multicast list has changed; set the hardware filter
+ * accordingly.
+ */
+ ie_mc_reset(sc);
+ error = 0;
+ }
+ break;
+ }
+#if I82586_DEBUG
+ if (cmd == SIOCSIFFLAGS)
+ sc->sc_debug = (ifp->if_flags & IFF_DEBUG) ? IED_ALL : 0;
+#endif
+ splx(s);
+ return (error);
+}
+
+static void
+ie_mc_reset(struct ie_softc *sc)
+{
+ struct ether_multi *enm;
+ struct ether_multistep step;
+ int size;
+
+ /*
+ * Step through the list of addresses.
+ */
+ again:
+ size = 0;
+ sc->mcast_count = 0;
+ ETHER_FIRST_MULTI(step, &sc->arpcom, enm);
+ while (enm) {
+ size += 6;
+ if (sc->mcast_count >= IE_MAXMCAST ||
+ memcmp(enm->enm_addrlo, enm->enm_addrhi, 6) != 0) {
+ sc->arpcom.ac_if.if_flags |= IFF_ALLMULTI;
+ i82586_ioctl(&sc->arpcom.ac_if,
+ SIOCSIFFLAGS, (void *)0);
+ return;
+ }
+ ETHER_NEXT_MULTI(step, enm);
+ }
+
+ if (size > sc->mcast_addrs_size) {
+ /* Need to allocate more space */
+ if (sc->mcast_addrs_size)
+ free(sc->mcast_addrs, M_IPMADDR);
+ sc->mcast_addrs = (char *)
+ malloc(size, M_IPMADDR, M_WAITOK);
+ sc->mcast_addrs_size = size;
+ }
+
+ /*
+ * We've got the space; now copy the addresses
+ */
+ ETHER_FIRST_MULTI(step, &sc->arpcom, enm);
+ while (enm) {
+ if (sc->mcast_count >= IE_MAXMCAST)
+ goto again; /* Just in case */
+
+ memcpy(&sc->mcast_addrs[sc->mcast_count], enm->enm_addrlo, 6);
+ sc->mcast_count++;
+ ETHER_NEXT_MULTI(step, enm);
+ }
+ sc->want_mcsetup = 1;
+}
+
+/*
+ * Media change callback.
+ */
+int
+i82586_mediachange(struct ifnet *ifp)
+{
+ struct ie_softc *sc = ifp->if_softc;
+
+ if (sc->sc_mediachange)
+ return ((*sc->sc_mediachange)(sc));
+ return (0);
+}
+
+/*
+ * Media status callback.
+ */
+void
+i82586_mediastatus(struct ifnet *ifp, struct ifmediareq *ifmr)
+{
+ struct ie_softc *sc = ifp->if_softc;
+
+ if (sc->sc_mediastatus)
+ (*sc->sc_mediastatus)(sc, ifmr);
+}
+
+#if I82586_DEBUG
+static void
+print_softie(struct ie_softc *sc)
+{
+ static char *trace_labels[] = {
+ "INTS_REQ",
+ "INTS_IN",
+ "INTS_LOOPS",
+ "INTS_OUT",
+ "RX_INT",
+ "RX_DROP",
+ "RX_ERR",
+ "RX_OK",
+ "RX_START",
+ "START_TX",
+ "TX_START",
+ "TX_INT",
+ "TX_REQ",
+ "TX_EVT",
+ "TX_EMIT",
+ "TX_BAD",
+ "TX_ACTIVE",
+ "TRACE_CNT"
+ };
+
+ int i;
+
+ printf("i82586 %s:\n", sc->arpcom.ac_if.if_name);
+
+ printf(" iobase=%p\n", sc->sc_iobase);
+
+ printf(" scp=0x%08x\t\tiscp=0x%08x\t\tscb=0x%08x\n",
+ sc->scp, sc->iscp, sc->scb);
+ printf(" buf_area=0x%08x\tbuf_area_sz=0x%08x\n",
+ sc->buf_area, sc->buf_area_sz);
+ printf(" rframes=0x%08x\trbds=0x%08x\t\trbufs=0x%08x\n",
+ sc->rframes, sc->rbds, sc->rbufs);
+ printf(" nop_cmds=0x%08x\txmit_cmds=0x%08x\n",
+ sc->nop_cmds, sc->xmit_cmds);
+ printf(" xbds=0x%08x\txbufs=0x%08x\n\n",
+ sc->xbds, sc->xbufs);
+ printf(" rfhead=%d\trftail=%d\n",
+ sc->rfhead, sc->rftail);
+ printf(" rbhead=%d\trbtail=%d\n",
+ sc->rbhead, sc->rbtail);
+ printf(" nframes=%d\tnrxbuf=%d\trnr_expect=%d\n",
+ sc->nframes, sc->nrxbuf, sc->rnr_expect);
+ printf(" xchead=%d\txctail=%d\n",
+ sc->xchead, sc->xctail);
+ printf(" xmit_busy=%d\txmit_req=%d\tdo_xmitnopchain=%d\n",
+ sc->xmit_busy, sc->xmit_req, sc->do_xmitnopchain);
+ printf(" promisc=%d\tasync_cmd_inprogress=%d\n\n",
+ sc->promisc, sc->async_cmd_inprogress);
+
+ {
+ int cnt;
+ int in;
+ int lfdone = 0;
+ char *tabs;
+
+ if (!sc->trace_flow_wrap) {
+ cnt = sc->trace_flow_in;
+ in = 0;
+ }
+ else {
+ cnt = I82586_TRACE_FLOW;
+ in = sc->trace_flow_in;
+ }
+
+ sc->trace_flow_in = sc->trace_flow_wrap = 0;
+
+ cnt /= 2;
+
+ for (i = 0; i < cnt; i++) {
+ if (!lfdone) {
+ switch (sc->trace_flow[in]) {
+ case I82586_INTS_REQ:
+ case I82586_INTS_IN:
+ printf("\n");
+ }
+ }
+
+ lfdone = 0;
+
+ if (strlen(trace_labels[sc->trace_flow[in]]) < 8)
+ tabs = "\t\t";
+ else
+ tabs = "\t";
+
+ printf(" %d\t%s%s0x%08x (%d)\n",
+ i, trace_labels[sc->trace_flow[in]], tabs,
+ sc->trace_flow[in + 1], sc->trace_flow[in + 1]);
+
+ switch (sc->trace_flow[in]) {
+ case I82586_INTS_REQ:
+ case I82586_INTS_OUT:
+ lfdone = 1;
+ printf("\n");
+ }
+
+ in += 2;
+
+ if (in >= I82586_TRACE_FLOW)
+ in = 0;
+ }
+ }
+}
+
+static void
+print_rbd(struct ie_softc *sc, int n)
+{
+ printf("RBD at %08x:\n status %04x, next %04x, buffer %lx\n"
+ "length/EOL %04x\n", IE_RBD_ADDR(sc->rbds,n),
+ sc->ie_bus_read16(sc, IE_RBD_STATUS(sc->rbds,n)),
+ sc->ie_bus_read16(sc, IE_RBD_NEXT(sc->rbds,n)),
+ (u_long)0,/*bus_space_read_4(sc->bt, sc->bh, IE_RBD_BUFADDR(sc->rbds,n)),-* XXX */
+ sc->ie_bus_read16(sc, IE_RBD_BUFLEN(sc->rbds,n)));
+}
+
+#endif
diff --git a/bsps/shared/net/i82586reg.h b/bsps/shared/net/i82586reg.h
new file mode 100644
index 0000000000..e093aff68f
--- /dev/null
+++ b/bsps/shared/net/i82586reg.h
@@ -0,0 +1,448 @@
+/* $NetBSD: i82586reg.h,v 1.7 1998/02/28 01:07:45 pk Exp $ */
+
+/*-
+ * Copyright (c) 1998 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Paul Kranenburg.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * 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.
+ */
+
+/*-
+ * Copyright (c) 1992, University of Vermont and State Agricultural College.
+ * Copyright (c) 1992, Garrett A. Wollman.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * Vermont and State Agricultural College and Garrett A. Wollman.
+ * 4. Neither the name of the University nor the name of the author
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 UNIVERSITY OR 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.
+ */
+
+/*
+ * Intel 82586 Ethernet chip
+ * Register, bit, and structure definitions.
+ *
+ * Written by GAW with reference to the Clarkson Packet Driver code for this
+ * chip written by Russ Nelson and others.
+ */
+
+/*
+ * NOTE, the structure definitions in here are for reference only.
+ * We use integer offsets exclusively to access the i82586 data structures.
+ */
+
+
+#if 0
+/*
+ * This is the master configuration block.
+ * It tells the hardware where all the rest of the stuff is.
+ */
+struct __ie_sys_conf_ptr {
+ u_int16_t mbz; /* must be zero */
+ u_int8_t ie_bus_use; /* true if 8-bit only */
+ u_int8_t mbz2[5]; /* must be zero */
+ u_int32_t ie_iscp_ptr; /* 24-bit physaddr of ISCP */
+};
+#endif
+#define IE_SCP_SZ 12
+#define IE_SCP_BUS_USE(base) ((base) + 2)
+#define IE_SCP_ISCP(base) ((base) + 8)
+
+/*
+ * Note that this is wired in hardware; the SCP is always located here, no
+ * matter what.
+ */
+#define IE_SCP_ADDR 0xfffff4
+
+#if 0
+/*
+ * The tells the hardware where all the rest of the stuff is, too.
+ * FIXME: some of these should be re-commented after we figure out their
+ * REAL function.
+ */
+struct __ie_int_sys_conf_ptr {
+ u_int8_t ie_busy; // zeroed after init
+ u_int8_t mbz;
+ u_int16_t ie_scb_offset; // 16-bit physaddr of next struct
+ caddr_t ie_base; // 24-bit physaddr for all 16-bit vars
+};
+#endif
+#define IE_ISCP_SZ 8
+#define IE_ISCP_BUSY(base) ((base) + 0)
+#define IE_ISCP_SCB(base) ((base) + 2)
+#define IE_ISCP_BASE(base) ((base) + 4)
+
+#if 0
+/*
+ * This FINALLY tells the hardware what to do and where to put it.
+ */
+struct __ie_sys_ctl_block {
+ u_int16_t ie_status; // status word
+ u_int16_t ie_command; // command word
+ u_int16_t ie_command_list; // 16-pointer to command block list
+ u_int16_t ie_recv_list; // 16-pointer to receive frame list
+ u_int16_t ie_err_crc; // CRC errors
+ u_int16_t ie_err_align; // Alignment errors
+ u_int16_t ie_err_resource; // Resource errors
+ u_int16_t ie_err_overrun; // Overrun errors
+};
+#endif
+#define IE_SCB_SZ 16
+#define IE_SCB_STATUS(base) ((base) + 0)
+#define IE_SCB_CMD(base) ((base) + 2)
+#define IE_SCB_CMDLST(base) ((base) + 4)
+#define IE_SCB_RCVLST(base) ((base) + 6)
+#define IE_SCB_ERRCRC(base) ((base) + 8)
+#define IE_SCB_ERRALN(base) ((base) + 10)
+#define IE_SCB_ERRRES(base) ((base) + 12)
+#define IE_SCB_ERROVR(base) ((base) + 14)
+
+/* Command values */
+#define IE_RUC_MASK 0x0070 /* mask for RU command */
+#define IE_RUC_NOP 0 /* for completeness */
+#define IE_RUC_START 0x0010 /* start receive unit command */
+#define IE_RUC_RESUME 0x0020 /* resume a suspended receiver command */
+#define IE_RUC_SUSPEND 0x0030 /* suspend receiver command */
+#define IE_RUC_ABORT 0x0040 /* abort current receive operation */
+
+#define IE_CUC_MASK 0x0700 /* mask for CU command */
+#define IE_CUC_NOP 0 /* included for completeness */
+#define IE_CUC_START 0x0100 /* do-command command */
+#define IE_CUC_RESUME 0x0200 /* resume a suspended cmd list */
+#define IE_CUC_SUSPEND 0x0300 /* suspend current command */
+#define IE_CUC_ABORT 0x0400 /* abort current command */
+
+#define IE_ACK_COMMAND 0xf000 /* mask for ACK command */
+#define IE_ACK_CX 0x8000 /* ack IE_ST_CX */
+#define IE_ACK_FR 0x4000 /* ack IE_ST_FR */
+#define IE_ACK_CNA 0x2000 /* ack IE_ST_CNA */
+#define IE_ACK_RNR 0x1000 /* ack IE_ST_RNR */
+
+#define IE_ACTION_COMMAND(x) (((x) & IE_CUC_MASK) == IE_CUC_START)
+ /* is this command an action command? */
+
+/* Status values */
+#define IE_ST_WHENCE 0xf000 /* mask for cause of interrupt */
+#define IE_ST_CX 0x8000 /* command with I bit completed */
+#define IE_ST_FR 0x4000 /* frame received */
+#define IE_ST_CNA 0x2000 /* all commands completed */
+#define IE_ST_RNR 0x1000 /* receive not ready */
+
+#define IE_CUS_MASK 0x0700 /* mask for command unit status */
+#define IE_CUS_ACTIVE 0x0200 /* command unit is active */
+#define IE_CUS_SUSPEND 0x0100 /* command unit is suspended */
+
+#define IE_RUS_MASK 0x0070 /* mask for receiver unit status */
+#define IE_RUS_SUSPEND 0x0010 /* receiver is suspended */
+#define IE_RUS_NOSPACE 0x0020 /* receiver has no resources */
+#define IE_RUS_READY 0x0040 /* receiver is ready */
+
+#if 0
+/*
+ * This is filled in partially by the chip, partially by us.
+ */
+struct __ie_recv_frame_desc {
+ u_int16_t ie_fd_status; // status for this frame
+ u_int16_t ie_fd_last; // end of frame list flag
+ u_int16_t ie_fd_next; // 16-pointer to next RFD
+ u_int16_t ie_fd_buf_desc; // 16-pointer to list of buffer descs
+ struct __ie_en_addr dest; // destination ether
+ struct __ie_en_addr src; // source ether
+ u_int16_t ie_length; // 802 length/Ether type
+ u_short mbz; // must be zero
+};
+#endif
+#define IE_RFRAME_SZ 24
+#define IE_RFRAME_ADDR(base,i) ((base) + (i) * IE_RFRAME_SZ)
+#define IE_RFRAME_STATUS(b,i) (IE_RFRAME_ADDR(b,i) + 0)
+#define IE_RFRAME_LAST(b,i) (IE_RFRAME_ADDR(b,i) + 2)
+#define IE_RFRAME_NEXT(b,i) (IE_RFRAME_ADDR(b,i) + 4)
+#define IE_RFRAME_BUFDESC(b,i) (IE_RFRAME_ADDR(b,i) + 6)
+#define IE_RFRAME_EDST(b,i) (IE_RFRAME_ADDR(b,i) + 8)
+#define IE_RFRAME_ESRC(b,i) (IE_RFRAME_ADDR(b,i) + 14)
+#define IE_RFRAME_ELEN(b,i) (IE_RFRAME_ADDR(b,i) + 20)
+
+/* "last" bits */
+#define IE_FD_EOL 0x8000 /* last rfd in list */
+#define IE_FD_SUSP 0x4000 /* suspend RU after receipt */
+
+/* status field bits */
+#define IE_FD_COMPLETE 0x8000 /* frame is complete */
+#define IE_FD_BUSY 0x4000 /* frame is busy */
+#define IE_FD_OK 0x2000 /* frame is ok */
+#define IE_FD_CRC 0x0800 /* CRC error */
+#define IE_FD_ALGN 0x0400 /* Alignment error */
+#define IE_FD_RNR 0x0200 /* receiver out of resources here */
+#define IE_FD_OVR 0x0100 /* DMA overrun */
+#define IE_FD_SHORT 0x0080 /* Short frame */
+#define IE_FD_NOEOF 0x0040 /* no EOF (?) */
+#define IE_FD_ERRMASK /* all error bits */ \
+ (IE_FD_CRC|IE_FD_ALGN|IE_FD_RNR|IE_FD_OVR|IE_FD_SHORT|IE_FD_NOEOF)
+#define IE_FD_STATUSBITS \
+ "\20\20COMPLT\17BUSY\16OK\14CRC\13ALGN\12RNR\11OVR\10SHORT\7NOEOF"
+
+#if 0
+/*
+ * linked list of buffers...
+ */
+struct __ie_recv_buf_desc {
+ u_int16_t ie_rbd_status; // status for this buffer
+ u_int16_t ie_rbd_next; // 16-pointer to next RBD
+ caddr_t ie_rbd_buffer; // 24-pointer to buffer for this RBD
+ u_int16_t ie_rbd_length; // length of the buffer
+ u_int16_t mbz; // must be zero
+};
+#endif
+#define IE_RBD_SZ 12
+#define IE_RBD_ADDR(base,i) ((base) + (i) * IE_RBD_SZ)
+#define IE_RBD_STATUS(b,i) (IE_RBD_ADDR(b,i) + 0)
+#define IE_RBD_NEXT(b,i) (IE_RBD_ADDR(b,i) + 2)
+#define IE_RBD_BUFADDR(b,i) (IE_RBD_ADDR(b,i) + 4)
+#define IE_RBD_BUFLEN(b,i) (IE_RBD_ADDR(b,i) + 8)
+
+/* RBD status fields */
+#define IE_RBD_LAST 0x8000 /* last buffer */
+#define IE_RBD_USED 0x4000 /* this buffer has data */
+#define IE_RBD_CNTMASK 0x3fff /* byte count of buffer data */
+
+/* RDB `End Of List' flag; encoded in `buffer length' field */
+#define IE_RBD_EOL 0x8000 /* last buffer */
+
+
+#if 0
+/*
+ * All commands share this in common.
+ */
+struct __ie_cmd_common {
+ u_int16_t ie_cmd_status; // status of this command
+ u_int16_t ie_cmd_cmd; // command word
+ u_int16_t ie_cmd_link; // link to next command
+};
+#endif
+#define IE_CMD_COMMON_SZ 6
+#define IE_CMD_COMMON_STATUS(base) ((base) + 0)
+#define IE_CMD_COMMON_CMD(base) ((base) + 2)
+#define IE_CMD_COMMON_LINK(base) ((base) + 4)
+
+#define IE_STAT_COMPL 0x8000 /* command is completed */
+#define IE_STAT_BUSY 0x4000 /* command is running now */
+#define IE_STAT_OK 0x2000 /* command completed successfully */
+#define IE_STAT_ABORT 0x1000 /* command was aborted */
+
+#define IE_CMD_NOP 0x0000 /* NOP */
+#define IE_CMD_IASETUP 0x0001 /* initial address setup */
+#define IE_CMD_CONFIG 0x0002 /* configure command */
+#define IE_CMD_MCAST 0x0003 /* multicast setup command */
+#define IE_CMD_XMIT 0x0004 /* transmit command */
+#define IE_CMD_TDR 0x0005 /* time-domain reflectometer command */
+#define IE_CMD_DUMP 0x0006 /* dump command */
+#define IE_CMD_DIAGNOSE 0x0007 /* diagnostics command */
+
+#define IE_CMD_LAST 0x8000 /* this is the last command in the list */
+#define IE_CMD_SUSPEND 0x4000 /* suspend CU after this command */
+#define IE_CMD_INTR 0x2000 /* post an interrupt after completion */
+
+/*
+ * No-op commands; just like COMMON but "indexable"
+ */
+#define IE_CMD_NOP_SZ IE_CMD_COMMON_SZ
+#define IE_CMD_NOP_ADDR(base,i) ((base) + (i) * IE_CMD_NOP_SZ)
+#define IE_CMD_NOP_STATUS(b,i) (IE_CMD_NOP_ADDR(b,i) + 0)
+#define IE_CMD_NOP_CMD(b,i) (IE_CMD_NOP_ADDR(b,i) + 2)
+#define IE_CMD_NOP_LINK(b,i) (IE_CMD_NOP_ADDR(b,i) + 4)
+
+
+#if 0
+/*
+ * This is the command to transmit a frame.
+ */
+struct __ie_xmit_cmd {
+ struct __ie_cmd_common com; // common part
+#define __ie_xmit_status com.ie_cmd_status
+
+ u_int16_t ie_xmit_desc; // pointer to buffer descriptor
+ struct __ie_en_addr ie_xmit_addr; // destination address
+ u_int16_t ie_xmit_length; // 802.3 length/Ether type field
+};
+#endif
+#define IE_CMD_XMIT_SZ (IE_CMD_COMMON_SZ + 10)
+#define IE_CMD_XMIT_ADDR(base,i) ((base) + (i) * IE_CMD_XMIT_SZ)
+#define IE_CMD_XMIT_STATUS(b,i) \
+ (IE_CMD_XMIT_ADDR(b,i) + 0) /* == CMD_COMMON_STATUS */
+#define IE_CMD_XMIT_CMD(b,i) \
+ (IE_CMD_XMIT_ADDR(b,i) + 2) /* == CMD_COMMON_CMD */
+#define IE_CMD_XMIT_LINK(b,i) \
+ (IE_CMD_XMIT_ADDR(b,i) + 4) /* == CMD_COMMON_LINK */
+#define IE_CMD_XMIT_DESC(b,i) \
+ (IE_CMD_XMIT_ADDR(b,i) + IE_CMD_COMMON_SZ + 0)
+#define IE_CMD_XMIT_EADDR(b,i) \
+ (IE_CMD_XMIT_ADDR(b,i) + IE_CMD_COMMON_SZ + 2)
+#define IE_CMD_XMIT_LEN(b,i) \
+ (IE_CMD_XMIT_ADDR(b,i) + IE_CMD_COMMON_SZ + 8)
+
+#define IE_XS_MAXCOLL 0x000f /* number of collisions during transmit */
+#define IE_XS_EXCMAX 0x0020 /* exceeded maximum number of collisions */
+#define IE_XS_SQE 0x0040 /* SQE positive */
+#define IE_XS_DEFERRED 0x0080 /* transmission deferred */
+#define IE_XS_UNDERRUN 0x0100 /* DMA underrun */
+#define IE_XS_LOSTCTS 0x0200 /* Lost CTS */
+#define IE_XS_NOCARRIER 0x0400 /* No Carrier */
+#define IE_XS_LATECOLL 0x0800 /* Late collision */
+
+#if 0
+/*
+ * This is a buffer descriptor for a frame to be transmitted.
+ */
+struct __ie_xmit_buf {
+ u_int16_t ie_xmit_flags; // see below
+ u_int16_t ie_xmit_next; // 16-pointer to next desc
+ caddr_t ie_xmit_buf; // 24-pointer to the actual buffer
+};
+#endif
+#define IE_XBD_SZ 8
+#define IE_XBD_ADDR(base,i) ((base) + (i) * IE_XBD_SZ)
+#define IE_XBD_FLAGS(b,i) (IE_XBD_ADDR(b,i) + 0)
+#define IE_XBD_NEXT(b,i) (IE_XBD_ADDR(b,i) + 2)
+#define IE_XBD_BUF(b,i) (IE_XBD_ADDR(b,i) + 4)
+
+#define IE_TBD_EOL 0x8000 /* this TBD is the last one */
+#define IE_TBD_CNTMASK 0x3fff /* The rest of the `flags' word
+ is actually the length. */
+
+
+#if 0
+/*
+ * Multicast setup command.
+ */
+struct __ie_mcast_cmd {
+ struct __ie_cmd_common com; // common part
+#define ie_mcast_status com.ie_cmd_status
+
+ // size (in bytes) of multicast addresses
+ u_short ie_mcast_bytes;
+ struct __ie_en_addr ie_mcast_addrs[IE_MAXMCAST + 1];// space for them
+};
+#endif
+#define IE_CMD_MCAST_SZ (IE_CMD_COMMON_SZ + 2 /* + XXX */)
+#define IE_CMD_MCAST_BYTES(base) ((base) + IE_CMD_COMMON_SZ + 0)
+#define IE_CMD_MCAST_MADDR(base) ((base) + IE_CMD_COMMON_SZ + 2)
+
+#if 0
+/*
+ * Time Domain Reflectometer command.
+ */
+struct __ie_tdr_cmd {
+ struct __ie_cmd_common com; // common part
+#define ie_tdr_status com.ie_cmd_status
+ u_short ie_tdr_time; // error bits and time
+};
+#endif
+#define IE_CMD_TDR_SZ (IE_CMD_COMMON_SZ + 2)
+#define IE_CMD_TDR_TIME(base) ((base) + IE_CMD_COMMON_SZ + 0)
+
+#define IE_TDR_SUCCESS 0x8000 /* TDR succeeded without error */
+#define IE_TDR_XCVR 0x4000 /* detected a transceiver problem */
+#define IE_TDR_OPEN 0x2000 /* detected an incorrect termination ("open") */
+#define IE_TDR_SHORT 0x1000 /* TDR detected a short circuit */
+#define IE_TDR_TIME 0x07ff /* mask for reflection time */
+
+#if 0
+/*
+ * Initial Address Setup command
+ */
+struct __ie_iasetup_cmd {
+ struct __ie_cmd_common com;
+#define ie_iasetup_status com.ie_cmd_status
+ struct __ie_en_addr ie_address;
+};
+#endif
+#define IE_CMD_IAS_SZ (IE_CMD_COMMON_SZ + 6)
+#define IE_CMD_IAS_EADDR(base) ((base) + IE_CMD_COMMON_SZ + 0)
+
+#if 0
+/*
+ * Configuration command
+ */
+struct __ie_config_cmd {
+ struct __ie_cmd_common com; // common part
+#define ie_config_status com.ie_cmd_status
+
+ u_int8_t ie_config_count; // byte count (0x0c)
+ u_int8_t ie_fifo; // fifo (8)
+ u_int8_t ie_save_bad; // save bad frames (0x40)
+ u_int8_t ie_addr_len; // address length (0x2e) (AL-LOC == 1)
+ u_int8_t ie_priority; // priority and backoff (0x0)
+ u_int8_t ie_ifs; // inter-frame spacing (0x60)
+ u_int8_t ie_slot_low; // slot time, LSB (0x0)
+ u_int8_t ie_slot_high; // slot time, MSN, and retries (0xf2)
+ u_int8_t ie_promisc; // 1 if promiscuous, else 0
+ u_int8_t ie_crs_cdt; // CSMA/CD parameters (0x0)
+ u_int8_t ie_min_len; // min frame length (0x40)
+ u_int8_t ie_junk; // stuff for 82596 (0xff)
+};
+#endif
+#define IE_CMD_CFG_SZ (IE_CMD_COMMON_SZ + 12)
+#define IE_CMD_CFG_CNT(base) ((base) + IE_CMD_COMMON_SZ + 0)
+#define IE_CMD_CFG_FIFO(base) ((base) + IE_CMD_COMMON_SZ + 1)
+#define IE_CMD_CFG_SAVEBAD(base) ((base) + IE_CMD_COMMON_SZ + 2)
+#define IE_CMD_CFG_ADDRLEN(base) ((base) + IE_CMD_COMMON_SZ + 3)
+#define IE_CMD_CFG_PRIORITY(base) ((base) + IE_CMD_COMMON_SZ + 4)
+#define IE_CMD_CFG_IFS(base) ((base) + IE_CMD_COMMON_SZ + 5)
+#define IE_CMD_CFG_SLOT_LOW(base) ((base) + IE_CMD_COMMON_SZ + 6)
+#define IE_CMD_CFG_SLOT_HIGH(base) ((base) + IE_CMD_COMMON_SZ + 7)
+#define IE_CMD_CFG_PROMISC(base) ((base) + IE_CMD_COMMON_SZ + 8)
+#define IE_CMD_CFG_CRSCDT(base) ((base) + IE_CMD_COMMON_SZ + 9)
+#define IE_CMD_CFG_MINLEN(base) ((base) + IE_CMD_COMMON_SZ + 10)
+#define IE_CMD_CFG_JUNK(base) ((base) + IE_CMD_COMMON_SZ + 11)
diff --git a/bsps/shared/net/if_dc.c b/bsps/shared/net/if_dc.c
new file mode 100644
index 0000000000..e822a7c533
--- /dev/null
+++ b/bsps/shared/net/if_dc.c
@@ -0,0 +1,3849 @@
+/*
+ * Ported from FreeBSD --> RTEMS, december 03.
+ * Daron Chabot <daron@nucleus.usask.ca>
+ * -- only tested with i386 bsp.
+ * -- supports *one* card (until the PCI & IRQ APIs get sorted out ;-))
+ *
+ *
+ * Copyright (c) 1997, 1998, 1999
+ * Bill Paul <wpaul@ee.columbia.edu>. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Bill Paul.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Bill Paul 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 Bill Paul OR THE VOICES IN HIS HEAD
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD: src/sys/pci/if_dc.c,v 1.9.2.41 2003/03/05 18:42:33 njl Exp $
+ */
+
+/*
+ * DEC "tulip" clone ethernet driver. Supports the DEC/Intel 21143
+ * series chips and several workalikes including the following:
+ *
+ * Macronix 98713/98715/98725/98727/98732 PMAC (www.macronix.com)
+ * Macronix/Lite-On 82c115 PNIC II (www.macronix.com)
+ * Lite-On 82c168/82c169 PNIC (www.litecom.com)
+ * ASIX Electronics AX88140A (www.asix.com.tw)
+ * ASIX Electronics AX88141 (www.asix.com.tw)
+ * ADMtek AL981 (www.admtek.com.tw)
+ * ADMtek AN985 (www.admtek.com.tw)
+ * Davicom DM9100, DM9102, DM9102A (www.davicom8.com)
+ * Accton EN1217 (www.accton.com)
+ * Conexant LANfinity (www.conexant.com)
+ *
+ * Datasheets for the 21143 are available at developer.intel.com.
+ * Datasheets for the clone parts can be found at their respective sites.
+ * (Except for the PNIC; see www.freebsd.org/~wpaul/PNIC/pnic.ps.gz.)
+ * The PNIC II is essentially a Macronix 98715A chip; the only difference
+ * worth noting is that its multicast hash table is only 128 bits wide
+ * instead of 512.
+ *
+ * Written by Bill Paul <wpaul@ee.columbia.edu>
+ * Electrical Engineering Department
+ * Columbia University, New York City
+ */
+
+/*
+ * The Intel 21143 is the successor to the DEC 21140. It is basically
+ * the same as the 21140 but with a few new features. The 21143 supports
+ * three kinds of media attachments:
+ *
+ * o MII port, for 10Mbps and 100Mbps support and NWAY
+ * autonegotiation provided by an external PHY.
+ * o SYM port, for symbol mode 100Mbps support.
+ * o 10baseT port.
+ * o AUI/BNC port.
+ *
+ * The 100Mbps SYM port and 10baseT port can be used together in
+ * combination with the internal NWAY support to create a 10/100
+ * autosensing configuration.
+ *
+ * Note that not all tulip workalikes are handled in this driver: we only
+ * deal with those which are relatively well behaved. The Winbond is
+ * handled separately due to its different register offsets and the
+ * special handling needed for its various bugs. The PNIC is handled
+ * here, but I'm not thrilled about it.
+ *
+ * All of the workalike chips use some form of MII transceiver support
+ * with the exception of the Macronix chips, which also have a SYM port.
+ * The ASIX AX88140A is also documented to have a SYM port, but all
+ * the cards I've seen use an MII transceiver, probably because the
+ * AX88140A doesn't support internal NWAY.
+ */
+
+/*
+ * This driver only supports architectures with the new style
+ * exception processing. The following checks try to keep this
+ * from being compiled on systems which can't support this driver.
+ */
+
+#define __INSIDE_RTEMS_BSD_TCPIP_STACK__
+
+#if defined(DRIVER_SUPPORTED)
+ #undef DRIVER_SUPPORTED
+#endif
+
+#if defined(__i386__)
+ #define DRIVER_SUPPORTED
+#endif
+
+#if defined(__PPC__)
+ #define DRIVER_SUPPORTED
+#endif
+
+#include <bsp.h>
+
+#if !defined(PCI_DRAM_OFFSET)
+ #undef DRIVER_SUPPORTED
+#endif
+
+#if defined(DRIVER_SUPPORTED) /* this covers the file "globally"... */
+#include <rtems/pci.h>
+
+#include <rtems/error.h>
+#include <errno.h>
+#include <rtems/rtems_bsdnet.h>
+
+#include <net/if_types.h>
+
+#include <sys/param.h>
+#include <sys/sockio.h>
+#include <sys/socket.h>
+#include <sys/mbuf.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <netinet/if_ether.h>
+#include <sys/malloc.h>
+#include <sys/systm.h>
+#include <bsp.h>
+
+/* moved to cpukit/include/rtems in CVS current ! */
+/*#include "if_media.h" */
+/*#include "pci.h" */
+#include <net/if_media.h>
+#include <rtems/pci.h>
+/*
+#include <sys/kernel.h>
+#include <sys/sysctl.h>
+*/
+
+#include <vm/vm.h> /* for vtophys */
+
+
+#define vtophys(p) (uintptr_t)(p)
+
+/*
+#include <net/if_arp.h>
+#include <net/if_vlan_var.h>
+#include <net/bpf.h>
+*/
+
+#if 0
+#include <vm/pmap.h> /* for vtophys */
+#include <machine/clock.h> /* for DELAY */
+#include <machine/bus_pio.h>
+#include <machine/bus_memio.h>
+#include <machine/bus.h>
+#include <machine/resource.h>
+#include <sys/bus.h>
+#include <sys/rman.h>
+#endif
+#include <dev/mii/mii.h>
+#if 0
+#include <dev/mii/miivar.h>
+
+#include <pci/pcireg.h>
+#include <pci/pcivar.h>
+#endif
+
+/* NOTE: use mem space mapping (for now ...)
+#define DC_USEIOSPACE
+*/
+
+#ifdef __alpha__
+#define SRM_MEDIA
+#endif
+
+#include <bsp/irq.h>
+
+
+#include <libchip/if_dcreg.h>
+
+
+#define DRIVER_PREFIX "tl"
+#define NDRIVER 1
+#define IRQ_EVENT RTEMS_EVENT_13 /* Ha ... */
+static struct dc_softc dc_softc_devs[NDRIVER];
+
+#define UNUSED
+
+#if 0
+/* "controller miibus0" required. See GENERIC if you get errors here. */
+#include "miibus_if.h"
+
+
+
+#ifndef lint
+static const char rcsid[] =
+ "$FreeBSD: src/sys/pci/if_dc.c,v 1.9.2.41 2003/03/05 18:42:33 njl Exp $";
+#endif
+
+#endif
+
+
+/*
+ * Various supported device vendors/types and their names.
+ * NOTE:
+ * -----
+ * Only the "ADMtek AN985" has been tested under RTEMS !!!
+ */
+static struct dc_type dc_devs[] = {
+ { DC_VENDORID_DEC, DC_DEVICEID_21143,
+ "Intel 21143 10/100BaseTX", 0 },
+ { DC_VENDORID_DAVICOM, DC_DEVICEID_DM9009,
+ "Davicom DM9009 10/100BaseTX", 0 },
+ { DC_VENDORID_DAVICOM, DC_DEVICEID_DM9100,
+ "Davicom DM9100 10/100BaseTX", 0 },
+ { DC_VENDORID_DAVICOM, DC_DEVICEID_DM9102,
+ "Davicom DM9102 10/100BaseTX", 0 },
+ { DC_VENDORID_DAVICOM, DC_DEVICEID_DM9102,
+ "Davicom DM9102A 10/100BaseTX", 0 },
+ { DC_VENDORID_ADMTEK, DC_DEVICEID_AL981,
+ "ADMtek AL981 10/100BaseTX", 0 },
+ { DC_VENDORID_ADMTEK, DC_DEVICEID_AN985,
+ "ADMtek AN985 10/100BaseTX", 0 },
+ { DC_VENDORID_ASIX, DC_DEVICEID_AX88140A,
+ "ASIX AX88140A 10/100BaseTX", 0 },
+ { DC_VENDORID_ASIX, DC_DEVICEID_AX88140A,
+ "ASIX AX88141 10/100BaseTX", 0 },
+ { DC_VENDORID_MX, DC_DEVICEID_98713,
+ "Macronix 98713 10/100BaseTX", 0 },
+ { DC_VENDORID_MX, DC_DEVICEID_98713,
+ "Macronix 98713A 10/100BaseTX", 0 },
+ { DC_VENDORID_CP, DC_DEVICEID_98713_CP,
+ "Compex RL100-TX 10/100BaseTX", 0 },
+ { DC_VENDORID_CP, DC_DEVICEID_98713_CP,
+ "Compex RL100-TX 10/100BaseTX", 0 },
+ { DC_VENDORID_MX, DC_DEVICEID_987x5,
+ "Macronix 98715/98715A 10/100BaseTX", 0 },
+ { DC_VENDORID_MX, DC_DEVICEID_987x5,
+ "Macronix 98715AEC-C 10/100BaseTX", 0 },
+ { DC_VENDORID_MX, DC_DEVICEID_987x5,
+ "Macronix 98725 10/100BaseTX", 0 },
+ { DC_VENDORID_MX, DC_DEVICEID_98727,
+ "Macronix 98727/98732 10/100BaseTX", 0 },
+ { DC_VENDORID_LO, DC_DEVICEID_82C115,
+ "LC82C115 PNIC II 10/100BaseTX", 0 },
+ { DC_VENDORID_LO, DC_DEVICEID_82C168,
+ "82c168 PNIC 10/100BaseTX", 0 },
+ { DC_VENDORID_LO, DC_DEVICEID_82C168,
+ "82c169 PNIC 10/100BaseTX", 0 },
+ { DC_VENDORID_ACCTON, DC_DEVICEID_EN1217,
+ "Accton EN1217 10/100BaseTX", 0 },
+ { DC_VENDORID_ACCTON, DC_DEVICEID_EN2242,
+ "Accton EN2242 MiniPCI 10/100BaseTX", 0 },
+ { DC_VENDORID_CONEXANT, DC_DEVICEID_RS7112,
+ "Conexant LANfinity MiniPCI 10/100BaseTX", 0 },
+ { 0, 0, NULL, 0 }
+};
+
+#if 0
+static int dc_probe __P((device_t));
+static int dc_attach __P((device_t));
+static int dc_detach __P((device_t));
+static int dc_suspend __P((device_t));
+static int dc_resume __P((device_t));
+static void dc_shutdown __P((device_t));
+static void dc_acpi __P((device_t));
+#endif
+
+static struct dc_type *dc_devtype(int);
+static int dc_newbuf(struct dc_softc *, int, struct mbuf *);
+static int dc_encap(struct dc_softc *, struct mbuf *,
+ u_int32_t *);
+static int dc_coal(struct dc_softc *, struct mbuf **);
+static void dc_pnic_rx_bug_war(struct dc_softc *, int);
+static int dc_rx_resync(struct dc_softc *);
+static void dc_rxeof(struct dc_softc *);
+static void dc_txeof(struct dc_softc *);
+/*static void dc_tick((void *));*/
+static void dc_tx_underrun(struct dc_softc *);
+static void dc_intr(void *);
+static void dc_daemon(void *);
+static void dc_start(struct ifnet *);
+static int dc_ioctl(struct ifnet *, ioctl_command_t, caddr_t);
+static void dc_init(void *);
+static void dc_stop(struct dc_softc *);
+static void dc_watchdog(struct ifnet *);
+#if 0
+static int dc_ifmedia_upd __P((struct ifnet *));
+static void dc_ifmedia_sts __P((struct ifnet *, struct ifmediareq *));
+#endif
+
+static void dc_delay(struct dc_softc *);
+static void dc_eeprom_idle(struct dc_softc *);
+static void dc_eeprom_putbyte(struct dc_softc *, int);
+static void dc_eeprom_getword(struct dc_softc *, int, u_int16_t *);
+static void dc_eeprom_getword_pnic(struct dc_softc *, int, u_int16_t *);
+static void dc_eeprom_width(struct dc_softc *);
+static void dc_read_eeprom(struct dc_softc *, caddr_t, int,int, int);
+
+#if 0
+static void dc_mii_writebit __P((struct dc_softc *, int));
+static int dc_mii_readbit __P((struct dc_softc *));
+static void dc_mii_sync __P((struct dc_softc *));
+static void dc_mii_send __P((struct dc_softc *, u_int32_t, int));
+static int dc_mii_readreg __P((struct dc_softc *, struct dc_mii_frame *));
+static int dc_mii_writereg __P((struct dc_softc *, struct dc_mii_frame *));
+static int dc_miibus_readreg __P((device_t, int, int));
+static int dc_miibus_writereg __P((device_t, int, int, int));
+static void dc_miibus_statchg __P((device_t));
+static void dc_miibus_mediainit __P((device_t));
+#endif
+
+static void dc_setcfg(struct dc_softc *, int);
+static u_int32_t dc_crc_le(struct dc_softc *, caddr_t);
+#ifndef UNUSED
+static u_int32_t dc_crc_be(caddr_t);
+#endif
+static void dc_setfilt_21143(struct dc_softc *);
+static void dc_setfilt_asix(struct dc_softc *);
+static void dc_setfilt_admtek(struct dc_softc *);
+static void dc_setfilt(struct dc_softc *);
+static void dc_reset(struct dc_softc *);
+static int dc_list_rx_init(struct dc_softc *);
+static int dc_list_tx_init(struct dc_softc *);
+static void dc_read_srom(struct dc_softc *, int);
+static void dc_parse_21143_srom(struct dc_softc *);
+static void dc_apply_fixup(struct dc_softc *, int);
+
+#if 0
+static void dc_decode_leaf_sia __P((struct dc_softc *,
+ struct dc_eblock_sia *));
+static void dc_decode_leaf_mii __P((struct dc_softc *,
+ struct dc_eblock_mii *));
+static void dc_decode_leaf_sym __P((struct dc_softc *,
+ struct dc_eblock_sym *));
+#endif
+
+
+#ifdef DC_USEIOSPACE
+#define DC_RES SYS_RES_IOPORT
+#define DC_RID DC_PCI_CFBIO
+#else
+#define DC_RES SYS_RES_MEMORY
+#define DC_RID DC_PCI_CFBMA
+#endif
+
+#if 0
+static device_method_t dc_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, dc_probe),
+ DEVMETHOD(device_attach, dc_attach),
+ DEVMETHOD(device_detach, dc_detach),
+ DEVMETHOD(device_suspend, dc_suspend),
+ DEVMETHOD(device_resume, dc_resume),
+ DEVMETHOD(device_shutdown, dc_shutdown),
+
+ /* bus interface */
+ DEVMETHOD(bus_print_child, bus_generic_print_child),
+ DEVMETHOD(bus_driver_added, bus_generic_driver_added),
+
+ /* MII interface */
+ DEVMETHOD(miibus_readreg, dc_miibus_readreg),
+ DEVMETHOD(miibus_writereg, dc_miibus_writereg),
+ DEVMETHOD(miibus_statchg, dc_miibus_statchg),
+ DEVMETHOD(miibus_mediainit, dc_miibus_mediainit),
+
+ { 0, 0 }
+};
+
+static driver_t dc_driver = {
+ "dc",
+ dc_methods,
+ sizeof(struct dc_softc)
+};
+
+static devclass_t dc_devclass;
+#endif
+
+
+#ifdef __i386__
+static int dc_quick=1;
+#if 0
+SYSCTL_INT(_hw, OID_AUTO, dc_quick, CTLFLAG_RW,
+ &dc_quick,0,"do not mdevget in dc driver");
+#endif
+#endif
+
+#if 0
+DRIVER_MODULE(if_dc, pci, dc_driver, dc_devclass, 0, 0);
+DRIVER_MODULE(miibus, dc, miibus_driver, miibus_devclass, 0, 0);
+#endif
+
+
+#define DC_SETBIT(sc, reg, x) \
+ CSR_WRITE_4(sc, reg, CSR_READ_4(sc, reg) | (x))
+
+#define DC_CLRBIT(sc, reg, x) \
+ CSR_WRITE_4(sc, reg, CSR_READ_4(sc, reg) & ~(x))
+
+#define SIO_SET(x) DC_SETBIT(sc, DC_SIO, (x))
+#define SIO_CLR(x) DC_CLRBIT(sc, DC_SIO, (x))
+
+
+/* XXX Fixme: rtems_bsp_delay( ) for the pc386 BSP (at least)
+ * needs work... see pc386/include/bsp.h.
+ * I have "a" solution, utilizing the 2nd i8254 timer,
+ * if anyone is interested (first timer is used for clock_tick ISR)...
+ */
+#ifdef __i386__
+extern void Wait_X_ms( unsigned int );
+#define DELAY(n) Wait_X_ms( (unsigned int)((n)/100) )
+#else
+#define DELAY(n) rtems_bsp_delay(n)
+#endif
+
+
+static void dc_delay(sc)
+ struct dc_softc *sc;
+{
+ int idx;
+
+ for (idx = (300 / 33) + 1; idx > 0; idx--)
+ CSR_READ_4(sc, DC_BUSCTL);
+}
+
+static void dc_eeprom_width(sc)
+ struct dc_softc *sc;
+{
+ int i;
+
+ /* Force EEPROM to idle state. */
+ dc_eeprom_idle(sc);
+
+ /* Enter EEPROM access mode. */
+ CSR_WRITE_4(sc, DC_SIO, DC_SIO_EESEL);
+ dc_delay(sc);
+ DC_SETBIT(sc, DC_SIO, DC_SIO_ROMCTL_READ);
+ dc_delay(sc);
+ DC_CLRBIT(sc, DC_SIO, DC_SIO_EE_CLK);
+ dc_delay(sc);
+ DC_SETBIT(sc, DC_SIO, DC_SIO_EE_CS);
+ dc_delay(sc);
+
+ for (i = 3; i--;) {
+ if (6 & (1 << i))
+ DC_SETBIT(sc, DC_SIO, DC_SIO_EE_DATAIN);
+ else
+ DC_CLRBIT(sc, DC_SIO, DC_SIO_EE_DATAIN);
+ dc_delay(sc);
+ DC_SETBIT(sc, DC_SIO, DC_SIO_EE_CLK);
+ dc_delay(sc);
+ DC_CLRBIT(sc, DC_SIO, DC_SIO_EE_CLK);
+ dc_delay(sc);
+ }
+
+ for (i = 1; i <= 12; i++) {
+ DC_SETBIT(sc, DC_SIO, DC_SIO_EE_CLK);
+ dc_delay(sc);
+ if (!(CSR_READ_4(sc, DC_SIO) & DC_SIO_EE_DATAOUT)) {
+ DC_CLRBIT(sc, DC_SIO, DC_SIO_EE_CLK);
+ dc_delay(sc);
+ break;
+ }
+ DC_CLRBIT(sc, DC_SIO, DC_SIO_EE_CLK);
+ dc_delay(sc);
+ }
+
+ /* Turn off EEPROM access mode. */
+ dc_eeprom_idle(sc);
+
+ if (i < 4 || i > 12)
+ sc->dc_romwidth = 6;
+ else
+ sc->dc_romwidth = i;
+
+ /* Enter EEPROM access mode. */
+ CSR_WRITE_4(sc, DC_SIO, DC_SIO_EESEL);
+ dc_delay(sc);
+ DC_SETBIT(sc, DC_SIO, DC_SIO_ROMCTL_READ);
+ dc_delay(sc);
+ DC_CLRBIT(sc, DC_SIO, DC_SIO_EE_CLK);
+ dc_delay(sc);
+ DC_SETBIT(sc, DC_SIO, DC_SIO_EE_CS);
+ dc_delay(sc);
+
+ /* Turn off EEPROM access mode. */
+ dc_eeprom_idle(sc);
+}
+
+static void dc_eeprom_idle(sc)
+ struct dc_softc *sc;
+{
+ register int i;
+
+ CSR_WRITE_4(sc, DC_SIO, DC_SIO_EESEL);
+ dc_delay(sc);
+ DC_SETBIT(sc, DC_SIO, DC_SIO_ROMCTL_READ);
+ dc_delay(sc);
+ DC_CLRBIT(sc, DC_SIO, DC_SIO_EE_CLK);
+ dc_delay(sc);
+ DC_SETBIT(sc, DC_SIO, DC_SIO_EE_CS);
+ dc_delay(sc);
+
+ for (i = 0; i < 25; i++) {
+ DC_CLRBIT(sc, DC_SIO, DC_SIO_EE_CLK);
+ dc_delay(sc);
+ DC_SETBIT(sc, DC_SIO, DC_SIO_EE_CLK);
+ dc_delay(sc);
+ }
+
+ DC_CLRBIT(sc, DC_SIO, DC_SIO_EE_CLK);
+ dc_delay(sc);
+ DC_CLRBIT(sc, DC_SIO, DC_SIO_EE_CS);
+ dc_delay(sc);
+ CSR_WRITE_4(sc, DC_SIO, 0x00000000);
+
+ return;
+}
+
+/*
+ * Send a read command and address to the EEPROM, check for ACK.
+ */
+static void dc_eeprom_putbyte(sc, addr)
+ struct dc_softc *sc;
+ int addr;
+{
+ register int d, i;
+
+ d = DC_EECMD_READ >> 6;
+ for (i = 3; i--; ) {
+ if (d & (1 << i))
+ DC_SETBIT(sc, DC_SIO, DC_SIO_EE_DATAIN);
+ else
+ DC_CLRBIT(sc, DC_SIO, DC_SIO_EE_DATAIN);
+ dc_delay(sc);
+ DC_SETBIT(sc, DC_SIO, DC_SIO_EE_CLK);
+ dc_delay(sc);
+ DC_CLRBIT(sc, DC_SIO, DC_SIO_EE_CLK);
+ dc_delay(sc);
+ }
+
+ /*
+ * Feed in each bit and strobe the clock.
+ */
+ for (i = sc->dc_romwidth; i--;) {
+ if (addr & (1 << i)) {
+ SIO_SET(DC_SIO_EE_DATAIN);
+ } else {
+ SIO_CLR(DC_SIO_EE_DATAIN);
+ }
+ dc_delay(sc);
+ SIO_SET(DC_SIO_EE_CLK);
+ dc_delay(sc);
+ SIO_CLR(DC_SIO_EE_CLK);
+ dc_delay(sc);
+ }
+
+ return;
+}
+
+/*
+ * Read a word of data stored in the EEPROM at address 'addr.'
+ * The PNIC 82c168/82c169 has its own non-standard way to read
+ * the EEPROM.
+ */
+static void dc_eeprom_getword_pnic(sc, addr, dest)
+ struct dc_softc *sc;
+ int addr;
+ u_int16_t *dest;
+{
+ register int i;
+ u_int32_t r;
+
+ CSR_WRITE_4(sc, DC_PN_SIOCTL, DC_PN_EEOPCODE_READ|addr);
+
+ for (i = 0; i < DC_TIMEOUT; i++) {
+ DELAY(1);
+ r = CSR_READ_4(sc, DC_SIO);
+ if (!(r & DC_PN_SIOCTL_BUSY)) {
+ *dest = (u_int16_t)(r & 0xFFFF);
+ return;
+ }
+ }
+
+ return;
+}
+
+/*
+ * Read a word of data stored in the EEPROM at address 'addr.'
+ */
+static void dc_eeprom_getword(sc, addr, dest)
+ struct dc_softc *sc;
+ int addr;
+ u_int16_t *dest;
+{
+ register int i;
+ u_int16_t word = 0;
+
+ /* Force EEPROM to idle state. */
+ dc_eeprom_idle(sc);
+
+ /* Enter EEPROM access mode. */
+ CSR_WRITE_4(sc, DC_SIO, DC_SIO_EESEL);
+ dc_delay(sc);
+ DC_SETBIT(sc, DC_SIO, DC_SIO_ROMCTL_READ);
+ dc_delay(sc);
+ DC_CLRBIT(sc, DC_SIO, DC_SIO_EE_CLK);
+ dc_delay(sc);
+ DC_SETBIT(sc, DC_SIO, DC_SIO_EE_CS);
+ dc_delay(sc);
+
+ /*
+ * Send address of word we want to read.
+ */
+ dc_eeprom_putbyte(sc, addr);
+
+ /*
+ * Start reading bits from EEPROM.
+ */
+ for (i = 0x8000; i; i >>= 1) {
+ SIO_SET(DC_SIO_EE_CLK);
+ dc_delay(sc);
+ if (CSR_READ_4(sc, DC_SIO) & DC_SIO_EE_DATAOUT)
+ word |= i;
+ dc_delay(sc);
+ SIO_CLR(DC_SIO_EE_CLK);
+ dc_delay(sc);
+ }
+
+ /* Turn off EEPROM access mode. */
+ dc_eeprom_idle(sc);
+
+ *dest = word;
+
+ return;
+}
+
+/*
+ * Read a sequence of words from the EEPROM.
+ */
+static void dc_read_eeprom(sc, dest, off, cnt, swap)
+ struct dc_softc *sc;
+ caddr_t dest;
+ int off;
+ int cnt;
+ int swap;
+{
+ int i;
+ u_int16_t word = 0, *ptr;
+
+ for (i = 0; i < cnt; i++) {
+ if (DC_IS_PNIC(sc))
+ dc_eeprom_getword_pnic(sc, off + i, &word);
+ else
+ dc_eeprom_getword(sc, off + i, &word);
+ ptr = (u_int16_t *)(dest + (i * 2));
+ if (swap)
+ *ptr = ntohs(word);
+ else
+ *ptr = word;
+ }
+
+ return;
+}
+
+
+#if 0
+/*
+ * The following two routines are taken from the Macronix 98713
+ * Application Notes pp.19-21.
+ */
+/*
+ * Write a bit to the MII bus.
+ */
+static void dc_mii_writebit(sc, bit)
+ struct dc_softc *sc;
+ int bit;
+{
+ if (bit)
+ CSR_WRITE_4(sc, DC_SIO,
+ DC_SIO_ROMCTL_WRITE|DC_SIO_MII_DATAOUT);
+ else
+ CSR_WRITE_4(sc, DC_SIO, DC_SIO_ROMCTL_WRITE);
+
+ DC_SETBIT(sc, DC_SIO, DC_SIO_MII_CLK);
+ DC_CLRBIT(sc, DC_SIO, DC_SIO_MII_CLK);
+
+ return;
+}
+
+/*
+ * Read a bit from the MII bus.
+ */
+static int dc_mii_readbit(sc)
+ struct dc_softc *sc;
+{
+ CSR_WRITE_4(sc, DC_SIO, DC_SIO_ROMCTL_READ|DC_SIO_MII_DIR);
+ CSR_READ_4(sc, DC_SIO);
+ DC_SETBIT(sc, DC_SIO, DC_SIO_MII_CLK);
+ DC_CLRBIT(sc, DC_SIO, DC_SIO_MII_CLK);
+ if (CSR_READ_4(sc, DC_SIO) & DC_SIO_MII_DATAIN)
+ return(1);
+
+ return(0);
+}
+
+/*
+ * Sync the PHYs by setting data bit and strobing the clock 32 times.
+ */
+static void dc_mii_sync(sc)
+ struct dc_softc *sc;
+{
+ register int i;
+
+ CSR_WRITE_4(sc, DC_SIO, DC_SIO_ROMCTL_WRITE);
+
+ for (i = 0; i < 32; i++)
+ dc_mii_writebit(sc, 1);
+
+ return;
+}
+
+/*
+ * Clock a series of bits through the MII.
+ */
+static void dc_mii_send(sc, bits, cnt)
+ struct dc_softc *sc;
+ u_int32_t bits;
+ int cnt;
+{
+ int i;
+
+ for (i = (0x1 << (cnt - 1)); i; i >>= 1)
+ dc_mii_writebit(sc, bits & i);
+}
+
+/*
+ * Read an PHY register through the MII.
+ */
+static int dc_mii_readreg(sc, frame)
+ struct dc_softc *sc;
+ struct dc_mii_frame *frame;
+
+{
+ int i, ack, s;
+
+
+ /*
+ * Set up frame for RX.
+ */
+ frame->mii_stdelim = DC_MII_STARTDELIM;
+ frame->mii_opcode = DC_MII_READOP;
+ frame->mii_turnaround = 0;
+ frame->mii_data = 0;
+
+ /*
+ * Sync the PHYs.
+ */
+ dc_mii_sync(sc);
+
+ /*
+ * Send command/address info.
+ */
+ dc_mii_send(sc, frame->mii_stdelim, 2);
+ dc_mii_send(sc, frame->mii_opcode, 2);
+ dc_mii_send(sc, frame->mii_phyaddr, 5);
+ dc_mii_send(sc, frame->mii_regaddr, 5);
+
+#ifdef notdef
+ /* Idle bit */
+ dc_mii_writebit(sc, 1);
+ dc_mii_writebit(sc, 0);
+#endif
+
+ /* Check for ack */
+ ack = dc_mii_readbit(sc);
+
+ /*
+ * Now try reading data bits. If the ack failed, we still
+ * need to clock through 16 cycles to keep the PHY(s) in sync.
+ */
+ if (ack) {
+ for(i = 0; i < 16; i++) {
+ dc_mii_readbit(sc);
+ }
+ goto fail;
+ }
+
+ for (i = 0x8000; i; i >>= 1) {
+ if (!ack) {
+ if (dc_mii_readbit(sc))
+ frame->mii_data |= i;
+ }
+ }
+
+fail:
+
+ dc_mii_writebit(sc, 0);
+ dc_mii_writebit(sc, 0);
+
+
+ if (ack)
+ return(1);
+ return(0);
+}
+
+/*
+ * Write to a PHY register through the MII.
+ */
+static int dc_mii_writereg(sc, frame)
+ struct dc_softc *sc;
+ struct dc_mii_frame *frame;
+
+{
+ int s;
+
+ /*
+ * Set up frame for TX.
+ */
+
+ frame->mii_stdelim = DC_MII_STARTDELIM;
+ frame->mii_opcode = DC_MII_WRITEOP;
+ frame->mii_turnaround = DC_MII_TURNAROUND;
+
+ /*
+ * Sync the PHYs.
+ */
+ dc_mii_sync(sc);
+
+ dc_mii_send(sc, frame->mii_stdelim, 2);
+ dc_mii_send(sc, frame->mii_opcode, 2);
+ dc_mii_send(sc, frame->mii_phyaddr, 5);
+ dc_mii_send(sc, frame->mii_regaddr, 5);
+ dc_mii_send(sc, frame->mii_turnaround, 2);
+ dc_mii_send(sc, frame->mii_data, 16);
+
+ /* Idle bit. */
+ dc_mii_writebit(sc, 0);
+ dc_mii_writebit(sc, 0);
+
+
+ return(0);
+}
+
+static int dc_miibus_readreg(dev, phy, reg)
+ device_t dev;
+ int phy, reg;
+{
+ struct dc_mii_frame frame;
+ struct dc_softc *sc;
+ int i, rval, phy_reg = 0;
+
+ sc = device_get_softc(dev);
+ bzero((char *)&frame, sizeof(frame));
+
+ /*
+ * Note: both the AL981 and AN985 have internal PHYs,
+ * however the AL981 provides direct access to the PHY
+ * registers while the AN985 uses a serial MII interface.
+ * The AN985's MII interface is also buggy in that you
+ * can read from any MII address (0 to 31), but only address 1
+ * behaves normally. To deal with both cases, we pretend
+ * that the PHY is at MII address 1.
+ */
+ if (DC_IS_ADMTEK(sc) && phy != DC_ADMTEK_PHYADDR)
+ return(0);
+
+ /*
+ * Note: the ukphy probes of the RS7112 report a PHY at
+ * MII address 0 (possibly HomePNA?) and 1 (ethernet)
+ * so we only respond to correct one.
+ */
+ if (DC_IS_CONEXANT(sc) && phy != DC_CONEXANT_PHYADDR)
+ return(0);
+
+ if (sc->dc_pmode != DC_PMODE_MII) {
+ if (phy == (MII_NPHY - 1)) {
+ switch(reg) {
+ case MII_BMSR:
+ /*
+ * Fake something to make the probe
+ * code think there's a PHY here.
+ */
+ return(BMSR_MEDIAMASK);
+ break;
+ case MII_PHYIDR1:
+ if (DC_IS_PNIC(sc))
+ return(DC_VENDORID_LO);
+ return(DC_VENDORID_DEC);
+ break;
+ case MII_PHYIDR2:
+ if (DC_IS_PNIC(sc))
+ return(DC_DEVICEID_82C168);
+ return(DC_DEVICEID_21143);
+ break;
+ default:
+ return(0);
+ break;
+ }
+ } else
+ return(0);
+ }
+
+ if (DC_IS_PNIC(sc)) {
+ CSR_WRITE_4(sc, DC_PN_MII, DC_PN_MIIOPCODE_READ |
+ (phy << 23) | (reg << 18));
+ for (i = 0; i < DC_TIMEOUT; i++) {
+ DELAY(1);
+ rval = CSR_READ_4(sc, DC_PN_MII);
+ if (!(rval & DC_PN_MII_BUSY)) {
+ rval &= 0xFFFF;
+ return(rval == 0xFFFF ? 0 : rval);
+ }
+ }
+ return(0);
+ }
+
+ if (DC_IS_COMET(sc)) {
+ switch(reg) {
+ case MII_BMCR:
+ phy_reg = DC_AL_BMCR;
+ break;
+ case MII_BMSR:
+ phy_reg = DC_AL_BMSR;
+ break;
+ case MII_PHYIDR1:
+ phy_reg = DC_AL_VENID;
+ break;
+ case MII_PHYIDR2:
+ phy_reg = DC_AL_DEVID;
+ break;
+ case MII_ANAR:
+ phy_reg = DC_AL_ANAR;
+ break;
+ case MII_ANLPAR:
+ phy_reg = DC_AL_LPAR;
+ break;
+ case MII_ANER:
+ phy_reg = DC_AL_ANER;
+ break;
+ default:
+ printk("dc%d: phy_read: bad phy register %x\n",
+ sc->dc_unit, reg);
+ return(0);
+ break;
+ }
+
+ rval = CSR_READ_4(sc, phy_reg) & 0x0000FFFF;
+
+ if (rval == 0xFFFF)
+ return(0);
+ return(rval);
+ }
+
+ frame.mii_phyaddr = phy;
+ frame.mii_regaddr = reg;
+ if (sc->dc_type == DC_TYPE_98713) {
+ phy_reg = CSR_READ_4(sc, DC_NETCFG);
+ CSR_WRITE_4(sc, DC_NETCFG, phy_reg & ~DC_NETCFG_PORTSEL);
+ }
+ dc_mii_readreg(sc, &frame);
+ if (sc->dc_type == DC_TYPE_98713)
+ CSR_WRITE_4(sc, DC_NETCFG, phy_reg);
+
+ return(frame.mii_data);
+}
+
+static int dc_miibus_writereg(dev, phy, reg, data)
+ device_t dev;
+ int phy, reg, data;
+{
+ struct dc_softc *sc;
+ struct dc_mii_frame frame;
+ int i, phy_reg = 0;
+
+ sc = device_get_softc(dev);
+ bzero((char *)&frame, sizeof(frame));
+
+ if (DC_IS_ADMTEK(sc) && phy != DC_ADMTEK_PHYADDR)
+ return(0);
+
+ if (DC_IS_CONEXANT(sc) && phy != DC_CONEXANT_PHYADDR)
+ return(0);
+
+ if (DC_IS_PNIC(sc)) {
+ CSR_WRITE_4(sc, DC_PN_MII, DC_PN_MIIOPCODE_WRITE |
+ (phy << 23) | (reg << 10) | data);
+ for (i = 0; i < DC_TIMEOUT; i++) {
+ if (!(CSR_READ_4(sc, DC_PN_MII) & DC_PN_MII_BUSY))
+ break;
+ }
+ return(0);
+ }
+
+ if (DC_IS_COMET(sc)) {
+ switch(reg) {
+ case MII_BMCR:
+ phy_reg = DC_AL_BMCR;
+ break;
+ case MII_BMSR:
+ phy_reg = DC_AL_BMSR;
+ break;
+ case MII_PHYIDR1:
+ phy_reg = DC_AL_VENID;
+ break;
+ case MII_PHYIDR2:
+ phy_reg = DC_AL_DEVID;
+ break;
+ case MII_ANAR:
+ phy_reg = DC_AL_ANAR;
+ break;
+ case MII_ANLPAR:
+ phy_reg = DC_AL_LPAR;
+ break;
+ case MII_ANER:
+ phy_reg = DC_AL_ANER;
+ break;
+ default:
+ printk("dc%d: phy_write: bad phy register %x\n",
+ sc->dc_unit, reg);
+ return(0);
+ break;
+ }
+
+ CSR_WRITE_4(sc, phy_reg, data);
+ return(0);
+ }
+
+ frame.mii_phyaddr = phy;
+ frame.mii_regaddr = reg;
+ frame.mii_data = data;
+
+ if (sc->dc_type == DC_TYPE_98713) {
+ phy_reg = CSR_READ_4(sc, DC_NETCFG);
+ CSR_WRITE_4(sc, DC_NETCFG, phy_reg & ~DC_NETCFG_PORTSEL);
+ }
+ dc_mii_writereg(sc, &frame);
+ if (sc->dc_type == DC_TYPE_98713)
+ CSR_WRITE_4(sc, DC_NETCFG, phy_reg);
+
+ return(0);
+}
+
+static void dc_miibus_statchg(dev)
+ device_t dev;
+{
+ struct dc_softc *sc;
+ struct mii_data *mii;
+ struct ifmedia *ifm;
+
+ sc = device_get_softc(dev);
+ if (DC_IS_ADMTEK(sc))
+ return;
+
+ mii = device_get_softc(sc->dc_miibus);
+ ifm = &mii->mii_media;
+ if (DC_IS_DAVICOM(sc) &&
+ IFM_SUBTYPE(ifm->ifm_media) == IFM_homePNA) {
+ dc_setcfg(sc, ifm->ifm_media);
+ sc->dc_if_media = ifm->ifm_media;
+ } else {
+ dc_setcfg(sc, mii->mii_media_active);
+ sc->dc_if_media = mii->mii_media_active;
+ }
+
+ return;
+}
+
+/*
+ * Special support for DM9102A cards with HomePNA PHYs. Note:
+ * with the Davicom DM9102A/DM9801 eval board that I have, it seems
+ * to be impossible to talk to the management interface of the DM9801
+ * PHY (its MDIO pin is not connected to anything). Consequently,
+ * the driver has to just 'know' about the additional mode and deal
+ * with it itself. *sigh*
+ */
+static void dc_miibus_mediainit(dev)
+ device_t dev;
+{
+ struct dc_softc *sc;
+ struct mii_data *mii;
+ struct ifmedia *ifm;
+ int rev;
+
+ rev = pci_read_config(dev, DC_PCI_CFRV, 4) & 0xFF;
+
+ sc = device_get_softc(dev);
+ mii = device_get_softc(sc->dc_miibus);
+ ifm = &mii->mii_media;
+
+ if (DC_IS_DAVICOM(sc) && rev >= DC_REVISION_DM9102A)
+ ifmedia_add(ifm, IFM_ETHER|IFM_homePNA, 0, NULL);
+
+ return;
+}
+#endif
+
+#define DC_POLY 0xEDB88320
+#define DC_BITS_512 9
+#define DC_BITS_128 7
+#define DC_BITS_64 6
+
+static u_int32_t dc_crc_le(sc, addr)
+ struct dc_softc *sc;
+ caddr_t addr;
+{
+ u_int32_t idx, bit, data, crc;
+
+ /* Compute CRC for the address value. */
+ crc = 0xFFFFFFFF; /* initial value */
+
+ for (idx = 0; idx < 6; idx++) {
+ for (data = *addr++, bit = 0; bit < 8; bit++, data >>= 1)
+ crc = (crc >> 1) ^ (((crc ^ data) & 1) ? DC_POLY : 0);
+ }
+
+ /*
+ * The hash table on the PNIC II and the MX98715AEC-C/D/E
+ * chips is only 128 bits wide.
+ */
+ if (sc->dc_flags & DC_128BIT_HASH)
+ return (crc & ((1 << DC_BITS_128) - 1));
+
+ /* The hash table on the MX98715BEC is only 64 bits wide. */
+ if (sc->dc_flags & DC_64BIT_HASH)
+ return (crc & ((1 << DC_BITS_64) - 1));
+
+ return (crc & ((1 << DC_BITS_512) - 1));
+}
+
+#ifndef UNUSED
+/*
+ * Calculate CRC of a multicast group address, return the lower 6 bits.
+ */
+static u_int32_t dc_crc_be(addr)
+ caddr_t addr;
+{
+ u_int32_t crc, carry;
+ int i, j;
+ u_int8_t c;
+
+ /* Compute CRC for the address value. */
+ crc = 0xFFFFFFFF; /* initial value */
+
+ for (i = 0; i < 6; i++) {
+ c = *(addr + i);
+ for (j = 0; j < 8; j++) {
+ carry = ((crc & 0x80000000) ? 1 : 0) ^ (c & 0x01);
+ crc <<= 1;
+ c >>= 1;
+ if (carry)
+ crc = (crc ^ 0x04c11db6) | carry;
+ }
+ }
+
+ /* return the filter bit position */
+ return((crc >> 26) & 0x0000003F);
+}
+#endif
+
+/*
+ * 21143-style RX filter setup routine. Filter programming is done by
+ * downloading a special setup frame into the TX engine. 21143, Macronix,
+ * PNIC, PNIC II and Davicom chips are programmed this way.
+ *
+ * We always program the chip using 'hash perfect' mode, i.e. one perfect
+ * address (our node address) and a 512-bit hash filter for multicast
+ * frames. We also sneak the broadcast address into the hash filter since
+ * we need that too.
+ */
+void dc_setfilt_21143(sc)
+ struct dc_softc *sc;
+{
+ struct dc_desc *sframe;
+ u_int32_t h, *sp;
+ /*struct ifmultiaddr *ifma;*/
+ struct ifnet *ifp;
+ int i;
+ u_int16_t *ac_enaddr;
+
+ ifp = &sc->arpcom.ac_if;
+
+ i = sc->dc_cdata.dc_tx_prod;
+ DC_INC(sc->dc_cdata.dc_tx_prod, DC_TX_LIST_CNT);
+ sc->dc_cdata.dc_tx_cnt++;
+ sframe = &sc->dc_ldata->dc_tx_list[i];
+ sp = (u_int32_t *)&sc->dc_cdata.dc_sbuf;
+ bzero((char *)sp, DC_SFRAME_LEN);
+
+ sframe->dc_data = vtophys(&sc->dc_cdata.dc_sbuf);
+ sframe->dc_ctl = DC_SFRAME_LEN | DC_TXCTL_SETUP | DC_TXCTL_TLINK |
+ DC_FILTER_HASHPERF | DC_TXCTL_FINT;
+
+ sc->dc_cdata.dc_tx_chain[i] = (struct mbuf *)&sc->dc_cdata.dc_sbuf;
+
+ /* If we want promiscuous mode, set the allframes bit. */
+ if (ifp->if_flags & IFF_PROMISC)
+ DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_RX_PROMISC);
+ else
+ DC_CLRBIT(sc, DC_NETCFG, DC_NETCFG_RX_PROMISC);
+
+ if (ifp->if_flags & IFF_ALLMULTI)
+ DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_RX_ALLMULTI);
+ else
+ DC_CLRBIT(sc, DC_NETCFG, DC_NETCFG_RX_ALLMULTI);
+#if 0
+ for (ifma = ifp->if_multiaddrs.lh_first; ifma != NULL;
+ ifma = ifma->ifma_link.le_next) {
+ if (ifma->ifma_addr->sa_family != AF_LINK)
+ continue;
+ h = dc_crc_le(sc,
+ LLADDR((struct sockaddr_dl *)ifma->ifma_addr));
+ sp[h >> 4] |= 1 << (h & 0xF);
+ }
+#endif
+
+ if (ifp->if_flags & IFF_BROADCAST) {
+ h = dc_crc_le(sc, (caddr_t)&etherbroadcastaddr);
+ sp[h >> 4] |= 1 << (h & 0xF);
+ }
+
+ /* Set our MAC address */
+ ac_enaddr = (u_int16_t *)sc->arpcom.ac_enaddr;
+ sp[39] = ac_enaddr[0];
+ sp[40] = ac_enaddr[1];
+ sp[41] = ac_enaddr[2];
+
+ sframe->dc_status = DC_TXSTAT_OWN;
+ CSR_WRITE_4(sc, DC_TXSTART, 0xFFFFFFFF);
+
+ /*
+ * The PNIC takes an exceedingly long time to process its
+ * setup frame; wait 10ms after posting the setup frame
+ * before proceeding, just so it has time to swallow its
+ * medicine.
+ */
+ DELAY(10000);
+
+ ifp->if_timer = 5;
+
+ return;
+}
+
+void dc_setfilt_admtek(sc)
+ struct dc_softc *sc;
+{
+ struct ifnet *ifp;
+#if 0
+ int h = 0;
+ u_int32_t hashes[2] = { 0, 0 };
+ struct ifmultiaddr *ifma;
+#endif
+ u_int32_t *ac_enaddr;
+
+ ifp = &sc->arpcom.ac_if;
+
+ /* Init our MAC address */
+ ac_enaddr = (u_int32_t *)&sc->arpcom.ac_enaddr[0];
+ CSR_WRITE_4(sc, DC_AL_PAR0, *ac_enaddr);
+ ac_enaddr = (u_int32_t *)&sc->arpcom.ac_enaddr[4];
+ CSR_WRITE_4(sc, DC_AL_PAR1, *ac_enaddr);
+
+ /* If we want promiscuous mode, set the allframes bit. */
+ if (ifp->if_flags & IFF_PROMISC)
+ DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_RX_PROMISC);
+ else
+ DC_CLRBIT(sc, DC_NETCFG, DC_NETCFG_RX_PROMISC);
+
+ if (ifp->if_flags & IFF_ALLMULTI)
+ DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_RX_ALLMULTI);
+ else
+ DC_CLRBIT(sc, DC_NETCFG, DC_NETCFG_RX_ALLMULTI);
+
+ /* first, zot all the existing hash bits */
+ CSR_WRITE_4(sc, DC_AL_MAR0, 0);
+ CSR_WRITE_4(sc, DC_AL_MAR1, 0);
+
+#if 0
+ /*
+ * If we're already in promisc or allmulti mode, we
+ * don't have to bother programming the multicast filter.
+ */
+ if (ifp->if_flags & (IFF_PROMISC|IFF_ALLMULTI))
+ return;
+
+ /* now program new ones */
+ for (ifma = ifp->if_multiaddrs.lh_first; ifma != NULL;
+ ifma = ifma->ifma_link.le_next) {
+ if (ifma->ifma_addr->sa_family != AF_LINK)
+ continue;
+ h = dc_crc_be(LLADDR((struct sockaddr_dl *)ifma->ifma_addr));
+ if (h < 32)
+ hashes[0] |= (1 << h);
+ else
+ hashes[1] |= (1 << (h - 32));
+ }
+
+ CSR_WRITE_4(sc, DC_AL_MAR0, hashes[0]);
+ CSR_WRITE_4(sc, DC_AL_MAR1, hashes[1]);
+#endif
+ return;
+}
+
+void dc_setfilt_asix(sc)
+ struct dc_softc *sc;
+{
+ struct ifnet *ifp;
+#if 0
+ int h = 0;
+ u_int32_t hashes[2] = { 0, 0 };
+ struct ifmultiaddr *ifma;
+#endif
+ u_int32_t *ac_enaddr;
+
+ ifp = &sc->arpcom.ac_if;
+
+ /* Init our MAC address */
+ CSR_WRITE_4(sc, DC_AX_FILTIDX, DC_AX_FILTIDX_PAR0);
+ ac_enaddr = (u_int32_t *)&sc->arpcom.ac_enaddr[0];
+ CSR_WRITE_4(sc, DC_AX_FILTDATA, *ac_enaddr);
+
+ CSR_WRITE_4(sc, DC_AX_FILTIDX, DC_AX_FILTIDX_PAR1);
+
+ ac_enaddr = (u_int32_t *)&sc->arpcom.ac_enaddr[4];
+ CSR_WRITE_4(sc, DC_AX_FILTDATA, *ac_enaddr);
+
+ /* If we want promiscuous mode, set the allframes bit. */
+ if (ifp->if_flags & IFF_PROMISC)
+ DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_RX_PROMISC);
+ else
+ DC_CLRBIT(sc, DC_NETCFG, DC_NETCFG_RX_PROMISC);
+
+ if (ifp->if_flags & IFF_ALLMULTI)
+ DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_RX_ALLMULTI);
+ else
+ DC_CLRBIT(sc, DC_NETCFG, DC_NETCFG_RX_ALLMULTI);
+
+ /*
+ * The ASIX chip has a special bit to enable reception
+ * of broadcast frames.
+ */
+ if (ifp->if_flags & IFF_BROADCAST)
+ DC_SETBIT(sc, DC_NETCFG, DC_AX_NETCFG_RX_BROAD);
+ else
+ DC_CLRBIT(sc, DC_NETCFG, DC_AX_NETCFG_RX_BROAD);
+
+ /* first, zot all the existing hash bits */
+ CSR_WRITE_4(sc, DC_AX_FILTIDX, DC_AX_FILTIDX_MAR0);
+ CSR_WRITE_4(sc, DC_AX_FILTDATA, 0);
+ CSR_WRITE_4(sc, DC_AX_FILTIDX, DC_AX_FILTIDX_MAR1);
+ CSR_WRITE_4(sc, DC_AX_FILTDATA, 0);
+
+#if 0
+ /*
+ * If we're already in promisc or allmulti mode, we
+ * don't have to bother programming the multicast filter.
+ */
+ if (ifp->if_flags & (IFF_PROMISC|IFF_ALLMULTI))
+ return;
+
+ /* now program new ones */
+ for (ifma = ifp->if_multiaddrs.lh_first; ifma != NULL;
+ ifma = ifma->ifma_link.le_next) {
+ if (ifma->ifma_addr->sa_family != AF_LINK)
+ continue;
+ h = dc_crc_be(LLADDR((struct sockaddr_dl *)ifma->ifma_addr));
+ if (h < 32)
+ hashes[0] |= (1 << h);
+ else
+ hashes[1] |= (1 << (h - 32));
+ }
+
+ CSR_WRITE_4(sc, DC_AX_FILTIDX, DC_AX_FILTIDX_MAR0);
+ CSR_WRITE_4(sc, DC_AX_FILTDATA, hashes[0]);
+ CSR_WRITE_4(sc, DC_AX_FILTIDX, DC_AX_FILTIDX_MAR1);
+ CSR_WRITE_4(sc, DC_AX_FILTDATA, hashes[1]);
+#endif
+ return;
+}
+
+static void dc_setfilt(sc)
+ struct dc_softc *sc;
+{
+ if (DC_IS_INTEL(sc) || DC_IS_MACRONIX(sc) || DC_IS_PNIC(sc) ||
+ DC_IS_PNICII(sc) || DC_IS_DAVICOM(sc) || DC_IS_CONEXANT(sc))
+ dc_setfilt_21143(sc);
+
+ if (DC_IS_ASIX(sc))
+ dc_setfilt_asix(sc);
+
+ if (DC_IS_ADMTEK(sc))
+ dc_setfilt_admtek(sc);
+
+ return;
+}
+
+/*
+ * In order to fiddle with the
+ * 'full-duplex' and '100Mbps' bits in the netconfig register, we
+ * first have to put the transmit and/or receive logic in the idle state.
+ */
+static void dc_setcfg(sc, media)
+ struct dc_softc *sc;
+ int media;
+{
+ int i, restart = 0;
+ u_int32_t isr;
+
+ if (IFM_SUBTYPE(media) == IFM_NONE)
+ return;
+
+ if (CSR_READ_4(sc, DC_NETCFG) & (DC_NETCFG_TX_ON|DC_NETCFG_RX_ON)) {
+ restart = 1;
+ DC_CLRBIT(sc, DC_NETCFG, (DC_NETCFG_TX_ON|DC_NETCFG_RX_ON));
+
+ for (i = 0; i < DC_TIMEOUT; i++) {
+ isr = CSR_READ_4(sc, DC_ISR);
+ if (isr & DC_ISR_TX_IDLE ||
+ (isr & DC_ISR_RX_STATE) == DC_RXSTATE_STOPPED)
+ break;
+ DELAY(10);
+ }
+
+ if (i == DC_TIMEOUT)
+ printk("dc%d: failed to force tx and "
+ "rx to idle state\n", sc->dc_unit);
+ }
+
+ if (IFM_SUBTYPE(media) == IFM_100_TX) {
+ DC_CLRBIT(sc, DC_NETCFG, DC_NETCFG_SPEEDSEL);
+ DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_HEARTBEAT);
+ if (sc->dc_pmode == DC_PMODE_MII) {
+ int watchdogreg;
+
+ if (DC_IS_INTEL(sc)) {
+ /* there's a write enable bit here that reads as 1 */
+ watchdogreg = CSR_READ_4(sc, DC_WATCHDOG);
+ watchdogreg &= ~DC_WDOG_CTLWREN;
+ watchdogreg |= DC_WDOG_JABBERDIS;
+ CSR_WRITE_4(sc, DC_WATCHDOG, watchdogreg);
+ } else {
+ DC_SETBIT(sc, DC_WATCHDOG, DC_WDOG_JABBERDIS);
+ }
+ DC_CLRBIT(sc, DC_NETCFG, (DC_NETCFG_PCS|
+ DC_NETCFG_PORTSEL|DC_NETCFG_SCRAMBLER));
+ if (sc->dc_type == DC_TYPE_98713)
+ DC_SETBIT(sc, DC_NETCFG, (DC_NETCFG_PCS|
+ DC_NETCFG_SCRAMBLER));
+ if (!DC_IS_DAVICOM(sc))
+ DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_PORTSEL);
+ DC_CLRBIT(sc, DC_10BTCTRL, 0xFFFF);
+ if (DC_IS_INTEL(sc))
+ dc_apply_fixup(sc, IFM_AUTO);
+ } else {
+ if (DC_IS_PNIC(sc)) {
+ DC_PN_GPIO_SETBIT(sc, DC_PN_GPIO_SPEEDSEL);
+ DC_PN_GPIO_SETBIT(sc, DC_PN_GPIO_100TX_LOOP);
+ DC_SETBIT(sc, DC_PN_NWAY, DC_PN_NWAY_SPEEDSEL);
+ }
+ DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_PORTSEL);
+ DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_PCS);
+ DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_SCRAMBLER);
+ if (DC_IS_INTEL(sc))
+ dc_apply_fixup(sc,
+ (media & IFM_GMASK) == IFM_FDX ?
+ IFM_100_TX|IFM_FDX : IFM_100_TX);
+ }
+ }
+
+ if (IFM_SUBTYPE(media) == IFM_10_T) {
+ DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_SPEEDSEL);
+ DC_CLRBIT(sc, DC_NETCFG, DC_NETCFG_HEARTBEAT);
+ if (sc->dc_pmode == DC_PMODE_MII) {
+ int watchdogreg;
+
+ /* there's a write enable bit here that reads as 1 */
+ if (DC_IS_INTEL(sc)) {
+ watchdogreg = CSR_READ_4(sc, DC_WATCHDOG);
+ watchdogreg &= ~DC_WDOG_CTLWREN;
+ watchdogreg |= DC_WDOG_JABBERDIS;
+ CSR_WRITE_4(sc, DC_WATCHDOG, watchdogreg);
+ } else {
+ DC_SETBIT(sc, DC_WATCHDOG, DC_WDOG_JABBERDIS);
+ }
+ DC_CLRBIT(sc, DC_NETCFG, (DC_NETCFG_PCS|
+ DC_NETCFG_PORTSEL|DC_NETCFG_SCRAMBLER));
+ if (sc->dc_type == DC_TYPE_98713)
+ DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_PCS);
+ if (!DC_IS_DAVICOM(sc))
+ DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_PORTSEL);
+ DC_CLRBIT(sc, DC_10BTCTRL, 0xFFFF);
+ if (DC_IS_INTEL(sc))
+ dc_apply_fixup(sc, IFM_AUTO);
+ } else {
+ if (DC_IS_PNIC(sc)) {
+ DC_PN_GPIO_CLRBIT(sc, DC_PN_GPIO_SPEEDSEL);
+ DC_PN_GPIO_SETBIT(sc, DC_PN_GPIO_100TX_LOOP);
+ DC_CLRBIT(sc, DC_PN_NWAY, DC_PN_NWAY_SPEEDSEL);
+ }
+ DC_CLRBIT(sc, DC_NETCFG, DC_NETCFG_PORTSEL);
+ DC_CLRBIT(sc, DC_NETCFG, DC_NETCFG_PCS);
+ DC_CLRBIT(sc, DC_NETCFG, DC_NETCFG_SCRAMBLER);
+ if (DC_IS_INTEL(sc)) {
+ DC_CLRBIT(sc, DC_SIARESET, DC_SIA_RESET);
+ DC_CLRBIT(sc, DC_10BTCTRL, 0xFFFF);
+ if ((media & IFM_GMASK) == IFM_FDX)
+ DC_SETBIT(sc, DC_10BTCTRL, 0x7F3D);
+ else
+ DC_SETBIT(sc, DC_10BTCTRL, 0x7F3F);
+ DC_SETBIT(sc, DC_SIARESET, DC_SIA_RESET);
+ DC_CLRBIT(sc, DC_10BTCTRL,
+ DC_TCTL_AUTONEGENBL);
+ dc_apply_fixup(sc,
+ (media & IFM_GMASK) == IFM_FDX ?
+ IFM_10_T|IFM_FDX : IFM_10_T);
+ DELAY(20000);
+ }
+ }
+ }
+
+#if 0
+ /*
+ * If this is a Davicom DM9102A card with a DM9801 HomePNA
+ * PHY and we want HomePNA mode, set the portsel bit to turn
+ * on the external MII port.
+ */
+ if (DC_IS_DAVICOM(sc)) {
+ if (IFM_SUBTYPE(media) == IFM_homePNA) {
+ DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_PORTSEL);
+ sc->dc_link = 1;
+ } else {
+ DC_CLRBIT(sc, DC_NETCFG, DC_NETCFG_PORTSEL);
+ }
+ }
+#endif
+
+ if ((media & IFM_GMASK) == IFM_FDX) {
+ DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_FULLDUPLEX);
+ if (sc->dc_pmode == DC_PMODE_SYM && DC_IS_PNIC(sc))
+ DC_SETBIT(sc, DC_PN_NWAY, DC_PN_NWAY_DUPLEX);
+ } else {
+ DC_CLRBIT(sc, DC_NETCFG, DC_NETCFG_FULLDUPLEX);
+ if (sc->dc_pmode == DC_PMODE_SYM && DC_IS_PNIC(sc))
+ DC_CLRBIT(sc, DC_PN_NWAY, DC_PN_NWAY_DUPLEX);
+ }
+
+ if (restart)
+ DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_TX_ON|DC_NETCFG_RX_ON);
+
+ return;
+}
+
+static void dc_reset(sc)
+ struct dc_softc *sc;
+{
+ register int i;
+
+ DC_SETBIT(sc, DC_BUSCTL, DC_BUSCTL_RESET);
+
+ for (i = 0; i < DC_TIMEOUT; i++) {
+ DELAY(10);
+ if (!(CSR_READ_4(sc, DC_BUSCTL) & DC_BUSCTL_RESET))
+ break;
+ }
+
+ if (DC_IS_ASIX(sc) || DC_IS_ADMTEK(sc) || DC_IS_CONEXANT(sc)) {
+ DELAY(10000);
+ DC_CLRBIT(sc, DC_BUSCTL, DC_BUSCTL_RESET);
+ i = 0;
+ }
+
+ if (i == DC_TIMEOUT)
+ printk("dc%d: reset never completed!\n", sc->dc_unit);
+
+ /* Wait a little while for the chip to get its brains in order. */
+ DELAY(1000);
+
+ CSR_WRITE_4(sc, DC_IMR, 0x00000000);
+ CSR_WRITE_4(sc, DC_BUSCTL, 0x00000000);
+ CSR_WRITE_4(sc, DC_NETCFG, 0x00000000);
+
+ /*
+ * Bring the SIA out of reset. In some cases, it looks
+ * like failing to unreset the SIA soon enough gets it
+ * into a state where it will never come out of reset
+ * until we reset the whole chip again.
+ */
+ if (DC_IS_INTEL(sc)) {
+ DC_SETBIT(sc, DC_SIARESET, DC_SIA_RESET);
+ CSR_WRITE_4(sc, DC_10BTCTRL, 0);
+ CSR_WRITE_4(sc, DC_WATCHDOG, 0);
+ }
+
+ return;
+}
+
+static
+struct dc_type *dc_devtype( int unitnum )
+{
+ struct dc_type *t;
+ uint32_t rev;
+ int rc;
+
+
+ t = dc_devs;
+
+ while(t->dc_name != NULL) {
+ rc = pci_find_device(t->dc_vid, t->dc_did, \
+ (unitnum - 1), &t->dc_bus, &t->dc_dev, &t->dc_fun);
+ if (rc == PCIB_ERR_SUCCESS) {
+ /* Check the PCI revision */
+ /*pcib_conf_read32(t->dc_devsig, DC_PCI_CFRV, &rev); */
+ pci_read_config_dword(t->dc_bus,t->dc_dev,t->dc_fun,\
+ DC_PCI_CFRV, &rev);
+ rev &= 0xFF;
+
+ if (t->dc_did == DC_DEVICEID_98713 &&
+ rev >= DC_REVISION_98713A)
+ t++;
+ if (t->dc_did == DC_DEVICEID_98713_CP &&
+ rev >= DC_REVISION_98713A)
+ t++;
+ if (t->dc_did == DC_DEVICEID_987x5 &&
+ rev >= DC_REVISION_98715AEC_C)
+ t++;
+ if (t->dc_did == DC_DEVICEID_987x5 &&
+ rev >= DC_REVISION_98725)
+ t++;
+ if (t->dc_did == DC_DEVICEID_AX88140A &&
+ rev >= DC_REVISION_88141)
+ t++;
+ if (t->dc_did == DC_DEVICEID_82C168 &&
+ rev >= DC_REVISION_82C169)
+ t++;
+ if (t->dc_did == DC_DEVICEID_DM9102 &&
+ rev >= DC_REVISION_DM9102A)
+ t++;
+ return(t);
+ }
+ t++;
+ }
+
+ return(NULL);
+}
+
+#if 0
+/*
+ * Probe for a 21143 or clone chip. Check the PCI vendor and device
+ * IDs against our list and return a device name if we find a match.
+ * We do a little bit of extra work to identify the exact type of
+ * chip. The MX98713 and MX98713A have the same PCI vendor/device ID,
+ * but different revision IDs. The same is true for 98715/98715A
+ * chips and the 98725, as well as the ASIX and ADMtek chips. In some
+ * cases, the exact chip revision affects driver behavior.
+ */
+static int dc_probe(dev)
+ device_t dev;
+{
+ struct dc_type *t;
+
+ t = dc_devtype(dev);
+
+ if (t != NULL) {
+ device_set_desc(dev, t->dc_name);
+ return(0);
+ }
+
+ return(ENXIO);
+}
+
+
+static void dc_acpi(dev)
+ device_t dev;
+{
+ u_int32_t r, cptr;
+ int unit;
+
+ unit = device_get_unit(dev);
+
+ /* Find the location of the capabilities block */
+ cptr = pci_read_config(dev, DC_PCI_CCAP, 4) & 0xFF;
+
+ r = pci_read_config(dev, cptr, 4) & 0xFF;
+ if (r == 0x01) {
+
+ r = pci_read_config(dev, cptr + 4, 4);
+ if (r & DC_PSTATE_D3) {
+ u_int32_t iobase, membase, irq;
+
+ /* Save important PCI config data. */
+ iobase = pci_read_config(dev, DC_PCI_CFBIO, 4);
+ membase = pci_read_config(dev, DC_PCI_CFBMA, 4);
+ irq = pci_read_config(dev, DC_PCI_CFIT, 4);
+
+ /* Reset the power state. */
+ printk("dc%d: chip is in D%d power mode "
+ "-- setting to D0\n", unit, r & DC_PSTATE_D3);
+ r &= 0xFFFFFFFC;
+ pci_write_config(dev, cptr + 4, r, 4);
+
+ /* Restore PCI config data. */
+ pci_write_config(dev, DC_PCI_CFBIO, iobase, 4);
+ pci_write_config(dev, DC_PCI_CFBMA, membase, 4);
+ pci_write_config(dev, DC_PCI_CFIT, irq, 4);
+ }
+ }
+ return;
+}
+#endif
+
+
+static void dc_apply_fixup(sc, media)
+ struct dc_softc *sc;
+ int media;
+{
+ struct dc_mediainfo *m;
+ u_int8_t *p;
+ int i;
+ u_int32_t reg;
+
+ m = sc->dc_mi;
+
+ while (m != NULL) {
+ if (m->dc_media == media)
+ break;
+ m = m->dc_next;
+ }
+
+ if (m == NULL)
+ return;
+
+ for (i = 0, p = m->dc_reset_ptr; i < m->dc_reset_len; i++, p += 2) {
+ reg = (p[0] | (p[1] << 8)) << 16;
+ CSR_WRITE_4(sc, DC_WATCHDOG, reg);
+ }
+
+ for (i = 0, p = m->dc_gp_ptr; i < m->dc_gp_len; i++, p += 2) {
+ reg = (p[0] | (p[1] << 8)) << 16;
+ CSR_WRITE_4(sc, DC_WATCHDOG, reg);
+ }
+
+ return;
+}
+
+#if 0
+static void dc_decode_leaf_sia(sc, l)
+ struct dc_softc *sc;
+ struct dc_eblock_sia *l;
+{
+ struct dc_mediainfo *m;
+
+ m = malloc(sizeof(struct dc_mediainfo), M_DEVBUF, M_NOWAIT);
+ bzero(m, sizeof(struct dc_mediainfo));
+ if (l->dc_sia_code == DC_SIA_CODE_10BT)
+ m->dc_media = IFM_10_T;
+
+ if (l->dc_sia_code == DC_SIA_CODE_10BT_FDX)
+ m->dc_media = IFM_10_T|IFM_FDX;
+
+ if (l->dc_sia_code == DC_SIA_CODE_10B2)
+ m->dc_media = IFM_10_2;
+
+ if (l->dc_sia_code == DC_SIA_CODE_10B5)
+ m->dc_media = IFM_10_5;
+
+ m->dc_gp_len = 2;
+ m->dc_gp_ptr = (u_int8_t *)&l->dc_sia_gpio_ctl;
+
+ m->dc_next = sc->dc_mi;
+ sc->dc_mi = m;
+
+ sc->dc_pmode = DC_PMODE_SIA;
+
+ return;
+}
+
+static void dc_decode_leaf_sym(sc, l)
+ struct dc_softc *sc;
+ struct dc_eblock_sym *l;
+{
+ struct dc_mediainfo *m;
+
+ m = malloc(sizeof(struct dc_mediainfo), M_DEVBUF, M_NOWAIT);
+ bzero(m, sizeof(struct dc_mediainfo));
+ if (l->dc_sym_code == DC_SYM_CODE_100BT)
+ m->dc_media = IFM_100_TX;
+
+ if (l->dc_sym_code == DC_SYM_CODE_100BT_FDX)
+ m->dc_media = IFM_100_TX|IFM_FDX;
+
+ m->dc_gp_len = 2;
+ m->dc_gp_ptr = (u_int8_t *)&l->dc_sym_gpio_ctl;
+
+ m->dc_next = sc->dc_mi;
+ sc->dc_mi = m;
+
+ sc->dc_pmode = DC_PMODE_SYM;
+
+ return;
+}
+
+static void dc_decode_leaf_mii(sc, l)
+ struct dc_softc *sc;
+ struct dc_eblock_mii *l;
+{
+ u_int8_t *p;
+ struct dc_mediainfo *m;
+
+ m = malloc(sizeof(struct dc_mediainfo), M_DEVBUF, M_NOWAIT);
+ bzero(m, sizeof(struct dc_mediainfo));
+ /* We abuse IFM_AUTO to represent MII. */
+ m->dc_media = IFM_AUTO;
+ m->dc_gp_len = l->dc_gpr_len;
+
+ p = (u_int8_t *)l;
+ p += sizeof(struct dc_eblock_mii);
+ m->dc_gp_ptr = p;
+ p += 2 * l->dc_gpr_len;
+ m->dc_reset_len = *p;
+ p++;
+ m->dc_reset_ptr = p;
+
+ m->dc_next = sc->dc_mi;
+ sc->dc_mi = m;
+
+ return;
+}
+#endif
+
+static void dc_read_srom(sc, bits)
+ struct dc_softc *sc;
+ int bits;
+{
+ int size;
+
+ size = 2 << bits;
+ sc->dc_srom = malloc(size, M_DEVBUF, M_NOWAIT);
+ dc_read_eeprom(sc, (caddr_t)sc->dc_srom, 0, (size / 2), 0);
+}
+
+static void dc_parse_21143_srom(sc)
+ struct dc_softc *sc;
+{
+ struct dc_leaf_hdr *lhdr;
+ struct dc_eblock_hdr *hdr;
+ int i, loff;
+ char *ptr;
+
+ loff = sc->dc_srom[27];
+ lhdr = (struct dc_leaf_hdr *)&(sc->dc_srom[loff]);
+
+ ptr = (char *)lhdr;
+ ptr += sizeof(struct dc_leaf_hdr) - 1;
+ for (i = 0; i < lhdr->dc_mcnt; i++) {
+ hdr = (struct dc_eblock_hdr *)ptr;
+ switch(hdr->dc_type) {
+#if 0
+ case DC_EBLOCK_MII:
+ dc_decode_leaf_mii(sc, (struct dc_eblock_mii *)hdr);
+ break;
+ case DC_EBLOCK_SIA:
+ dc_decode_leaf_sia(sc, (struct dc_eblock_sia *)hdr);
+ break;
+ case DC_EBLOCK_SYM:
+ dc_decode_leaf_sym(sc, (struct dc_eblock_sym *)hdr);
+ break;
+#endif
+ default:
+ /* Don't care. Yet. */
+ break;
+ }
+ ptr += (hdr->dc_len & 0x7F);
+ ptr++;
+ }
+
+ return;
+}
+
+
+static void
+nop(const rtems_irq_connect_data* unused)
+{
+}
+
+/*
+ * Attach the interface. Allocate softc structures, do ifmedia
+ * setup and ethernet/BPF attach.
+ */
+int
+rtems_dc_driver_attach(struct rtems_bsdnet_ifconfig *config, int attaching)
+{
+ int rc;
+ u_char eaddr[ETHER_ADDR_LEN];
+
+ char *unitName;
+ int unitNumber;
+
+ uint32_t command;
+ struct dc_softc *sc;
+ struct ifnet *ifp;
+ struct dc_type *t;
+ uint32_t revision;
+ int error = 0, mac_offset;
+ uint32_t value;
+
+ /*
+ * Get the instance number for the board we're going to configure
+ * from the user.
+ */
+ unitNumber = rtems_bsdnet_parse_driver_name(config, &unitName);
+ if( unitNumber < 0) {
+ return 0;
+ }
+ if( strcmp(unitName, DRIVER_PREFIX) ) {
+ printk("dec2114x : unit name '%s' not %s\n", \
+ unitName, DRIVER_PREFIX );
+ return 0;
+ }
+
+ sc = &dc_softc_devs[unitNumber - 1];
+ ifp = &sc->arpcom.ac_if;
+
+ if(ifp->if_softc != NULL) {
+ printk("dec2114x[%d]: unit number already in use.\n", \
+ unitNumber);
+ return (0);
+ }
+ memset(sc, 0, sizeof(struct dc_softc));
+
+ /*unit = device_get_unit(dev);*/
+ sc->dc_unit = unitNumber;
+ sc->dc_name = unitName;
+
+ /*
+ * Handle power management nonsense.
+ *
+ dc_acpi(dev);
+ */
+
+ /* Scan for dec2114x cards in pci config space */
+ if( (sc->dc_info = dc_devtype(unitNumber)) == NULL) {
+ printk("Can't find any dec2114x NICs in PCI space.\n");
+ return 0;
+ }
+ t = sc->dc_info;
+
+
+ /*
+ * Map control/status registers.
+ */
+ /*sig = sc->dc_info->dc_devsig; */
+ pci_read_config_dword(t->dc_bus,t->dc_dev,t->dc_fun,\
+ PCI_COMMAND, &command);
+ /*pcib_conf_read32(sig, PCI_COMMAND, &command); */
+ command |= (PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER);
+ pci_write_config_dword(t->dc_bus,t->dc_dev,t->dc_fun,\
+ PCI_COMMAND, command);
+ /*pcib_conf_write32(sig, PCI_COMMAND, command); */
+ pci_read_config_dword(t->dc_bus,t->dc_dev,t->dc_fun,\
+ PCI_COMMAND, &command);
+ /*pcib_conf_read32(sig, PCI_COMMAND, &command); */
+
+#ifdef DC_USEIOSPACE
+ if (!(command & PCI_COMMAND_IO)) {
+ printk("dc%d: failed to enable I/O ports!\n", sc->dc_unit);
+ error = ENXIO;
+ goto fail;
+ }
+#else
+ if (!(command & PCI_COMMAND_MEMORY)) {
+ printk("dc%d: failed to enable memory mapping!\n", sc->dc_unit);
+ error = ENXIO;
+ goto fail;
+ }
+#endif
+
+#if 0
+ rid = DC_RID;
+ sc->dc_res = bus_alloc_resource(dev, DC_RES, &rid,
+ 0, ~0, 1, RF_ACTIVE);
+
+ if (sc->dc_res == NULL) {
+ printk("dc%d: couldn't map ports/memory\n", unit);
+ error = ENXIO;
+ goto fail;
+ }
+ sc->dc_btag = rman_get_bustag(sc->dc_res);
+ sc->dc_bhandle = rman_get_bushandle(sc->dc_res);
+#endif
+
+ /* sc->membase is the address of the card's CSRs !!! */
+ /*pcib_conf_read32(sig, DC_PCI_CFBMA, &value); */
+ pci_read_config_dword(t->dc_bus,t->dc_dev,t->dc_fun,\
+ DC_PCI_CFBMA, &value);
+ sc->membase = value;
+
+ /* Allocate interrupt */
+ memset(&sc->irqInfo, 0, sizeof(rtems_irq_connect_data));
+ /*pcib_conf_read32(sig, DC_PCI_CFIT, &value); */
+ pci_read_config_dword(t->dc_bus,t->dc_dev,t->dc_fun,\
+ DC_PCI_CFIT, &value);
+
+ sc->irqInfo.name = value & 0xFF;
+ sc->irqInfo.hdl = (rtems_irq_hdl)dc_intr;
+ sc->irqInfo.handle = (void *)sc; /* new parameter */
+ sc->irqInfo.on = nop;
+ sc->irqInfo.off = nop;
+ sc->irqInfo.isOn = NULL;
+
+#ifdef BSP_SHARED_HANDLER_SUPPORT
+ rc = BSP_install_rtems_shared_irq_handler( &sc->irqInfo );
+#else
+ rc = BSP_install_rtems_irq_handler( &sc->irqInfo );
+#endif
+ if(!rc) {
+ rtems_panic("Can't install dec2114x irq handler.\n");
+ }
+
+
+#if 0
+ rid = 0;
+ sc->dc_irq = bus_alloc_resource(dev, SYS_RES_IRQ, &rid, 0, ~0, 1,
+ RF_SHAREABLE | RF_ACTIVE);
+
+ if (sc->dc_irq == NULL) {
+ printk("dc%d: couldn't map interrupt\n", unit);
+ bus_release_resource(dev, DC_RES, DC_RID, sc->dc_res);
+ error = ENXIO;
+ goto fail;
+ }
+
+ error = bus_setup_intr(dev, sc->dc_irq, INTR_TYPE_NET,
+ dc_intr, sc, &sc->dc_intrhand);
+
+ if (error) {
+ bus_release_resource(dev, SYS_RES_IRQ, 0, sc->dc_irq);
+ bus_release_resource(dev, DC_RES, DC_RID, sc->dc_res);
+ printk("dc%d: couldn't set up irq\n", unit);
+ goto fail;
+ }
+#endif
+
+
+ /* Need this info to decide on a chip type.
+ sc->dc_info = dc_devtype(dev);
+ */
+ /*pcib_conf_read32(sig, DC_PCI_CFRV, &revision); */
+ pci_read_config_dword(t->dc_bus,t->dc_dev,t->dc_fun,\
+ DC_PCI_CFRV, &revision);
+ revision &= 0x000000FF;
+
+ /* Get the eeprom width, but PNIC has diff eeprom */
+ if (sc->dc_info->dc_did != DC_DEVICEID_82C168)
+ dc_eeprom_width(sc);
+
+ switch(sc->dc_info->dc_did) {
+ case DC_DEVICEID_21143:
+ sc->dc_type = DC_TYPE_21143;
+ sc->dc_flags |= DC_TX_POLL|DC_TX_USE_TX_INTR;
+ sc->dc_flags |= DC_REDUCED_MII_POLL;
+ /* Save EEPROM contents so we can parse them later. */
+ dc_read_srom(sc, sc->dc_romwidth);
+ break;
+ case DC_DEVICEID_DM9009:
+ case DC_DEVICEID_DM9100:
+ case DC_DEVICEID_DM9102:
+ sc->dc_type = DC_TYPE_DM9102;
+ sc->dc_flags |= DC_TX_COALESCE|DC_TX_INTR_ALWAYS;
+ sc->dc_flags |= DC_REDUCED_MII_POLL|DC_TX_STORENFWD;
+ sc->dc_pmode = DC_PMODE_MII;
+ /* Increase the latency timer value. */
+ /*pcib_conf_read32(sig, DC_PCI_CFLT, &command); */
+ pci_read_config_dword(t->dc_bus,t->dc_dev,t->dc_fun,\
+ DC_PCI_CFLT, &command);
+ command &= 0xFFFF00FF;
+ command |= 0x00008000;
+ /*pcib_conf_write32(sig, DC_PCI_CFLT, command); */
+ pci_write_config_dword(t->dc_bus,t->dc_dev,t->dc_fun,\
+ DC_PCI_CFLT, command);
+ break;
+ case DC_DEVICEID_AL981:
+ sc->dc_type = DC_TYPE_AL981;
+ sc->dc_flags |= DC_TX_USE_TX_INTR;
+ sc->dc_flags |= DC_TX_ADMTEK_WAR;
+ sc->dc_pmode = DC_PMODE_MII;
+ dc_read_srom(sc, sc->dc_romwidth);
+ break;
+ case DC_DEVICEID_AN985:
+ case DC_DEVICEID_EN2242:
+ sc->dc_type = DC_TYPE_AN985;
+ sc->dc_flags |= DC_TX_USE_TX_INTR;
+ sc->dc_flags |= DC_TX_ADMTEK_WAR;
+ sc->dc_pmode = DC_PMODE_MII;
+ dc_read_srom(sc, sc->dc_romwidth);
+ break;
+ case DC_DEVICEID_98713:
+ case DC_DEVICEID_98713_CP:
+ if (revision < DC_REVISION_98713A) {
+ sc->dc_type = DC_TYPE_98713;
+ }
+ if (revision >= DC_REVISION_98713A) {
+ sc->dc_type = DC_TYPE_98713A;
+ sc->dc_flags |= DC_21143_NWAY;
+ }
+ sc->dc_flags |= DC_REDUCED_MII_POLL;
+ sc->dc_flags |= DC_TX_POLL|DC_TX_USE_TX_INTR;
+ break;
+ case DC_DEVICEID_987x5:
+ case DC_DEVICEID_EN1217:
+ /*
+ * Macronix MX98715AEC-C/D/E parts have only a
+ * 128-bit hash table. We need to deal with these
+ * in the same manner as the PNIC II so that we
+ * get the right number of bits out of the
+ * CRC routine.
+ */
+ if (revision >= DC_REVISION_98715AEC_C &&
+ revision < DC_REVISION_98725)
+ sc->dc_flags |= DC_128BIT_HASH;
+ sc->dc_type = DC_TYPE_987x5;
+ sc->dc_flags |= DC_TX_POLL|DC_TX_USE_TX_INTR;
+ sc->dc_flags |= DC_REDUCED_MII_POLL|DC_21143_NWAY;
+ break;
+ case DC_DEVICEID_98727:
+ sc->dc_type = DC_TYPE_987x5;
+ sc->dc_flags |= DC_TX_POLL|DC_TX_USE_TX_INTR;
+ sc->dc_flags |= DC_REDUCED_MII_POLL|DC_21143_NWAY;
+ break;
+ case DC_DEVICEID_82C115:
+ sc->dc_type = DC_TYPE_PNICII;
+ sc->dc_flags |= DC_TX_POLL|DC_TX_USE_TX_INTR|DC_128BIT_HASH;
+ sc->dc_flags |= DC_REDUCED_MII_POLL|DC_21143_NWAY;
+ break;
+ case DC_DEVICEID_82C168:
+ sc->dc_type = DC_TYPE_PNIC;
+ sc->dc_flags |= DC_TX_STORENFWD|DC_TX_INTR_ALWAYS;
+ sc->dc_flags |= DC_PNIC_RX_BUG_WAR;
+ sc->dc_pnic_rx_buf = malloc(DC_RXLEN * 5, M_DEVBUF, M_NOWAIT);
+ if (revision < DC_REVISION_82C169)
+ sc->dc_pmode = DC_PMODE_SYM;
+ break;
+ case DC_DEVICEID_AX88140A:
+ sc->dc_type = DC_TYPE_ASIX;
+ sc->dc_flags |= DC_TX_USE_TX_INTR|DC_TX_INTR_FIRSTFRAG;
+ sc->dc_flags |= DC_REDUCED_MII_POLL;
+ sc->dc_pmode = DC_PMODE_MII;
+ break;
+ case DC_DEVICEID_RS7112:
+ sc->dc_type = DC_TYPE_CONEXANT;
+ sc->dc_flags |= DC_TX_INTR_ALWAYS;
+ sc->dc_flags |= DC_REDUCED_MII_POLL;
+ sc->dc_pmode = DC_PMODE_MII;
+ dc_read_srom(sc, sc->dc_romwidth);
+ break;
+ default:
+ printk("dc%d: unknown device: %x\n", sc->dc_unit,
+ sc->dc_info->dc_did);
+ break;
+ }
+
+ /* Save the cache line size. */
+ if (DC_IS_DAVICOM(sc)) {
+ sc->dc_cachesize = 0;
+ }
+ else {
+ /*pcib_conf_read32(sig, DC_PCI_CFLT, &value); */
+ pci_read_config_dword(t->dc_bus,t->dc_dev,t->dc_fun,\
+ DC_PCI_CFLT, &value);
+ sc->dc_cachesize = (u_int8_t)(value & 0xFF);
+ }
+
+ /* Reset the adapter. */
+ dc_reset(sc);
+
+ /* Take 21143 out of snooze mode */
+ if (DC_IS_INTEL(sc)) {
+ /*pcib_conf_read32(sig, DC_PCI_CFDD, &command); */
+ pci_read_config_dword(t->dc_bus,t->dc_dev,t->dc_fun,\
+ DC_PCI_CFDD, &command);
+ command &= ~(DC_CFDD_SNOOZE_MODE|DC_CFDD_SLEEP_MODE);
+ /*pcib_conf_write32(sig, DC_PCI_CFDD, command); */
+ pci_write_config_dword(t->dc_bus,t->dc_dev,t->dc_fun,\
+ DC_PCI_CFDD, command);
+ }
+
+
+ /*
+ * Try to learn something about the supported media.
+ * We know that ASIX and ADMtek and Davicom devices
+ * will *always* be using MII media, so that's a no-brainer.
+ * The tricky ones are the Macronix/PNIC II and the
+ * Intel 21143.
+ */
+ if (DC_IS_INTEL(sc))
+ dc_parse_21143_srom(sc);
+ else if (DC_IS_MACRONIX(sc) || DC_IS_PNICII(sc)) {
+ if (sc->dc_type == DC_TYPE_98713)
+ sc->dc_pmode = DC_PMODE_MII;
+ else
+ sc->dc_pmode = DC_PMODE_SYM;
+ } else if (!sc->dc_pmode)
+ sc->dc_pmode = DC_PMODE_MII;
+
+ /*
+ * Get station address from the EEPROM.
+ */
+ switch(sc->dc_type) {
+ case DC_TYPE_98713:
+ case DC_TYPE_98713A:
+ case DC_TYPE_987x5:
+ case DC_TYPE_PNICII:
+ dc_read_eeprom(sc, (caddr_t)&mac_offset,
+ (DC_EE_NODEADDR_OFFSET / 2), 1, 0);
+ dc_read_eeprom(sc, (caddr_t)&eaddr, (mac_offset / 2), 3, 0);
+ break;
+ case DC_TYPE_PNIC:
+ dc_read_eeprom(sc, (caddr_t)&eaddr, 0, 3, 1);
+ break;
+ case DC_TYPE_DM9102:
+ case DC_TYPE_21143:
+ case DC_TYPE_ASIX:
+ dc_read_eeprom(sc, (caddr_t)&eaddr, DC_EE_NODEADDR, 3, 0);
+ break;
+ case DC_TYPE_AL981:
+ case DC_TYPE_AN985:
+ bcopy(&sc->dc_srom[DC_AL_EE_NODEADDR], (caddr_t)&eaddr,
+ ETHER_ADDR_LEN);
+ dc_read_eeprom(sc, (caddr_t)&eaddr, DC_AL_EE_NODEADDR, 3, 0);
+ break;
+ case DC_TYPE_CONEXANT:
+ bcopy(sc->dc_srom + DC_CONEXANT_EE_NODEADDR, &eaddr, 6);
+ break;
+ default:
+ dc_read_eeprom(sc, (caddr_t)&eaddr, DC_EE_NODEADDR, 3, 0);
+ break;
+ }
+
+ /*
+ * A 21143 or clone chip was detected. Inform the world.
+ */
+ bcopy(eaddr, (char *)&sc->arpcom.ac_enaddr, ETHER_ADDR_LEN);
+ printk("dc%d: MAC address -- %02x:%02x:%02x:%02x:%02x:%02x\n", \
+ sc->dc_unit,sc->arpcom.ac_enaddr[0], \
+ sc->arpcom.ac_enaddr[1], sc->arpcom.ac_enaddr[2], \
+ sc->arpcom.ac_enaddr[3], sc->arpcom.ac_enaddr[4], \
+ sc->arpcom.ac_enaddr[5]);
+
+
+ sc->dc_ldata = malloc(sizeof(struct dc_list_data), M_DEVBUF, M_NOWAIT);
+
+ if (sc->dc_ldata == NULL) {
+ printk("dc%d: no memory for list buffers!\n", sc->dc_unit);
+ if (sc->dc_pnic_rx_buf != NULL)
+ free(sc->dc_pnic_rx_buf, M_DEVBUF);
+#if 0
+ bus_teardown_intr(dev, sc->dc_irq, sc->dc_intrhand);
+ bus_release_resource(dev, SYS_RES_IRQ, 0, sc->dc_irq);
+ bus_release_resource(dev, DC_RES, DC_RID, sc->dc_res);
+#endif
+ error = ENXIO;
+ goto fail;
+ }
+
+ bzero(sc->dc_ldata, sizeof(struct dc_list_data));
+
+ ifp = &sc->arpcom.ac_if;
+ ifp->if_softc = sc;
+ ifp->if_unit = unitNumber; /*sc->dc_unit;*/
+ ifp->if_name = unitName; /*sc->dc_name;*/
+ ifp->if_mtu = ETHERMTU;
+ ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX; /* | IFF_MULTICAST;*/
+ ifp->if_ioctl = dc_ioctl;
+ ifp->if_output = ether_output;
+ ifp->if_start = dc_start;
+ ifp->if_watchdog = dc_watchdog;
+ ifp->if_init = dc_init;
+ ifp->if_baudrate = 100000000;
+ ifp->if_snd.ifq_maxlen = DC_TX_LIST_CNT - 1;
+
+#if 0
+ /*
+ * Do MII setup. If this is a 21143, check for a PHY on the
+ * MII bus after applying any necessary fixups to twiddle the
+ * GPIO bits. If we don't end up finding a PHY, restore the
+ * old selection (SIA only or SIA/SYM) and attach the dcphy
+ * driver instead.
+ */
+ if (DC_IS_INTEL(sc)) {
+ dc_apply_fixup(sc, IFM_AUTO);
+ tmp = sc->dc_pmode;
+ sc->dc_pmode = DC_PMODE_MII;
+ }
+
+ error = mii_phy_probe(dev, &sc->dc_miibus,
+ dc_ifmedia_upd, dc_ifmedia_sts);
+
+ if (error && DC_IS_INTEL(sc)) {
+ sc->dc_pmode = tmp;
+ if (sc->dc_pmode != DC_PMODE_SIA)
+ sc->dc_pmode = DC_PMODE_SYM;
+ sc->dc_flags |= DC_21143_NWAY;
+ mii_phy_probe(dev, &sc->dc_miibus,
+ dc_ifmedia_upd, dc_ifmedia_sts);
+ /*
+ * For non-MII cards, we need to have the 21143
+ * drive the LEDs. Except there are some systems
+ * like the NEC VersaPro NoteBook PC which have no
+ * LEDs, and twiddling these bits has adverse effects
+ * on them. (I.e. you suddenly can't get a link.)
+ */
+ if (pci_read_config(dev, DC_PCI_CSID, 4) != 0x80281033)
+ sc->dc_flags |= DC_TULIP_LEDS;
+ error = 0;
+ }
+
+ if (error) {
+ printk("dc%d: MII without any PHY!\n", sc->dc_unit);
+ contigfree(sc->dc_ldata, sizeof(struct dc_list_data),
+ M_DEVBUF);
+ if (sc->dc_pnic_rx_buf != NULL)
+ free(sc->dc_pnic_rx_buf, M_DEVBUF);
+ bus_teardown_intr(dev, sc->dc_irq, sc->dc_intrhand);
+ bus_release_resource(dev, SYS_RES_IRQ, 0, sc->dc_irq);
+ bus_release_resource(dev, DC_RES, DC_RID, sc->dc_res);
+ error = ENXIO;
+ goto fail;
+ }
+#endif
+
+ /*
+ * Call MI attach routine.
+ */
+ if_attach(ifp);
+ ether_ifattach(ifp);
+ /*callout_handle_init(&sc->dc_stat_ch);*/
+
+ if (DC_IS_ADMTEK(sc)) {
+ /*
+ * Set automatic TX underrun recovery for the ADMtek chips
+ */
+ DC_SETBIT(sc, DC_AL_CR, DC_AL_CR_ATUR);
+ }
+
+ if(sc->daemontid == 0) {
+ sc->daemontid = rtems_bsdnet_newproc("decD",4096, \
+ dc_daemon,(void *)sc);
+ printk("dec[%d]: daemon process started\n", sc->dc_unit);
+ }
+
+ /*
+ * Tell the upper layer(s) we support long frames.
+ *
+ ifp->if_data.ifi_hdrlen = sizeof(struct ether_vlan_header);
+ */
+
+#ifdef SRM_MEDIA /* only defined if __alpha__ is defined... */
+ sc->dc_srm_media = 0;
+
+ /* Remember the SRM console media setting */
+ if (DC_IS_INTEL(sc)) {
+ command = pci_read_config(dev, DC_PCI_CFDD, 4);
+ command &= ~(DC_CFDD_SNOOZE_MODE|DC_CFDD_SLEEP_MODE);
+ switch ((command >> 8) & 0xff) {
+ case 3:
+ sc->dc_srm_media = IFM_10_T;
+ break;
+ case 4:
+ sc->dc_srm_media = IFM_10_T | IFM_FDX;
+ break;
+ case 5:
+ sc->dc_srm_media = IFM_100_TX;
+ break;
+ case 6:
+ sc->dc_srm_media = IFM_100_TX | IFM_FDX;
+ break;
+ }
+ if (sc->dc_srm_media)
+ sc->dc_srm_media |= IFM_ACTIVE | IFM_ETHER;
+ }
+#endif
+
+
+fail:
+
+ return (1); /*(error);*/
+}
+
+#if 0
+static int dc_detach(dev)
+ device_t dev;
+{
+ struct dc_softc *sc;
+ struct ifnet *ifp;
+ int s;
+ struct dc_mediainfo *m;
+
+
+ sc = device_get_softc(dev);
+ ifp = &sc->arpcom.ac_if;
+
+ dc_stop(sc);
+ ether_ifdetach(ifp, ETHER_BPF_SUPPORTED);
+
+ bus_generic_detach(dev);
+ device_delete_child(dev, sc->dc_miibus);
+
+ bus_teardown_intr(dev, sc->dc_irq, sc->dc_intrhand);
+ bus_release_resource(dev, SYS_RES_IRQ, 0, sc->dc_irq);
+ bus_release_resource(dev, DC_RES, DC_RID, sc->dc_res);
+
+ contigfree(sc->dc_ldata, sizeof(struct dc_list_data), M_DEVBUF);
+ if (sc->dc_pnic_rx_buf != NULL)
+ free(sc->dc_pnic_rx_buf, M_DEVBUF);
+
+ while(sc->dc_mi != NULL) {
+ m = sc->dc_mi->dc_next;
+ free(sc->dc_mi, M_DEVBUF);
+ sc->dc_mi = m;
+ }
+ free(sc->dc_srom, M_DEVBUF);
+
+
+ return(0);
+}
+#endif
+
+
+/*
+ * Initialize the transmit descriptors.
+ */
+static int dc_list_tx_init(sc)
+ struct dc_softc *sc;
+{
+ struct dc_chain_data *cd;
+ struct dc_list_data *ld;
+ int i;
+
+ cd = &sc->dc_cdata;
+ ld = sc->dc_ldata;
+ for (i = 0; i < DC_TX_LIST_CNT; i++) {
+ if (i == (DC_TX_LIST_CNT - 1)) {
+ ld->dc_tx_list[i].dc_next =
+ vtophys(&ld->dc_tx_list[0]);
+ } else {
+ ld->dc_tx_list[i].dc_next =
+ vtophys(&ld->dc_tx_list[i + 1]);
+ }
+ cd->dc_tx_chain[i] = NULL;
+ ld->dc_tx_list[i].dc_data = 0;
+ ld->dc_tx_list[i].dc_ctl = 0;
+ }
+
+ cd->dc_tx_prod = cd->dc_tx_cons = cd->dc_tx_cnt = 0;
+
+ return(0);
+}
+
+
+/*
+ * Initialize the RX descriptors and allocate mbufs for them. Note that
+ * we arrange the descriptors in a closed ring, so that the last descriptor
+ * points back to the first.
+ */
+static int dc_list_rx_init(sc)
+ struct dc_softc *sc;
+{
+ struct dc_chain_data *cd;
+ struct dc_list_data *ld;
+ int i;
+
+ cd = &sc->dc_cdata;
+ ld = sc->dc_ldata;
+
+ for (i = 0; i < DC_RX_LIST_CNT; i++) {
+ if (dc_newbuf(sc, i, NULL) == ENOBUFS)
+ return(ENOBUFS);
+ if (i == (DC_RX_LIST_CNT - 1)) {
+ ld->dc_rx_list[i].dc_next =
+ vtophys(&ld->dc_rx_list[0]);
+ } else {
+ ld->dc_rx_list[i].dc_next =
+ vtophys(&ld->dc_rx_list[i + 1]);
+ }
+ }
+
+ cd->dc_rx_prod = 0;
+
+ return(0);
+}
+
+/*
+ * Initialize an RX descriptor and attach an MBUF cluster.
+ */
+static int dc_newbuf(sc, i, m)
+ struct dc_softc *sc;
+ int i;
+ struct mbuf *m;
+{
+ struct mbuf *m_new = NULL;
+ struct dc_desc *c;
+
+ c = &sc->dc_ldata->dc_rx_list[i];
+
+ if (m == NULL) {
+ MGETHDR(m_new, M_DONTWAIT, MT_DATA);
+ if (m_new == NULL)
+ return(ENOBUFS);
+
+ MCLGET(m_new, M_DONTWAIT);
+ if (!(m_new->m_flags & M_EXT)) {
+ m_freem(m_new);
+ return(ENOBUFS);
+ }
+ m_new->m_len = m_new->m_pkthdr.len = MCLBYTES;
+ } else {
+ m_new = m;
+ m_new->m_len = m_new->m_pkthdr.len = MCLBYTES;
+ m_new->m_data = m_new->m_ext.ext_buf;
+ }
+
+ m_adj(m_new, sizeof(u_int64_t));
+
+ /*
+ * If this is a PNIC chip, zero the buffer. This is part
+ * of the workaround for the receive bug in the 82c168 and
+ * 82c169 chips.
+ */
+ if (sc->dc_flags & DC_PNIC_RX_BUG_WAR)
+ bzero((char *)mtod(m_new, char *), m_new->m_len);
+
+ sc->dc_cdata.dc_rx_chain[i] = m_new;
+ c->dc_data = vtophys(mtod(m_new, caddr_t));
+ c->dc_ctl = DC_RXCTL_RLINK | DC_RXLEN;
+ c->dc_status = DC_RXSTAT_OWN;
+
+ return(0);
+}
+
+/*
+ * Grrrrr.
+ * The PNIC chip has a terrible bug in it that manifests itself during
+ * periods of heavy activity. The exact mode of failure if difficult to
+ * pinpoint: sometimes it only happens in promiscuous mode, sometimes it
+ * will happen on slow machines. The bug is that sometimes instead of
+ * uploading one complete frame during reception, it uploads what looks
+ * like the entire contents of its FIFO memory. The frame we want is at
+ * the end of the whole mess, but we never know exactly how much data has
+ * been uploaded, so salvaging the frame is hard.
+ *
+ * There is only one way to do it reliably, and it's disgusting.
+ * Here's what we know:
+ *
+ * - We know there will always be somewhere between one and three extra
+ * descriptors uploaded.
+ *
+ * - We know the desired received frame will always be at the end of the
+ * total data upload.
+ *
+ * - We know the size of the desired received frame because it will be
+ * provided in the length field of the status word in the last descriptor.
+ *
+ * Here's what we do:
+ *
+ * - When we allocate buffers for the receive ring, we bzero() them.
+ * This means that we know that the buffer contents should be all
+ * zeros, except for data uploaded by the chip.
+ *
+ * - We also force the PNIC chip to upload frames that include the
+ * ethernet CRC at the end.
+ *
+ * - We gather all of the bogus frame data into a single buffer.
+ *
+ * - We then position a pointer at the end of this buffer and scan
+ * backwards until we encounter the first non-zero byte of data.
+ * This is the end of the received frame. We know we will encounter
+ * some data at the end of the frame because the CRC will always be
+ * there, so even if the sender transmits a packet of all zeros,
+ * we won't be fooled.
+ *
+ * - We know the size of the actual received frame, so we subtract
+ * that value from the current pointer location. This brings us
+ * to the start of the actual received packet.
+ *
+ * - We copy this into an mbuf and pass it on, along with the actual
+ * frame length.
+ *
+ * The performance hit is tremendous, but it beats dropping frames all
+ * the time.
+ */
+
+#define DC_WHOLEFRAME (DC_RXSTAT_FIRSTFRAG|DC_RXSTAT_LASTFRAG)
+static void dc_pnic_rx_bug_war(sc, idx)
+ struct dc_softc *sc;
+ int idx;
+{
+ struct dc_desc *cur_rx;
+ struct dc_desc *c = NULL;
+ struct mbuf *m = NULL;
+ unsigned char *ptr;
+ int i, total_len;
+ u_int32_t rxstat = 0;
+
+ i = sc->dc_pnic_rx_bug_save;
+ cur_rx = &sc->dc_ldata->dc_rx_list[idx];
+ ptr = sc->dc_pnic_rx_buf;
+ bzero(ptr, sizeof(DC_RXLEN * 5));
+
+ /* Copy all the bytes from the bogus buffers. */
+ while (1) {
+ c = &sc->dc_ldata->dc_rx_list[i];
+ rxstat = c->dc_status;
+ m = sc->dc_cdata.dc_rx_chain[i];
+ bcopy(mtod(m, char *), ptr, DC_RXLEN);
+ ptr += DC_RXLEN;
+ /* If this is the last buffer, break out. */
+ if (i == idx || rxstat & DC_RXSTAT_LASTFRAG)
+ break;
+ dc_newbuf(sc, i, m);
+ DC_INC(i, DC_RX_LIST_CNT);
+ }
+
+ /* Find the length of the actual receive frame. */
+ total_len = DC_RXBYTES(rxstat);
+
+ /* Scan backwards until we hit a non-zero byte. */
+ while(*ptr == 0x00)
+ ptr--;
+#if 0
+ /* Round off. */
+ if ((uintptr_t)(ptr) & 0x3)
+ ptr -= 1;
+#endif
+
+ /* Now find the start of the frame. */
+ ptr -= total_len;
+ if (ptr < sc->dc_pnic_rx_buf)
+ ptr = sc->dc_pnic_rx_buf;
+
+ /*
+ * Now copy the salvaged frame to the last mbuf and fake up
+ * the status word to make it look like a successful
+ * frame reception.
+ */
+ dc_newbuf(sc, i, m);
+ bcopy(ptr, mtod(m, char *), total_len);
+ cur_rx->dc_status = rxstat | DC_RXSTAT_FIRSTFRAG;
+
+ return;
+}
+
+/*
+ * This routine searches the RX ring for dirty descriptors in the
+ * event that the rxeof routine falls out of sync with the chip's
+ * current descriptor pointer. This may happen sometimes as a result
+ * of a "no RX buffer available" condition that happens when the chip
+ * consumes all of the RX buffers before the driver has a chance to
+ * process the RX ring. This routine may need to be called more than
+ * once to bring the driver back in sync with the chip, however we
+ * should still be getting RX DONE interrupts to drive the search
+ * for new packets in the RX ring, so we should catch up eventually.
+ */
+static int dc_rx_resync(sc)
+ struct dc_softc *sc;
+{
+ int i, pos;
+ struct dc_desc *cur_rx;
+
+ pos = sc->dc_cdata.dc_rx_prod;
+
+ for (i = 0; i < DC_RX_LIST_CNT; i++) {
+ cur_rx = &sc->dc_ldata->dc_rx_list[pos];
+ if (!(cur_rx->dc_status & DC_RXSTAT_OWN))
+ break;
+ DC_INC(pos, DC_RX_LIST_CNT);
+ }
+
+ /* If the ring really is empty, then just return. */
+ if (i == DC_RX_LIST_CNT)
+ return(0);
+
+ /* We've fallen behing the chip: catch it. */
+ sc->dc_cdata.dc_rx_prod = pos;
+
+ return(EAGAIN);
+}
+
+/*
+ * A frame has been uploaded: pass the resulting mbuf chain up to
+ * the higher level protocols.
+ */
+static void dc_rxeof(sc)
+ struct dc_softc *sc;
+{
+ struct ether_header *eh;
+ struct mbuf *m;
+ struct ifnet *ifp;
+ struct dc_desc *cur_rx;
+ int i, total_len = 0;
+ u_int32_t rxstat;
+
+ ifp = &sc->arpcom.ac_if;
+ i = sc->dc_cdata.dc_rx_prod;
+
+ while(!(sc->dc_ldata->dc_rx_list[i].dc_status & DC_RXSTAT_OWN)) {
+
+#ifdef DEVICE_POLLING
+ if (ifp->if_ipending & IFF_POLLING) {
+ if (sc->rxcycles <= 0)
+ break;
+ sc->rxcycles--;
+ }
+#endif /* DEVICE_POLLING */
+ cur_rx = &sc->dc_ldata->dc_rx_list[i];
+ rxstat = cur_rx->dc_status;
+ m = sc->dc_cdata.dc_rx_chain[i];
+ total_len = DC_RXBYTES(rxstat);
+
+ if (sc->dc_flags & DC_PNIC_RX_BUG_WAR) {
+ if ((rxstat & DC_WHOLEFRAME) != DC_WHOLEFRAME) {
+ if (rxstat & DC_RXSTAT_FIRSTFRAG)
+ sc->dc_pnic_rx_bug_save = i;
+ if ((rxstat & DC_RXSTAT_LASTFRAG) == 0) {
+ DC_INC(i, DC_RX_LIST_CNT);
+ continue;
+ }
+ dc_pnic_rx_bug_war(sc, i);
+ rxstat = cur_rx->dc_status;
+ total_len = DC_RXBYTES(rxstat);
+ }
+ }
+
+ sc->dc_cdata.dc_rx_chain[i] = NULL;
+
+ /*
+ * If an error occurs, update stats, clear the
+ * status word and leave the mbuf cluster in place:
+ * it should simply get re-used next time this descriptor
+ * comes up in the ring. However, don't report long
+ * frames as errors since they could be vlans
+ */
+ if ((rxstat & DC_RXSTAT_RXERR)){
+ if (!(rxstat & DC_RXSTAT_GIANT) ||
+ (rxstat & (DC_RXSTAT_CRCERR | DC_RXSTAT_DRIBBLE |
+ DC_RXSTAT_MIIERE | DC_RXSTAT_COLLSEEN |
+ DC_RXSTAT_RUNT | DC_RXSTAT_DE))) {
+ ifp->if_ierrors++;
+ if (rxstat & DC_RXSTAT_COLLSEEN)
+ ifp->if_collisions++;
+ dc_newbuf(sc, i, m);
+ if (rxstat & DC_RXSTAT_CRCERR) {
+ DC_INC(i, DC_RX_LIST_CNT);
+ continue;
+ } else {
+ dc_init(sc);
+ return;
+ }
+ }
+ }
+
+ /* No errors; receive the packet. */
+ total_len -= ETHER_CRC_LEN;
+
+#ifdef __i386__
+ /*
+ * On the x86 we do not have alignment problems, so try to
+ * allocate a new buffer for the receive ring, and pass up
+ * the one where the packet is already, saving the expensive
+ * copy done in m_devget().
+ * If we are on an architecture with alignment problems, or
+ * if the allocation fails, then use m_devget and leave the
+ * existing buffer in the receive ring.
+ */
+ if (dc_quick && dc_newbuf(sc, i, NULL) == 0) {
+ m->m_pkthdr.rcvif = ifp;
+ m->m_pkthdr.len = m->m_len = total_len;
+ DC_INC(i, DC_RX_LIST_CNT);
+ } else
+#endif
+ {
+ struct mbuf *m0;
+
+ m0 = m_devget(mtod(m, char *) - ETHER_ALIGN,
+ total_len + ETHER_ALIGN, 0, ifp, NULL);
+ dc_newbuf(sc, i, m);
+ DC_INC(i, DC_RX_LIST_CNT);
+ if (m0 == NULL) {
+ ifp->if_ierrors++;
+ continue;
+ }
+ m_adj(m0, ETHER_ALIGN);
+ m = m0;
+ }
+
+ ifp->if_ipackets++;
+ eh = mtod(m, struct ether_header *);
+
+ /* Remove header from mbuf and pass it on. */
+ m_adj(m, sizeof(struct ether_header));
+ ether_input(ifp, eh, m);
+ }
+
+ sc->dc_cdata.dc_rx_prod = i;
+}
+
+/*
+ * A frame was downloaded to the chip. It's safe for us to clean up
+ * the list buffers.
+ */
+
+static void
+dc_txeof(sc)
+ struct dc_softc *sc;
+{
+ struct dc_desc *cur_tx = NULL;
+ struct ifnet *ifp;
+ int idx;
+
+ ifp = &sc->arpcom.ac_if;
+
+ /*
+ * Go through our tx list and free mbufs for those
+ * frames that have been transmitted.
+ */
+ idx = sc->dc_cdata.dc_tx_cons;
+ while(idx != sc->dc_cdata.dc_tx_prod) {
+ u_int32_t txstat;
+
+ cur_tx = &sc->dc_ldata->dc_tx_list[idx];
+ txstat = cur_tx->dc_status;
+
+ if (txstat & DC_TXSTAT_OWN)
+ break;
+
+ if (!(cur_tx->dc_ctl & DC_TXCTL_LASTFRAG) ||
+ cur_tx->dc_ctl & DC_TXCTL_SETUP) {
+ if (cur_tx->dc_ctl & DC_TXCTL_SETUP) {
+ /*
+ * Yes, the PNIC is so brain damaged
+ * that it will sometimes generate a TX
+ * underrun error while DMAing the RX
+ * filter setup frame. If we detect this,
+ * we have to send the setup frame again,
+ * or else the filter won't be programmed
+ * correctly.
+ */
+ if (DC_IS_PNIC(sc)) {
+ if (txstat & DC_TXSTAT_ERRSUM)
+ dc_setfilt(sc);
+ }
+ sc->dc_cdata.dc_tx_chain[idx] = NULL;
+ }
+ sc->dc_cdata.dc_tx_cnt--;
+ DC_INC(idx, DC_TX_LIST_CNT);
+ continue;
+ }
+
+ if (DC_IS_CONEXANT(sc)) {
+ /*
+ * For some reason Conexant chips like
+ * setting the CARRLOST flag even when
+ * the carrier is there. In CURRENT we
+ * have the same problem for Xircom
+ * cards !
+ */
+ if (/*sc->dc_type == DC_TYPE_21143 &&*/
+ sc->dc_pmode == DC_PMODE_MII &&
+ ((txstat & 0xFFFF) & ~(DC_TXSTAT_ERRSUM|
+ DC_TXSTAT_NOCARRIER)))
+ txstat &= ~DC_TXSTAT_ERRSUM;
+ } else {
+ if (/*sc->dc_type == DC_TYPE_21143 &&*/
+ sc->dc_pmode == DC_PMODE_MII &&
+ ((txstat & 0xFFFF) & ~(DC_TXSTAT_ERRSUM|
+ DC_TXSTAT_NOCARRIER|DC_TXSTAT_CARRLOST)))
+ txstat &= ~DC_TXSTAT_ERRSUM;
+ }
+
+ if (txstat & DC_TXSTAT_ERRSUM) {
+ ifp->if_oerrors++;
+ if (txstat & DC_TXSTAT_EXCESSCOLL)
+ ifp->if_collisions++;
+ if (txstat & DC_TXSTAT_LATECOLL)
+ ifp->if_collisions++;
+ if (!(txstat & DC_TXSTAT_UNDERRUN)) {
+ dc_init(sc);
+ return;
+ }
+ }
+
+ ifp->if_collisions += (txstat & DC_TXSTAT_COLLCNT) >> 3;
+
+ ifp->if_opackets++;
+ if (sc->dc_cdata.dc_tx_chain[idx] != NULL) {
+ m_freem(sc->dc_cdata.dc_tx_chain[idx]);
+ sc->dc_cdata.dc_tx_chain[idx] = NULL;
+ }
+
+ sc->dc_cdata.dc_tx_cnt--;
+ DC_INC(idx, DC_TX_LIST_CNT);
+ }
+
+ if (idx != sc->dc_cdata.dc_tx_cons) {
+ /* some buffers have been freed */
+ sc->dc_cdata.dc_tx_cons = idx;
+ ifp->if_flags &= ~IFF_OACTIVE;
+ }
+ ifp->if_timer = (sc->dc_cdata.dc_tx_cnt == 0) ? 0 : 5;
+
+ return;
+}
+
+
+#if 0
+static void dc_tick(xsc)
+ void *xsc;
+{
+ struct dc_softc *sc;
+ /*struct mii_data *mii;*/
+ struct ifnet *ifp;
+ int s;
+ u_int32_t r;
+
+
+ sc = xsc;
+ ifp = &sc->arpcom.ac_if;
+ mii = device_get_softc(sc->dc_miibus);
+
+ if (sc->dc_flags & DC_REDUCED_MII_POLL) {
+ if (sc->dc_flags & DC_21143_NWAY) {
+ r = CSR_READ_4(sc, DC_10BTSTAT);
+ if (IFM_SUBTYPE(mii->mii_media_active) ==
+ IFM_100_TX && (r & DC_TSTAT_LS100)) {
+ sc->dc_link = 0;
+ mii_mediachg(mii);
+ }
+ if (IFM_SUBTYPE(mii->mii_media_active) ==
+ IFM_10_T && (r & DC_TSTAT_LS10)) {
+ sc->dc_link = 0;
+ mii_mediachg(mii);
+ }
+ if (sc->dc_link == 0)
+ mii_tick(mii);
+ } else {
+ r = CSR_READ_4(sc, DC_ISR);
+ if ((r & DC_ISR_RX_STATE) == DC_RXSTATE_WAIT &&
+ sc->dc_cdata.dc_tx_cnt == 0)
+ mii_tick(mii);
+ if (!(mii->mii_media_status & IFM_ACTIVE))
+ sc->dc_link = 0;
+ }
+ } else
+ mii_tick(mii);
+
+ /*
+ * When the init routine completes, we expect to be able to send
+ * packets right away, and in fact the network code will send a
+ * gratuitous ARP the moment the init routine marks the interface
+ * as running. However, even though the MAC may have been initialized,
+ * there may be a delay of a few seconds before the PHY completes
+ * autonegotiation and the link is brought up. Any transmissions
+ * made during that delay will be lost. Dealing with this is tricky:
+ * we can't just pause in the init routine while waiting for the
+ * PHY to come ready since that would bring the whole system to
+ * a screeching halt for several seconds.
+ *
+ * What we do here is prevent the TX start routine from sending
+ * any packets until a link has been established. After the
+ * interface has been initialized, the tick routine will poll
+ * the state of the PHY until the IFM_ACTIVE flag is set. Until
+ * that time, packets will stay in the send queue, and once the
+ * link comes up, they will be flushed out to the wire.
+ */
+ if (!sc->dc_link) {
+ mii_pollstat(mii);
+ if (mii->mii_media_status & IFM_ACTIVE &&
+ IFM_SUBTYPE(mii->mii_media_active) != IFM_NONE) {
+ sc->dc_link++;
+ if (ifp->if_snd.ifq_head != NULL)
+ dc_start(ifp);
+ }
+ }
+
+ if (sc->dc_flags & DC_21143_NWAY && !sc->dc_link)
+ sc->dc_stat_ch = timeout(dc_tick, sc, hz/10);
+ else
+ sc->dc_stat_ch = timeout(dc_tick, sc, hz);
+
+ return;
+}
+#endif
+
+/*
+ * A transmit underrun has occurred. Back off the transmit threshold,
+ * or switch to store and forward mode if we have to.
+ */
+static void dc_tx_underrun(sc)
+ struct dc_softc *sc;
+{
+ u_int32_t isr;
+ int i;
+
+ if (DC_IS_DAVICOM(sc))
+ dc_init(sc);
+
+ if (DC_IS_INTEL(sc)) {
+ /*
+ * The real 21143 requires that the transmitter be idle
+ * in order to change the transmit threshold or store
+ * and forward state.
+ */
+ DC_CLRBIT(sc, DC_NETCFG, DC_NETCFG_TX_ON);
+
+ for (i = 0; i < DC_TIMEOUT; i++) {
+ isr = CSR_READ_4(sc, DC_ISR);
+ if (isr & DC_ISR_TX_IDLE)
+ break;
+ DELAY(10);
+ }
+ if (i == DC_TIMEOUT) {
+ printk("dc%d: failed to force tx to idle state\n",
+ sc->dc_unit);
+ dc_init(sc);
+ }
+ }
+
+ printk("dc%d: TX underrun -- ", sc->dc_unit);
+ sc->dc_txthresh += DC_TXTHRESH_INC;
+ if (sc->dc_txthresh > DC_TXTHRESH_MAX) {
+ printk("using store and forward mode\n");
+ DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_STORENFWD);
+ } else {
+ printk("increasing TX threshold\n");
+ DC_CLRBIT(sc, DC_NETCFG, DC_NETCFG_TX_THRESH);
+ DC_SETBIT(sc, DC_NETCFG, sc->dc_txthresh);
+ }
+
+ if (DC_IS_INTEL(sc))
+ DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_TX_ON);
+
+ return;
+}
+
+#ifdef DEVICE_POLLING
+static poll_handler_t dc_poll;
+
+static void
+dc_poll(struct ifnet *ifp, enum poll_cmd cmd, int count)
+{
+ struct dc_softc *sc = ifp->if_softc;
+
+ if (cmd == POLL_DEREGISTER) { /* final call, enable interrupts */
+ /* Re-enable interrupts. */
+ CSR_WRITE_4(sc, DC_IMR, DC_INTRS);
+ return;
+ }
+ sc->rxcycles = count;
+ dc_rxeof(sc);
+ dc_txeof(sc);
+ if (ifp->if_snd.ifq_head != NULL && !(ifp->if_flags & IFF_OACTIVE))
+ dc_start(ifp);
+
+ if (cmd == POLL_AND_CHECK_STATUS) { /* also check status register */
+ u_int32_t status;
+
+ status = CSR_READ_4(sc, DC_ISR);
+ status &= (DC_ISR_RX_WATDOGTIMEO|DC_ISR_RX_NOBUF|
+ DC_ISR_TX_NOBUF|DC_ISR_TX_IDLE|DC_ISR_TX_UNDERRUN|
+ DC_ISR_BUS_ERR);
+ if (!status)
+ return ;
+ /* ack what we have */
+ CSR_WRITE_4(sc, DC_ISR, status);
+
+ if (status & (DC_ISR_RX_WATDOGTIMEO|DC_ISR_RX_NOBUF) ) {
+ u_int32_t r = CSR_READ_4(sc, DC_FRAMESDISCARDED);
+ ifp->if_ierrors += (r & 0xffff) + ((r >> 17) & 0x7ff);
+
+ if (dc_rx_resync(sc))
+ dc_rxeof(sc);
+ }
+ /* restart transmit unit if necessary */
+ if (status & DC_ISR_TX_IDLE && sc->dc_cdata.dc_tx_cnt)
+ CSR_WRITE_4(sc, DC_TXSTART, 0xFFFFFFFF);
+
+ if (status & DC_ISR_TX_UNDERRUN)
+ dc_tx_underrun(sc);
+
+ if (status & DC_ISR_BUS_ERR) {
+ printk("dc_poll: dc%d bus error\n", sc->dc_unit);
+ dc_reset(sc);
+ dc_init(sc);
+ }
+ }
+}
+#endif /* DEVICE_POLLING */
+
+static void
+dc_intr(void* arg)
+{
+ /* Need to make this work for multiple devices ... eventually */
+ struct dc_softc *sc = (struct dc_softc *)arg;
+
+
+ /* Disable interrupts. */
+ CSR_WRITE_4(sc, DC_IMR, 0x00000000);
+
+ rtems_bsdnet_event_send(sc->daemontid, IRQ_EVENT);
+#if 0
+ if (sc->suspended) {
+ return;
+ }
+
+ ifp = &sc->arpcom.ac_if;
+
+#ifdef DEVICE_POLLING
+ if (ifp->if_ipending & IFF_POLLING)
+ return;
+ if (ether_poll_register(dc_poll, ifp)) { /* ok, disable interrupts */
+ CSR_WRITE_4(sc, DC_IMR, 0x00000000);
+ return;
+ }
+#endif /* DEVICE_POLLING */
+ if ( (CSR_READ_4(sc, DC_ISR) & DC_INTRS) == 0)
+ return ;
+
+ /* Suppress unwanted interrupts */
+ if (!(ifp->if_flags & IFF_UP)) {
+ if (CSR_READ_4(sc, DC_ISR) & DC_INTRS)
+ dc_stop(sc);
+ return;
+ }
+#endif
+}
+
+
+static void
+dc_daemon(void * arg)
+{
+ struct dc_softc *sc = (struct dc_softc *)arg;
+ struct ifnet *ifp;
+ u_int32_t status;
+ rtems_event_set events;
+
+
+ for(;;) {
+ rtems_bsdnet_event_receive(RTEMS_ALL_EVENTS, \
+ RTEMS_WAIT | RTEMS_EVENT_ANY, \
+ RTEMS_NO_TIMEOUT,
+ &events);
+
+
+ ifp = &sc->arpcom.ac_if;
+
+ while((status = CSR_READ_4(sc, DC_ISR)) & DC_INTRS) {
+
+ CSR_WRITE_4(sc, DC_ISR, status);
+
+ if (status & DC_ISR_RX_OK) {
+ int curpkts;
+ curpkts = ifp->if_ipackets;
+ dc_rxeof(sc);
+ if (curpkts == ifp->if_ipackets) {
+ while(dc_rx_resync(sc))
+ dc_rxeof(sc);
+ }
+ }
+
+ if (status & (DC_ISR_TX_OK|DC_ISR_TX_NOBUF))
+ dc_txeof(sc);
+
+ if (status & DC_ISR_TX_IDLE) {
+ dc_txeof(sc);
+ if (sc->dc_cdata.dc_tx_cnt) {
+ DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_TX_ON);
+ CSR_WRITE_4(sc, DC_TXSTART, 0xFFFFFFFF);
+ }
+ }
+
+ if (status & DC_ISR_TX_UNDERRUN)
+ dc_tx_underrun(sc);
+
+ if ((status & DC_ISR_RX_WATDOGTIMEO)
+ || (status & DC_ISR_RX_NOBUF)) {
+ int curpkts;
+ curpkts = ifp->if_ipackets;
+ dc_rxeof(sc);
+ if (curpkts == ifp->if_ipackets) {
+ while(dc_rx_resync(sc))
+ dc_rxeof(sc);
+ }
+ }
+
+ if (status & DC_ISR_BUS_ERR) {
+ dc_reset(sc);
+ dc_init(sc);
+ }
+ }
+
+ /* Make atomic !!! */
+ /* Re-enable interrupts. */
+ CSR_WRITE_4(sc, DC_IMR, DC_INTRS);
+
+ if (ifp->if_snd.ifq_head != NULL)
+ dc_start(ifp);
+ }
+
+}
+
+
+/*
+ * Encapsulate an mbuf chain in a descriptor by coupling the mbuf data
+ * pointers to the fragment pointers.
+ */
+static int dc_encap(sc, m_head, txidx)
+ struct dc_softc *sc;
+ struct mbuf *m_head;
+ u_int32_t *txidx;
+{
+ struct dc_desc *f = NULL;
+ struct mbuf *m;
+ int frag, cur, cnt = 0;
+
+ /*
+ * Start packing the mbufs in this chain into
+ * the fragment pointers. Stop when we run out
+ * of fragments or hit the end of the mbuf chain.
+ */
+ m = m_head;
+ cur = frag = *txidx;
+
+ for (m = m_head; m != NULL; m = m->m_next) {
+ if (m->m_len != 0) {
+ if (sc->dc_flags & DC_TX_ADMTEK_WAR) {
+ if (*txidx != sc->dc_cdata.dc_tx_prod &&
+ frag == (DC_TX_LIST_CNT - 1))
+ return(ENOBUFS);
+ }
+ if ((DC_TX_LIST_CNT -
+ (sc->dc_cdata.dc_tx_cnt + cnt)) < 5)
+ return(ENOBUFS);
+
+ f = &sc->dc_ldata->dc_tx_list[frag];
+ f->dc_ctl = DC_TXCTL_TLINK | m->m_len;
+ if (cnt == 0) {
+ f->dc_status = 0;
+ f->dc_ctl |= DC_TXCTL_FIRSTFRAG;
+ } else
+ f->dc_status = DC_TXSTAT_OWN;
+ f->dc_data = vtophys(mtod(m, vm_offset_t));
+ cur = frag;
+ DC_INC(frag, DC_TX_LIST_CNT);
+ cnt++;
+ }
+ }
+
+ if (m != NULL)
+ return(ENOBUFS);
+
+ sc->dc_cdata.dc_tx_cnt += cnt;
+ sc->dc_cdata.dc_tx_chain[cur] = m_head;
+ sc->dc_ldata->dc_tx_list[cur].dc_ctl |= DC_TXCTL_LASTFRAG;
+ if (sc->dc_flags & DC_TX_INTR_FIRSTFRAG)
+ sc->dc_ldata->dc_tx_list[*txidx].dc_ctl |= DC_TXCTL_FINT;
+ if (sc->dc_flags & DC_TX_INTR_ALWAYS)
+ sc->dc_ldata->dc_tx_list[cur].dc_ctl |= DC_TXCTL_FINT;
+ if (sc->dc_flags & DC_TX_USE_TX_INTR && sc->dc_cdata.dc_tx_cnt > 64)
+ sc->dc_ldata->dc_tx_list[cur].dc_ctl |= DC_TXCTL_FINT;
+ sc->dc_ldata->dc_tx_list[*txidx].dc_status = DC_TXSTAT_OWN;
+ *txidx = frag;
+
+ return(0);
+}
+
+/*
+ * Coalesce an mbuf chain into a single mbuf cluster buffer.
+ * Needed for some really badly behaved chips that just can't
+ * do scatter/gather correctly.
+ */
+static int dc_coal(sc, m_head)
+ struct dc_softc *sc;
+ struct mbuf **m_head;
+{
+ struct mbuf *m_new, *m;
+
+ m = *m_head;
+ MGETHDR(m_new, M_DONTWAIT, MT_DATA);
+ if (m_new == NULL)
+ return(ENOBUFS);
+ if (m->m_pkthdr.len > MHLEN) {
+ MCLGET(m_new, M_DONTWAIT);
+ if (!(m_new->m_flags & M_EXT)) {
+ m_freem(m_new);
+ return(ENOBUFS);
+ }
+ }
+ m_copydata(m, 0, m->m_pkthdr.len, mtod(m_new, caddr_t));
+ m_new->m_pkthdr.len = m_new->m_len = m->m_pkthdr.len;
+ m_freem(m);
+ *m_head = m_new;
+
+ return(0);
+}
+
+/*
+ * Main transmit routine. To avoid having to do mbuf copies, we put pointers
+ * to the mbuf data regions directly in the transmit lists. We also save a
+ * copy of the pointers since the transmit list fragment pointers are
+ * physical addresses.
+ */
+
+static void dc_start(ifp)
+ struct ifnet *ifp;
+{
+ struct dc_softc *sc;
+ struct mbuf *m_head = NULL;
+ u_int32_t idx;
+
+ sc = ifp->if_softc;
+#if 0
+ if (!sc->dc_link && ifp->if_snd.ifq_len < 10)
+ return;
+#endif
+ if (ifp->if_flags & IFF_OACTIVE)
+ return;
+
+ idx = sc->dc_cdata.dc_tx_prod;
+
+ while(sc->dc_cdata.dc_tx_chain[idx] == NULL) {
+ IF_DEQUEUE(&ifp->if_snd, m_head);
+ if (m_head == NULL)
+ break;
+
+ if (sc->dc_flags & DC_TX_COALESCE &&
+ m_head->m_next != NULL) {
+ /* only coalesce if have >1 mbufs */
+ if (dc_coal(sc, &m_head)) {
+ IF_PREPEND(&ifp->if_snd, m_head);
+ ifp->if_flags |= IFF_OACTIVE;
+ break;
+ }
+ }
+
+ if (dc_encap(sc, m_head, &idx)) {
+ IF_PREPEND(&ifp->if_snd, m_head);
+ ifp->if_flags |= IFF_OACTIVE;
+ break;
+ }
+#if 0
+ /*
+ * If there's a BPF listener, bounce a copy of this frame
+ * to him.
+ */
+ if (ifp->if_bpf)
+ bpf_mtap(ifp, m_head);
+#endif
+ if (sc->dc_flags & DC_TX_ONE) {
+ ifp->if_flags |= IFF_OACTIVE;
+ break;
+ }
+ }
+
+ /* Transmit */
+ sc->dc_cdata.dc_tx_prod = idx;
+ if (!(sc->dc_flags & DC_TX_POLL))
+ CSR_WRITE_4(sc, DC_TXSTART, 0xFFFFFFFF);
+
+ /*
+ * Set a timeout in case the chip goes out to lunch.
+ */
+ ifp->if_timer = 5;
+
+ return;
+}
+
+static void dc_init(xsc)
+ void *xsc;
+{
+ struct dc_softc *sc = xsc;
+ struct ifnet *ifp = &sc->arpcom.ac_if;
+ /*struct mii_data *mii;*/
+
+
+ /*mii = device_get_softc(sc->dc_miibus);*/
+
+ /*
+ * Cancel pending I/O and free all RX/TX buffers.
+ */
+ dc_stop(sc);
+ dc_reset(sc);
+
+ /*
+ * Set cache alignment and burst length.
+ */
+ if (DC_IS_ASIX(sc) || DC_IS_DAVICOM(sc))
+ CSR_WRITE_4(sc, DC_BUSCTL, 0);
+ else
+ CSR_WRITE_4(sc, DC_BUSCTL, DC_BUSCTL_MRME|DC_BUSCTL_MRLE);
+ /*
+ * Evenly share the bus between receive and transmit process.
+ */
+ if (DC_IS_INTEL(sc))
+ DC_SETBIT(sc, DC_BUSCTL, DC_BUSCTL_ARBITRATION);
+ if (DC_IS_DAVICOM(sc) || DC_IS_INTEL(sc)) {
+ DC_SETBIT(sc, DC_BUSCTL, DC_BURSTLEN_USECA);
+ } else {
+ DC_SETBIT(sc, DC_BUSCTL, DC_BURSTLEN_16LONG);
+ }
+ if (sc->dc_flags & DC_TX_POLL)
+ DC_SETBIT(sc, DC_BUSCTL, DC_TXPOLL_1);
+ switch(sc->dc_cachesize) {
+ case 32:
+ DC_SETBIT(sc, DC_BUSCTL, DC_CACHEALIGN_32LONG);
+ break;
+ case 16:
+ DC_SETBIT(sc, DC_BUSCTL, DC_CACHEALIGN_16LONG);
+ break;
+ case 8:
+ DC_SETBIT(sc, DC_BUSCTL, DC_CACHEALIGN_8LONG);
+ break;
+ case 0:
+ default:
+ DC_SETBIT(sc, DC_BUSCTL, DC_CACHEALIGN_NONE);
+ break;
+ }
+
+ if (sc->dc_flags & DC_TX_STORENFWD)
+ DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_STORENFWD);
+ else {
+ if (sc->dc_txthresh > DC_TXTHRESH_MAX) {
+ DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_STORENFWD);
+ } else {
+ DC_CLRBIT(sc, DC_NETCFG, DC_NETCFG_STORENFWD);
+ DC_SETBIT(sc, DC_NETCFG, sc->dc_txthresh);
+ }
+ }
+
+ DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_NO_RXCRC);
+ DC_CLRBIT(sc, DC_NETCFG, DC_NETCFG_TX_BACKOFF);
+
+ if (DC_IS_MACRONIX(sc) || DC_IS_PNICII(sc)) {
+ /*
+ * The app notes for the 98713 and 98715A say that
+ * in order to have the chips operate properly, a magic
+ * number must be written to CSR16. Macronix does not
+ * document the meaning of these bits so there's no way
+ * to know exactly what they do. The 98713 has a magic
+ * number all its own; the rest all use a different one.
+ */
+ DC_CLRBIT(sc, DC_MX_MAGICPACKET, 0xFFFF0000);
+ if (sc->dc_type == DC_TYPE_98713)
+ DC_SETBIT(sc, DC_MX_MAGICPACKET, DC_MX_MAGIC_98713);
+ else
+ DC_SETBIT(sc, DC_MX_MAGICPACKET, DC_MX_MAGIC_98715);
+ }
+
+ DC_CLRBIT(sc, DC_NETCFG, DC_NETCFG_TX_THRESH);
+ DC_SETBIT(sc, DC_NETCFG, DC_TXTHRESH_MIN);
+
+ /* Init circular RX list. */
+ if (dc_list_rx_init(sc) == ENOBUFS) {
+ printk("dc%d: initialization failed: no "
+ "memory for rx buffers\n", sc->dc_unit);
+ dc_stop(sc);
+ return;
+ }
+
+ /*
+ * Init tx descriptors.
+ */
+ dc_list_tx_init(sc);
+
+ /*
+ * Load the address of the RX list.
+ */
+ CSR_WRITE_4(sc, DC_RXADDR, vtophys(&sc->dc_ldata->dc_rx_list[0]));
+ CSR_WRITE_4(sc, DC_TXADDR, vtophys(&sc->dc_ldata->dc_tx_list[0]));
+
+ /*
+ * Enable interrupts.
+ */
+#ifdef DEVICE_POLLING
+ /*
+ * ... but only if we are not polling, and make sure they are off in
+ * the case of polling. Some cards (e.g. fxp) turn interrupts on
+ * after a reset.
+ */
+ if (ifp->if_ipending & IFF_POLLING)
+ CSR_WRITE_4(sc, DC_IMR, 0x00000000);
+ else
+#endif
+ /* Enable interrupts */
+ CSR_WRITE_4(sc, DC_IMR, DC_INTRS);
+ CSR_WRITE_4(sc, DC_ISR, 0xFFFFFFFF);
+
+ /* Enable transmitter. */
+ DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_TX_ON);
+
+ /*
+ * If this is an Intel 21143 and we're not using the
+ * MII port, program the LED control pins so we get
+ * link and activity indications.
+ */
+ if (sc->dc_flags & DC_TULIP_LEDS) {
+ CSR_WRITE_4(sc, DC_WATCHDOG,
+ DC_WDOG_CTLWREN|DC_WDOG_LINK|DC_WDOG_ACTIVITY);
+ CSR_WRITE_4(sc, DC_WATCHDOG, 0);
+ }
+
+ /*
+ * Load the RX/multicast filter. We do this sort of late
+ * because the filter programming scheme on the 21143 and
+ * some clones requires DMAing a setup frame via the TX
+ * engine, and we need the transmitter enabled for that.
+ */
+ dc_setfilt(sc);
+
+ /* Enable receiver. */
+ DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_RX_ON);
+ CSR_WRITE_4(sc, DC_RXSTART, 0xFFFFFFFF);
+
+ /*mii_mediachg(mii);*/
+ dc_setcfg(sc, sc->dc_if_media);
+
+ ifp->if_flags |= IFF_RUNNING;
+ ifp->if_flags &= ~IFF_OACTIVE;
+
+
+#if 0
+
+ /* Don't start the ticker if this is a homePNA link. */
+ if (IFM_SUBTYPE(mii->mii_media.ifm_media) == IFM_homePNA)
+ sc->dc_link = 1;
+ else {
+ if (sc->dc_flags & DC_21143_NWAY)
+ sc->dc_stat_ch = timeout(dc_tick, sc, hz/10);
+ else
+ sc->dc_stat_ch = timeout(dc_tick, sc, hz);
+ }
+
+#ifdef SRM_MEDIA
+ if(sc->dc_srm_media) {
+ struct ifreq ifr;
+
+ ifr.ifr_media = sc->dc_srm_media;
+ ifmedia_ioctl(ifp, &ifr, &mii->mii_media, SIOCSIFMEDIA);
+ sc->dc_srm_media = 0;
+ }
+#endif
+#endif /* end if (0) */
+ return;
+}
+
+
+#if 0
+/*
+ * Set media options.
+ */
+static int dc_ifmedia_upd(ifp)
+ struct ifnet *ifp;
+{
+ struct dc_softc *sc;
+ struct mii_data *mii;
+ struct ifmedia *ifm;
+
+ sc = ifp->if_softc;
+ mii = device_get_softc(sc->dc_miibus);
+ mii_mediachg(mii);
+ ifm = &mii->mii_media;
+
+ if (DC_IS_DAVICOM(sc) &&
+ IFM_SUBTYPE(ifm->ifm_media) == IFM_homePNA)
+ dc_setcfg(sc, ifm->ifm_media);
+ else
+ sc->dc_link = 0;
+
+ return(0);
+}
+
+/*
+ * Report current media status.
+ */
+static void dc_ifmedia_sts(ifp, ifmr)
+ struct ifnet *ifp;
+ struct ifmediareq *ifmr;
+{
+ struct dc_softc *sc;
+ struct mii_data *mii;
+ struct ifmedia *ifm;
+
+ sc = ifp->if_softc;
+ mii = device_get_softc(sc->dc_miibus);
+ mii_pollstat(mii);
+ ifm = &mii->mii_media;
+ if (DC_IS_DAVICOM(sc)) {
+ if (IFM_SUBTYPE(ifm->ifm_media) == IFM_homePNA) {
+ ifmr->ifm_active = ifm->ifm_media;
+ ifmr->ifm_status = 0;
+ return;
+ }
+ }
+ ifmr->ifm_active = mii->mii_media_active;
+ ifmr->ifm_status = mii->mii_media_status;
+
+ return;
+}
+#endif
+
+
+static int dc_ioctl(ifp, command, data)
+ struct ifnet *ifp;
+ ioctl_command_t command;
+ caddr_t data;
+{
+ struct dc_softc *sc = ifp->if_softc;
+ /*struct ifreq *ifr = (struct ifreq *) data;
+ struct mii_data *mii;*/
+ int error = 0;
+
+
+ switch(command) {
+ case SIOCSIFADDR:
+ case SIOCGIFADDR:
+ case SIOCSIFMTU:
+ error = ether_ioctl(ifp, command, data);
+ break;
+ case SIOCSIFFLAGS:
+ if (ifp->if_flags & IFF_UP) {
+ int need_setfilt = (ifp->if_flags ^ sc->dc_if_flags) &
+ (IFF_PROMISC | IFF_ALLMULTI);
+ if (ifp->if_flags & IFF_RUNNING) {
+ if (need_setfilt)
+ dc_setfilt(sc);
+ } else {
+ sc->dc_txthresh = 0;
+ dc_init(sc);
+ }
+ } else {
+ if (ifp->if_flags & IFF_RUNNING)
+ dc_stop(sc);
+ }
+ sc->dc_if_flags = ifp->if_flags;
+ error = 0;
+ break;
+ case SIOCADDMULTI:
+ case SIOCDELMULTI:
+ dc_setfilt(sc);
+ error = 0;
+ break;
+#if 0
+ case SIOCGIFMEDIA:
+ case SIOCSIFMEDIA:
+ mii = device_get_softc(sc->dc_miibus);
+ error = ifmedia_ioctl(ifp, ifr, &mii->mii_media, command);
+#ifdef SRM_MEDIA
+ if (sc->dc_srm_media)
+ sc->dc_srm_media = 0;
+#endif
+ break;
+#endif
+ default:
+ error = EINVAL;
+ break;
+ }
+
+
+ return(error);
+}
+
+static void dc_watchdog(ifp)
+ struct ifnet *ifp;
+{
+ struct dc_softc *sc;
+
+ sc = ifp->if_softc;
+
+ ifp->if_oerrors++;
+ printk("dc%d: watchdog timeout\n", sc->dc_unit);
+
+ dc_stop(sc);
+ dc_reset(sc);
+ dc_init(sc);
+
+ if (ifp->if_snd.ifq_head != NULL)
+ dc_start(ifp);
+
+ return;
+}
+
+/*
+ * Stop the adapter and free any mbufs allocated to the
+ * RX and TX lists.
+ */
+static void dc_stop(sc)
+ struct dc_softc *sc;
+{
+ register int i;
+ struct ifnet *ifp;
+
+ ifp = &sc->arpcom.ac_if;
+ ifp->if_timer = 0;
+
+ /*untimeout(dc_tick, sc, sc->dc_stat_ch);*/
+
+ ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE);
+#ifdef DEVICE_POLLING
+ ether_poll_deregister(ifp);
+#endif
+
+ DC_CLRBIT(sc, DC_NETCFG, (DC_NETCFG_RX_ON|DC_NETCFG_TX_ON));
+ CSR_WRITE_4(sc, DC_IMR, 0x00000000);
+ CSR_WRITE_4(sc, DC_TXADDR, 0x00000000);
+ CSR_WRITE_4(sc, DC_RXADDR, 0x00000000);
+ sc->dc_link = 0;
+
+ /*
+ * Free data in the RX lists.
+ */
+ for (i = 0; i < DC_RX_LIST_CNT; i++) {
+ if (sc->dc_cdata.dc_rx_chain[i] != NULL) {
+ m_freem(sc->dc_cdata.dc_rx_chain[i]);
+ sc->dc_cdata.dc_rx_chain[i] = NULL;
+ }
+ }
+ bzero((char *)&sc->dc_ldata->dc_rx_list,
+ sizeof(sc->dc_ldata->dc_rx_list));
+
+ /*
+ * Free the TX list buffers.
+ */
+ for (i = 0; i < DC_TX_LIST_CNT; i++) {
+ if (sc->dc_cdata.dc_tx_chain[i] != NULL) {
+ if (sc->dc_ldata->dc_tx_list[i].dc_ctl &
+ DC_TXCTL_SETUP) {
+ sc->dc_cdata.dc_tx_chain[i] = NULL;
+ continue;
+ }
+ m_freem(sc->dc_cdata.dc_tx_chain[i]);
+ sc->dc_cdata.dc_tx_chain[i] = NULL;
+ }
+ }
+
+ bzero((char *)&sc->dc_ldata->dc_tx_list,
+ sizeof(sc->dc_ldata->dc_tx_list));
+
+ return;
+}
+
+
+#if 0
+/*
+ * Stop all chip I/O so that the kernel's probe routines don't
+ * get confused by errant DMAs when rebooting.
+ */
+static void dc_shutdown(dev)
+ device_t dev;
+{
+ struct dc_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ dc_stop(sc);
+
+ return;
+}
+
+/*
+ * Device suspend routine. Stop the interface and save some PCI
+ * settings in case the BIOS doesn't restore them properly on
+ * resume.
+ */
+static int dc_suspend(dev)
+ device_t dev;
+{
+ register int i;
+ int s;
+ struct dc_softc *sc;
+
+
+ sc = device_get_softc(dev);
+
+ dc_stop(sc);
+
+ for (i = 0; i < 5; i++)
+ sc->saved_maps[i] = pci_read_config(dev, PCIR_MAPS + i * 4, 4);
+ sc->saved_biosaddr = pci_read_config(dev, PCIR_BIOS, 4);
+ sc->saved_intline = pci_read_config(dev, PCIR_INTLINE, 1);
+ sc->saved_cachelnsz = pci_read_config(dev, PCIR_CACHELNSZ, 1);
+ sc->saved_lattimer = pci_read_config(dev, PCIR_LATTIMER, 1);
+
+ sc->suspended = 1;
+
+ return (0);
+}
+
+/*
+ * Device resume routine. Restore some PCI settings in case the BIOS
+ * doesn't, re-enable busmastering, and restart the interface if
+ * appropriate.
+ */
+static int dc_resume(dev)
+ device_t dev;
+{
+ register int i;
+ int s;
+ struct dc_softc *sc;
+ struct ifnet *ifp;
+
+
+ sc = device_get_softc(dev);
+ ifp = &sc->arpcom.ac_if;
+
+ dc_acpi(dev);
+
+ /* better way to do this? */
+ for (i = 0; i < 5; i++)
+ pci_write_config(dev, PCIR_MAPS + i * 4, sc->saved_maps[i], 4);
+ pci_write_config(dev, PCIR_BIOS, sc->saved_biosaddr, 4);
+ pci_write_config(dev, PCIR_INTLINE, sc->saved_intline, 1);
+ pci_write_config(dev, PCIR_CACHELNSZ, sc->saved_cachelnsz, 1);
+ pci_write_config(dev, PCIR_LATTIMER, sc->saved_lattimer, 1);
+
+ /* reenable busmastering */
+ pci_enable_busmaster(dev);
+ pci_enable_io(dev, DC_RES);
+
+ /* reinitialize interface if necessary */
+ if (ifp->if_flags & IFF_UP)
+ dc_init(sc);
+
+ sc->suspended = 0;
+
+ return (0);
+}
+#endif
+
+#endif /* end if supported */
diff --git a/bsps/shared/net/if_fxp.c b/bsps/shared/net/if_fxp.c
new file mode 100644
index 0000000000..2fe9a5c403
--- /dev/null
+++ b/bsps/shared/net/if_fxp.c
@@ -0,0 +1,2339 @@
+/*-
+ * Copyright (c) 1995, David Greenman
+ * Copyright (c) 2001 Jonathan Lemon <jlemon@freebsd.org>
+ * 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 unmodified, 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 AUTHOR 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 AUTHOR 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.
+ *
+ * $FreeBSD: src/sys/dev/fxp/if_fxp.c,v 1.118 2001/09/05 23:33:58 brooks Exp $
+ */
+
+/*
+ * Intel EtherExpress Pro/100B PCI Fast Ethernet driver
+ */
+
+/*
+ * RTEMS Revision Preliminary History
+ *
+ * July XXX, 2002 W. Eric Norum <eric.norum@usask.ca>
+ * Placed in RTEMS CVS repository. All further modifications will be
+ * noted in the CVS log and not in this comment.
+ *
+ * July 11, 2002 W. Eric Norum <eric.norum@usask.ca>
+ * Minor modifications to get driver working with NIC on VersaLogic
+ * Bobcat PC-104 single-board computer. The Bobcat has no video
+ * driver so printf/printk calls are directed to COM2:. This
+ * arrangement seems to require delays after the printk calls or
+ * else things lock up. Perhaps the RTEMS pc386 console code
+ * should be modified to insert these delays itself.
+ *
+ * June 27, 2002 W. Eric Norum <eric.norum@usask.ca>
+ * Obtained from Thomas Doerfler <Thomas.Doerfler@imd-systems.de>.
+ * A big thank-you to Thomas for making this available.
+ *
+ * October 01, 2001 Thomas Doerfler <Thomas.Doerfler@imd-systems.de>
+ * Original RTEMS modifications.
+ */
+
+#define __INSIDE_RTEMS_BSD_TCPIP_STACK__
+
+#if defined(__i386__)
+
+/*#define DEBUG_OUT 0*/
+
+#include <rtems.h>
+#include <rtems/error.h>
+#include <rtems/rtems_bsdnet.h>
+#include <bsp.h>
+#include <inttypes.h>
+
+#include <errno.h>
+#include <sys/param.h>
+#include <sys/mbuf.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <netinet/if_ether.h>
+#include <sys/malloc.h>
+#include <sys/systm.h>
+#include <bsp.h>
+#include <bsp/irq.h>
+#include <bsp/irq-generic.h>
+#include <rtems/pci.h>
+
+#ifdef NS
+#include <netns/ns.h>
+#include <netns/ns_if.h>
+#endif
+
+#include <net/bpf.h>
+
+#include <vm/vm.h> /* for vtophys */
+
+#include <net/if_types.h>
+
+#include "if_fxpreg.h"
+#include <libchip/if_fxpvar.h>
+
+/*
+ * some adaptation replacements for RTEMS
+ */
+static rtems_interval fxp_ticksPerSecond;
+#define device_printf(device,format,args...) printk(format,## args)
+#define DELAY(n) rtems_task_wake_after(((n)*fxp_ticksPerSecond/1000000)+1)
+#ifdef DEBUG_OUT
+#define DBGLVL_PRINTK(LVL,format, args...) \
+if (DEBUG_OUT >= (LVL)) { \
+ printk(format, ## args); \
+}
+#else
+#define DBGLVL_PRINTK(LVL,format, args...)
+#endif
+
+/*
+ * RTEMS event used by interrupt handler to signal driver tasks.
+ * This must not be any of the events used by the network task synchronization.
+ */
+#define INTERRUPT_EVENT RTEMS_EVENT_1
+
+/*
+ * remapping between PCI device and CPU memmory address view...
+ */
+#if defined(__i386)
+#define vtophys(p) (u_int32_t)(p)
+#else
+#define vtophys(p) vtophys(p)
+#endif
+
+#define NFXPDRIVER 1
+static struct fxp_softc fxp_softc[NFXPDRIVER];
+static bool fxp_is_verbose = true;
+/*
+ * NOTE! On the Alpha, we have an alignment constraint. The
+ * card DMAs the packet immediately following the RFA. However,
+ * the first thing in the packet is a 14-byte Ethernet header.
+ * This means that the packet is misaligned. To compensate,
+ * we actually offset the RFA 2 bytes into the cluster. This
+ * alignes the packet after the Ethernet header at a 32-bit
+ * boundary. HOWEVER! This means that the RFA is misaligned!
+ */
+#define RFA_ALIGNMENT_FUDGE 2
+
+/*
+ * Set initial transmit threshold at 64 (512 bytes). This is
+ * increased by 64 (512 bytes) at a time, to maximum of 192
+ * (1536 bytes), if an underrun occurs.
+ */
+static int tx_threshold = 64;
+
+/*
+ * The configuration byte map has several undefined fields which
+ * must be one or must be zero. Set up a template for these bits
+ * only, (assuming a 82557 chip) leaving the actual configuration
+ * to fxp_init.
+ *
+ * See struct fxp_cb_config for the bit definitions.
+ */
+static u_char fxp_cb_config_template[] = {
+ 0x0, 0x0, /* cb_status */
+ 0x0, 0x0, /* cb_command */
+ 0x0, 0x0, 0x0, 0x0, /* link_addr */
+ 0x0, /* 0 */
+ 0x0, /* 1 */
+ 0x0, /* 2 */
+ 0x0, /* 3 */
+ 0x0, /* 4 */
+ 0x0, /* 5 */
+ 0x32, /* 6 */
+ 0x0, /* 7 */
+ 0x0, /* 8 */
+ 0x0, /* 9 */
+ 0x6, /* 10 */
+ 0x0, /* 11 */
+ 0x0, /* 12 */
+ 0x0, /* 13 */
+ 0xf2, /* 14 */
+ 0x48, /* 15 */
+ 0x0, /* 16 */
+ 0x40, /* 17 */
+ 0xf0, /* 18 */
+ 0x0, /* 19 */
+ 0x3f, /* 20 */
+ 0x5 /* 21 */
+};
+
+struct fxp_ident {
+ u_int16_t devid;
+ char *name;
+ int warn;
+};
+
+#define UNTESTED 1
+
+/*
+ * Claim various Intel PCI device identifiers for this driver. The
+ * sub-vendor and sub-device field are extensively used to identify
+ * particular variants, but we don't currently differentiate between
+ * them.
+ */
+static struct fxp_ident fxp_ident_table[] = {
+ { 0x1229, "Intel Pro 10/100B/100+ Ethernet", 0 },
+ { 0x2449, "Intel Pro/100 Ethernet", UNTESTED },
+ { 0x1209, "Intel Embedded 10/100 Ethernet", 0 },
+ { 0x1029, "Intel Pro/100 Ethernet", UNTESTED },
+ { 0x1030, "Intel Pro/100 Ethernet", 0 },
+ { 0x1031, "Intel Pro/100 Ethernet", UNTESTED },
+ { 0x1032, "Intel Pro/100 Ethernet", UNTESTED },
+ { 0x1033, "Intel Pro/100 Ethernet", UNTESTED },
+ { 0x1034, "Intel Pro/100 Ethernet", UNTESTED },
+ { 0x1035, "Intel Pro/100 Ethernet", UNTESTED },
+ { 0x1036, "Intel Pro/100 Ethernet", UNTESTED },
+ { 0x1037, "Intel Pro/100 Ethernet", UNTESTED },
+ { 0x1038, "Intel Pro/100 Ethernet", UNTESTED },
+ { 0x103B, "Intel Pro/100 Ethernet (82801BD PRO/100 VM (LOM))", 0 },
+ { 0, NULL, 0 }
+};
+
+#if 0
+static int fxp_probe(device_t dev);
+static int fxp_attach(device_t dev);
+static int fxp_detach(device_t dev);
+static int fxp_shutdown(device_t dev);
+#endif
+int fxp_output (struct ifnet *,
+ struct mbuf *, struct sockaddr *, struct rtentry *);
+
+
+static void fxp_intr(void *arg);
+static void fxp_init(void *xsc);
+static void fxp_tick(void *xsc);
+static void fxp_start(struct ifnet *ifp);
+static void fxp_stop(struct fxp_softc *sc);
+static void fxp_release(struct fxp_softc *sc);
+static int fxp_ioctl(struct ifnet *ifp, ioctl_command_t command,
+ caddr_t data);
+static void fxp_watchdog(struct ifnet *ifp);
+static int fxp_add_rfabuf(struct fxp_softc *sc, struct mbuf *oldm);
+static void fxp_mc_setup(struct fxp_softc *sc);
+static u_int16_t fxp_eeprom_getword(struct fxp_softc *sc, int offset,
+ int autosize);
+static void fxp_eeprom_putword(struct fxp_softc *sc, int offset,
+ u_int16_t data);
+static void fxp_autosize_eeprom(struct fxp_softc *sc);
+static void fxp_read_eeprom(struct fxp_softc *sc, u_short *data,
+ int offset, int words);
+static void fxp_write_eeprom(struct fxp_softc *sc, u_short *data,
+ int offset, int words);
+#ifdef NOTUSED
+static int fxp_ifmedia_upd(struct ifnet *ifp);
+static void fxp_ifmedia_sts(struct ifnet *ifp,
+ struct ifmediareq *ifmr);
+static int fxp_serial_ifmedia_upd(struct ifnet *ifp);
+static void fxp_serial_ifmedia_sts(struct ifnet *ifp,
+ struct ifmediareq *ifmr);
+static volatile int fxp_miibus_readreg(device_t dev, int phy, int reg);
+static void fxp_miibus_writereg(device_t dev, int phy, int reg,
+ int value);
+#endif
+static __inline void fxp_lwcopy(volatile u_int32_t *src,
+ volatile u_int32_t *dst);
+static __inline void fxp_scb_wait(struct fxp_softc *sc);
+static __inline void fxp_scb_cmd(struct fxp_softc *sc, int cmd);
+static __inline void fxp_dma_wait(volatile u_int16_t *status,
+ struct fxp_softc *sc);
+
+/*
+ * Inline function to copy a 16-bit aligned 32-bit quantity.
+ */
+static __inline void
+fxp_lwcopy(volatile u_int32_t *src, volatile u_int32_t *dst)
+{
+#ifdef __i386__
+ *dst = *src;
+#else
+ volatile u_int16_t *a = (volatile u_int16_t*)src;
+ volatile u_int16_t *b = (volatile u_int16_t*)dst;
+
+ b[0] = a[0];
+ b[1] = a[1];
+#endif
+}
+
+/*
+ * inline access functions to pci space registers
+ */
+static __inline u_int8_t fxp_csr_read_1(struct fxp_softc *sc,int reg) {
+ u_int8_t val;
+ if (sc->pci_regs_are_io) {
+ inport_byte(sc->pci_regs_base + reg,val);
+ }
+ else {
+ val = *(volatile u_int8_t*)(sc->pci_regs_base+reg);
+ }
+ return val;
+}
+static __inline u_int32_t fxp_csr_read_2(struct fxp_softc *sc,int reg) {
+ u_int16_t val;
+ if (sc->pci_regs_are_io) {
+ inport_word(sc->pci_regs_base + reg,val);
+ }
+ else {
+ val = *(volatile u_int16_t*)(sc->pci_regs_base+reg);
+ }
+ return val;
+}
+static __inline u_int32_t fxp_csr_read_4(struct fxp_softc *sc,int reg) {
+ u_int32_t val;
+ if (sc->pci_regs_are_io) {
+ inport_long(sc->pci_regs_base + reg,val);
+ }
+ else {
+ val = *(volatile u_int32_t*)(sc->pci_regs_base+reg);
+ }
+ return val;
+}
+
+/*
+ * Wait for the previous command to be accepted (but not necessarily
+ * completed).
+ */
+static __inline void
+fxp_scb_wait(struct fxp_softc *sc)
+{
+ int i = 10000;
+
+ while (CSR_READ_1(sc, FXP_CSR_SCB_COMMAND) && --i)
+ DELAY(2);
+ if (i == 0)
+ device_printf(sc->dev, "SCB timeout: 0x%d 0x%d"
+ "0x%d" PRIx32 "0x%" PRIx32 "\n",
+ CSR_READ_1(sc, FXP_CSR_SCB_COMMAND),
+ CSR_READ_1(sc, FXP_CSR_SCB_STATACK),
+ CSR_READ_1(sc, FXP_CSR_SCB_RUSCUS),
+ CSR_READ_2(sc, FXP_CSR_FLOWCONTROL));
+}
+
+static __inline void
+fxp_scb_cmd(struct fxp_softc *sc, int cmd)
+{
+
+ if (cmd == FXP_SCB_COMMAND_CU_RESUME && sc->cu_resume_bug) {
+ CSR_WRITE_1(sc, FXP_CSR_SCB_COMMAND, FXP_CB_COMMAND_NOP);
+ fxp_scb_wait(sc);
+ }
+ CSR_WRITE_1(sc, FXP_CSR_SCB_COMMAND, cmd);
+}
+
+static __inline void
+fxp_dma_wait(volatile u_int16_t *status, struct fxp_softc *sc)
+{
+ int i = 10000;
+
+ while (!(*status & FXP_CB_STATUS_C) && --i)
+ DELAY(2);
+ if (i == 0)
+ device_printf(sc->dev, "DMA timeout\n");
+}
+
+
+#define FXP_PCI_CONF_ACCESSOR(_confop, _baseop, _type) \
+ \
+ static inline int _confop ( \
+ struct fxp_softc *sc, \
+ int offset, \
+ _type data ) \
+ { \
+ _baseop( \
+ sc->pci_bus, \
+ sc->pci_dev, \
+ sc->pci_fun, \
+ offset, \
+ data \
+ ); \
+ return PCIB_ERR_SUCCESS; \
+ }
+
+FXP_PCI_CONF_ACCESSOR( fxp_pci_conf_read8, pci_read_config_byte, uint8_t * );
+FXP_PCI_CONF_ACCESSOR( fxp_pci_conf_read16, pci_read_config_word, uint16_t * );
+FXP_PCI_CONF_ACCESSOR( fxp_pci_conf_read32, pci_read_config_dword, uint32_t * );
+FXP_PCI_CONF_ACCESSOR( fxp_pci_conf_write8, pci_write_config_byte, uint8_t );
+FXP_PCI_CONF_ACCESSOR( fxp_pci_conf_write16, pci_write_config_word, uint16_t );
+FXP_PCI_CONF_ACCESSOR( fxp_pci_conf_write32, pci_write_config_dword, uint32_t );
+
+static __inline unsigned int fxp_pci_get_vendor(struct fxp_softc *sc) {
+ u_int16_t vendor;
+ fxp_pci_conf_read16(sc, PCI_VENDOR_ID, &vendor);
+ return vendor;
+}
+
+static __inline unsigned int fxp_pci_get_device(struct fxp_softc *sc) {
+ u_int16_t device;
+ fxp_pci_conf_read16(sc, PCI_DEVICE_ID, &device);
+ return device;
+}
+
+static __inline unsigned int fxp_pci_get_subvendor(struct fxp_softc *sc) {
+ u_int16_t subvendor;
+ fxp_pci_conf_read16(sc, PCI_SUBSYSTEM_VENDOR_ID, &subvendor);
+ return subvendor;
+}
+
+static __inline unsigned int fxp_pci_get_subdevice(struct fxp_softc *sc) {
+ u_int16_t subdevice;
+ fxp_pci_conf_read16(sc, PCI_SUBSYSTEM_ID, &subdevice);
+ return subdevice;
+}
+
+static __inline unsigned int fxp_pci_get_revid(struct fxp_softc *sc) {
+ u_int8_t revid;
+ fxp_pci_conf_read8(sc, PCI_REVISION_ID, &revid);
+ return revid;
+}
+
+/* Prototype to avoid warning. This must be a global symbol. */
+int rtems_fxp_attach(struct rtems_bsdnet_ifconfig *config, int attaching);
+
+int
+rtems_fxp_attach(struct rtems_bsdnet_ifconfig *config, int attaching)
+{
+ int error = 0;
+ struct fxp_softc *sc;
+ struct ifnet *ifp;
+ uint16_t val16;
+ uint32_t val32;
+ uint16_t data;
+ int i;
+ int s;
+ int unitNumber;
+ char *unitName;
+ u_int16_t dev_id;
+ u_int8_t interrupt;
+ int mtu;
+
+ /*
+ * Set up some timing values
+ */
+ fxp_ticksPerSecond = rtems_clock_get_ticks_per_second();
+ DBGLVL_PRINTK(1,"fxp_attach called\n");
+
+ /*
+ * Parse driver name
+ */
+ if ((unitNumber = rtems_bsdnet_parse_driver_name (config, &unitName)) < 0)
+ return 0;
+
+ /*
+ * Is driver free?
+ */
+ if ((unitNumber <= 0) || (unitNumber > NFXPDRIVER)) {
+ device_printf(dev,"Bad FXP unit number.\n");
+ return 0;
+ }
+ sc = &fxp_softc[unitNumber - 1];
+ ifp = &sc->arpcom.ac_if;
+ if (ifp->if_softc != NULL) {
+ device_printf(dev,"FXP Driver already in use.\n");
+ return 0;
+ }
+
+ memset(sc, 0, sizeof(*sc));
+#ifdef NOTUSED
+ sc->dev = dev;
+ callout_handle_init(&sc->stat_ch);
+ mtx_init(&sc->sc_mtx, device_get_nameunit(dev), MTX_DEF | MTX_RECURSE);
+#endif
+ s = splimp();
+
+ /*
+ * find device on pci bus
+ */
+ { int j; int pbus, pdev, pfun;
+
+ for (j=0; fxp_ident_table[j].devid; j++ ) {
+ i = pci_find_device( 0x8086, fxp_ident_table[j].devid,
+ unitNumber-1, &pbus, &pdev, &pfun );
+ sc->pci_bus = pbus;
+ sc->pci_dev = pdev;
+ sc->pci_fun = pfun;
+ DBGLVL_PRINTK(2,"fxp_attach: find_devid returned %d ,"
+ "pci bus %d dev %d fun %d \n",
+ i, sc->pci_bus, sc->pci_dev, sc->pci_fun);
+ if (PCIB_ERR_SUCCESS == i) {
+ if ( UNTESTED == fxp_ident_table[j].warn ) {
+ device_printf(dev,
+"WARNING: this chip version has NOT been reported to work under RTEMS yet.\n");
+ device_printf(dev,
+" If it works OK, report it as tested in 'c/src/libchip/network/if_fxp.c'\n");
+ }
+ break;
+ }
+ }
+ }
+
+ /*
+ * FIXME: add search for more device types...
+ */
+ if (i != PCIB_ERR_SUCCESS) {
+ device_printf(dev, "could not find 82559ER device\n");
+ return 0;
+ }
+
+
+ /*
+ * Enable bus mastering. Enable memory space too, in case
+ * BIOS/Prom forgot about it.
+ */
+ fxp_pci_conf_read16(sc, PCI_COMMAND,&val16);
+ val16 |= (PCI_COMMAND_MEMORY|PCI_COMMAND_MASTER);
+ fxp_pci_conf_write16(sc, PCI_COMMAND, val16);
+ DBGLVL_PRINTK(3,"fxp_attach: PCI_COMMAND_write = 0x%x\n",val16);
+ fxp_pci_conf_read16(sc, PCI_COMMAND,&val16);
+ DBGLVL_PRINTK(4,"fxp_attach: PCI_COMMAND_read = 0x%x\n",val16);
+
+ /*
+ * Figure out which we should try first - memory mapping or i/o mapping?
+ * We default to memory mapping. Then we accept an override from the
+ * command line. Then we check to see which one is enabled.
+ */
+#ifdef NOTUSED
+ m1 = PCI_COMMAND_MEMORY;
+ m2 = PCI_COMMAND_IO;
+ prefer_iomap = 0;
+ if (resource_int_value(device_get_name(dev), device_get_unit(dev),
+ "prefer_iomap", &prefer_iomap) == 0 && prefer_iomap != 0) {
+ m1 = PCI_COMMAND_IO;
+ m2 = PCI_COMMAND_MEMORY;
+ }
+
+ if (val & m1) {
+ sc->rtp = ((m1 == PCI_COMMAND_MEMORY)
+ ? SYS_RES_MEMORY : SYS_RES_IOPORT);
+ sc->rgd = ((m1 == PCI_COMMAND_MEMORY)
+ ? FXP_PCI_MMBA : FXP_PCI_IOBA);
+ sc->mem = bus_alloc_resource(dev, sc->rtp, &sc->rgd,
+ 0, ~0, 1, RF_ACTIVE);
+ }
+ if (sc->mem == NULL && (val & m2)) {
+ sc->rtp = ((m2 == PCI_COMMAND_MEMORY)
+ ? SYS_RES_MEMORY : SYS_RES_IOPORT);
+ sc->rgd = ((m2 == PCI_COMMAND_MEMORY)
+ ? FXP_PCI_MMBA : FXP_PCI_IOBA);
+ sc->mem = bus_alloc_resource(dev, sc->rtp, &sc->rgd,
+ 0, ~0, 1, RF_ACTIVE);
+ }
+
+ if (!sc->mem) {
+ device_printf(dev, "could not map device registers\n");
+ error = ENXIO;
+ goto fail;
+ }
+ if (fxp_is_verbose) {
+ device_printf(dev, "using %s space register mapping\n",
+ sc->rtp == SYS_RES_MEMORY? "memory" : "I/O");
+ }
+
+ sc->sc_st = rman_get_bustag(sc->mem);
+ sc->sc_sh = rman_get_bushandle(sc->mem);
+
+ /*
+ * Allocate our interrupt.
+ */
+ rid = 0;
+ sc->irq = bus_alloc_resource(dev, SYS_RES_IRQ, &rid, 0, ~0, 1,
+ RF_SHAREABLE | RF_ACTIVE);
+ if (sc->irq == NULL) {
+ device_printf(dev, "could not map interrupt\n");
+ error = ENXIO;
+ goto fail;
+ }
+
+ error = bus_setup_intr(dev, sc->irq, INTR_TYPE_NET,
+ fxp_intr, sc, &sc->ih);
+ if (error) {
+ device_printf(dev, "could not setup irq\n");
+ goto fail;
+ }
+#endif
+
+ /*
+ * get mapping and base address of registers
+ */
+ fxp_pci_conf_read16(sc, PCI_COMMAND,&val16);
+ DBGLVL_PRINTK(4,"fxp_attach: PCI_COMMAND_read = 0x%x\n",val16);
+ if((val16 & PCI_COMMAND_IO) != 0) {
+ sc->pci_regs_are_io = true;
+ fxp_pci_conf_read32(sc, PCI_BASE_ADDRESS_1, &val32);
+ sc->pci_regs_base = val32 & PCI_BASE_ADDRESS_IO_MASK;
+ }
+ else {
+ sc->pci_regs_are_io = false;
+ fxp_pci_conf_read32(sc, PCI_BASE_ADDRESS_0, &val32);
+ sc->pci_regs_base = val32 & PCI_BASE_ADDRESS_MEM_MASK;
+ }
+ DBGLVL_PRINTK(3,"fxp_attach: CSR registers are mapped in %s space"
+ " at address 0x%x\n",
+ sc->pci_regs_are_io ? "I/O" : "MEM",
+ sc->pci_regs_base);
+
+ /*
+ * get interrupt level to be used
+ */
+ fxp_pci_conf_read8(sc, PCI_INTERRUPT_LINE, &interrupt);
+ DBGLVL_PRINTK(3,"fxp_attach: interrupt = 0x%x\n",interrupt);
+ sc->irq_num = interrupt;
+ /*
+ * Reset to a stable state.
+ CSR_WRITE_4(sc, FXP_CSR_PORT, FXP_PORT_SELECTIVE_RESET);
+ */
+ CSR_WRITE_4(sc, FXP_CSR_PORT, FXP_PORT_SOFTWARE_RESET);
+ DELAY(10);
+
+ sc->cbl_base = malloc(sizeof(struct fxp_cb_tx) * FXP_NTXCB,
+ M_DEVBUF, M_NOWAIT);
+ DBGLVL_PRINTK(3,"fxp_attach: sc->cbl_base = 0x%x\n",sc->cbl_base);
+ if (sc->cbl_base == NULL)
+ goto failmem;
+ else
+ memset(sc->cbl_base, 0, sizeof(struct fxp_cb_tx) * FXP_NTXCB);
+
+ sc->fxp_stats = malloc(sizeof(struct fxp_stats), M_DEVBUF,
+ M_NOWAIT);
+ DBGLVL_PRINTK(3,"fxp_attach: sc->fxp_stats = 0x%x\n",sc->fxp_stats);
+ if (sc->fxp_stats == NULL)
+ goto failmem;
+ else
+ memset(sc->fxp_stats, 0, sizeof(struct fxp_stats));
+
+ sc->mcsp = malloc(sizeof(struct fxp_cb_mcs), M_DEVBUF, M_NOWAIT);
+ DBGLVL_PRINTK(3,"fxp_attach: sc->mcsp = 0x%x\n",sc->mcsp);
+ if (sc->mcsp == NULL)
+ goto failmem;
+
+ /*
+ * Pre-allocate our receive buffers.
+ */
+ for (i = 0; i < FXP_NRFABUFS; i++) {
+ if (fxp_add_rfabuf(sc, NULL) != 0) {
+ goto failmem;
+ }
+ }
+
+ /*
+ * Find out how large of an SEEPROM we have.
+ */
+ DBGLVL_PRINTK(3,"fxp_attach: calling fxp_autosize_eeprom\n");
+ fxp_autosize_eeprom(sc);
+
+ /*
+ * Determine whether we must use the 503 serial interface.
+ */
+ fxp_read_eeprom(sc, &data, 6, 1);
+ if ((data & FXP_PHY_DEVICE_MASK) != 0 &&
+ (data & FXP_PHY_SERIAL_ONLY))
+ sc->flags |= FXP_FLAG_SERIAL_MEDIA;
+
+ /*
+ * Find out the basic controller type; we currently only
+ * differentiate between a 82557 and greater.
+ */
+ fxp_read_eeprom(sc, &data, 5, 1);
+ if ((data >> 8) == 1)
+ sc->chip = FXP_CHIP_82557;
+ DBGLVL_PRINTK(3,"fxp_attach: sc->chip = %d\n",sc->chip);
+
+ /*
+ * Enable workarounds for certain chip revision deficiencies.
+ *
+ * Systems based on the ICH2/ICH2-M chip from Intel have a defect
+ * where the chip can cause a PCI protocol violation if it receives
+ * a CU_RESUME command when it is entering the IDLE state. The
+ * workaround is to disable Dynamic Standby Mode, so the chip never
+ * deasserts CLKRUN#, and always remains in an active state.
+ *
+ * See Intel 82801BA/82801BAM Specification Update, Errata #30.
+ */
+#ifdef NOTUSED
+ i = fxp_pci_get_device(dev);
+#else
+ fxp_pci_conf_read16(sc, PCI_DEVICE_ID, &dev_id);
+ DBGLVL_PRINTK(3,"fxp_attach: device id = 0x%x\n",dev_id);
+#endif
+ if (dev_id == 0x2449 || (dev_id > 0x1030 && dev_id < 0x1039)) {
+ device_printf(dev, "*** See Intel 82801BA/82801BAM Specification Update, Errata #30. ***\n");
+ fxp_read_eeprom(sc, &data, 10, 1);
+ if (data & 0x02) { /* STB enable */
+ u_int16_t cksum;
+ int i;
+
+ device_printf(dev,
+ "*** DISABLING DYNAMIC STANDBY MODE IN EEPROM ***\n");
+ data &= ~0x02;
+ fxp_write_eeprom(sc, &data, 10, 1);
+ device_printf(dev, "New EEPROM ID: 0x%x\n", data);
+ cksum = 0;
+ for (i = 0; i < (1 << sc->eeprom_size) - 1; i++) {
+ fxp_read_eeprom(sc, &data, i, 1);
+ cksum += data;
+ }
+ i = (1 << sc->eeprom_size) - 1;
+ cksum = 0xBABA - cksum;
+ fxp_read_eeprom(sc, &data, i, 1);
+ fxp_write_eeprom(sc, &cksum, i, 1);
+ device_printf(dev,
+ "EEPROM checksum @ 0x%x: 0x%x -> 0x%x\n",
+ i, data, cksum);
+ /*
+ * We need to do a full PCI reset here. A software
+ * reset to the port doesn't cut it, but let's try
+ * anyway.
+ */
+ CSR_WRITE_4(sc, FXP_CSR_PORT, FXP_PORT_SOFTWARE_RESET);
+ DELAY(50);
+ device_printf(dev,
+ "*** PLEASE REBOOT THE SYSTEM NOW FOR CORRECT OPERATION ***\n");
+#if 1
+ /*
+ * If the user elects to continue, try the software
+ * workaround, as it is better than nothing.
+ */
+ sc->flags |= FXP_FLAG_CU_RESUME_BUG;
+#endif
+ }
+ }
+
+ /*
+ * If we are not a 82557 chip, we can enable extended features.
+ */
+ if (sc->chip != FXP_CHIP_82557) {
+ u_int8_t tmp_val;
+ /*
+ * If MWI is enabled in the PCI configuration, and there
+ * is a valid cacheline size (8 or 16 dwords), then tell
+ * the board to turn on MWI.
+ */
+ fxp_pci_conf_read8(sc, PCI_CACHE_LINE_SIZE, &tmp_val);
+ DBGLVL_PRINTK(3,"fxp_attach: CACHE_LINE_SIZE = %d\n",tmp_val);
+ if (val16 & PCI_COMMAND_MEMORY &&
+ tmp_val != 0)
+ sc->flags |= FXP_FLAG_MWI_ENABLE;
+
+ /* turn on the extended TxCB feature */
+ sc->flags |= FXP_FLAG_EXT_TXCB;
+
+ /* enable reception of long frames for VLAN */
+ sc->flags |= FXP_FLAG_LONG_PKT_EN;
+ DBGLVL_PRINTK(3,"fxp_attach: sc->flags = 0x%x\n",
+ sc->flags);
+ }
+
+ /*
+ * Read MAC address.
+ */
+ fxp_read_eeprom(sc, (u_int16_t*)sc->arpcom.ac_enaddr, 0, 3);
+ if (fxp_is_verbose) {
+ device_printf(dev, "Ethernet address %x:%x:%x:%x:%x:%x %s \n",
+ ((u_int8_t*)sc->arpcom.ac_enaddr)[0],
+ ((u_int8_t*)sc->arpcom.ac_enaddr)[1],
+ ((u_int8_t*)sc->arpcom.ac_enaddr)[2],
+ ((u_int8_t*)sc->arpcom.ac_enaddr)[3],
+ ((u_int8_t*)sc->arpcom.ac_enaddr)[4],
+ ((u_int8_t*)sc->arpcom.ac_enaddr)[5],
+ sc->flags & FXP_FLAG_SERIAL_MEDIA ? ", 10Mbps" : "");
+ device_printf(dev, "PCI IDs: 0x%x 0x%x 0x%x 0x%x 0x%x\n",
+ fxp_pci_get_vendor(sc), fxp_pci_get_device(sc),
+ fxp_pci_get_subvendor(sc), fxp_pci_get_subdevice(sc),
+ fxp_pci_get_revid(sc));
+ device_printf(dev, "Chip Type: %d\n", sc->chip);
+ }
+
+#ifdef NOTUSED /* do not set up interface at all... */
+ /*
+ * If this is only a 10Mbps device, then there is no MII, and
+ * the PHY will use a serial interface instead.
+ *
+ * The Seeq 80c24 AutoDUPLEX(tm) Ethernet Interface Adapter
+ * doesn't have a programming interface of any sort. The
+ * media is sensed automatically based on how the link partner
+ * is configured. This is, in essence, manual configuration.
+ */
+ if (sc->flags & FXP_FLAG_SERIAL_MEDIA) {
+ ifmedia_init(&sc->sc_media, 0, fxp_serial_ifmedia_upd,
+ fxp_serial_ifmedia_sts);
+ ifmedia_add(&sc->sc_media, IFM_ETHER|IFM_MANUAL, 0, NULL);
+ ifmedia_set(&sc->sc_media, IFM_ETHER|IFM_MANUAL);
+ } else {
+ if (mii_phy_probe(dev, &sc->miibus, fxp_ifmedia_upd,
+ fxp_ifmedia_sts)) {
+ device_printf(dev, "MII without any PHY!\n");
+ error = ENXIO;
+ goto fail;
+ }
+ }
+#endif
+ if (config->mtu)
+ mtu = config->mtu;
+ else
+ mtu = ETHERMTU;
+
+ ifp->if_softc = sc;
+ ifp->if_unit = unitNumber;
+ ifp->if_name = unitName;
+ ifp->if_mtu = mtu;
+ ifp->if_baudrate = 100000000;
+ ifp->if_init = fxp_init;
+ ifp->if_ioctl = fxp_ioctl;
+ ifp->if_start = fxp_start;
+ ifp->if_output = ether_output;
+ ifp->if_watchdog = fxp_watchdog;
+ ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX /*| IFF_MULTICAST*/;
+ if (ifp->if_snd.ifq_maxlen == 0)
+ ifp->if_snd.ifq_maxlen = ifqmaxlen;
+
+ /*
+ * Attach the interface.
+ */
+ DBGLVL_PRINTK(3,"fxp_attach: calling if_attach\n");
+ if_attach (ifp);
+ DBGLVL_PRINTK(3,"fxp_attach: calling ether_if_attach\n");
+ ether_ifattach(ifp);
+ DBGLVL_PRINTK(3,"fxp_attach: return from ether_if_attach\n");
+
+#ifdef NOTUSED
+ /*
+ * Tell the upper layer(s) we support long frames.
+ */
+ ifp->if_data.ifi_hdrlen = sizeof(struct ether_vlan_header);
+#endif
+ /*
+ * Let the system queue as many packets as we have available
+ * TX descriptors.
+ */
+ ifp->if_snd.ifq_maxlen = FXP_NTXCB - 1;
+
+ splx(s);
+ return (0);
+
+failmem:
+ device_printf(dev, "Failed to malloc memory\n");
+ error = ENOMEM;
+#ifdef NOTUSED
+fail:
+#endif
+ splx(s);
+ fxp_release(sc);
+ return (error);
+}
+
+/*
+ * release all resources
+ */
+static void
+fxp_release(struct fxp_softc *sc)
+{
+
+#ifdef NOTUSED
+ bus_generic_detach(sc->dev);
+ if (sc->miibus)
+ device_delete_child(sc->dev, sc->miibus);
+#endif
+ if (sc->cbl_base)
+ free(sc->cbl_base, M_DEVBUF);
+ if (sc->fxp_stats)
+ free(sc->fxp_stats, M_DEVBUF);
+ if (sc->mcsp)
+ free(sc->mcsp, M_DEVBUF);
+ if (sc->rfa_headm)
+ m_freem(sc->rfa_headm);
+
+#ifdef NOTUSED
+ if (sc->ih)
+ bus_teardown_intr(sc->dev, sc->irq, sc->ih);
+ if (sc->irq)
+ bus_release_resource(sc->dev, SYS_RES_IRQ, 0, sc->irq);
+ if (sc->mem)
+ bus_release_resource(sc->dev, sc->rtp, sc->rgd, sc->mem);
+ mtx_destroy(&sc->sc_mtx);
+#endif
+}
+
+#if NOTUSED
+/*
+ * Detach interface.
+ */
+static int
+fxp_detach(device_t dev)
+{
+ struct fxp_softc *sc = device_get_softc(dev);
+ int s;
+
+ /* disable interrupts */
+ CSR_WRITE_1(sc, FXP_CSR_SCB_INTRCNTL, FXP_SCB_INTR_DISABLE);
+
+ s = splimp();
+
+ /*
+ * Stop DMA and drop transmit queue.
+ */
+ fxp_stop(sc);
+
+ /*
+ * Close down routes etc.
+ */
+ ether_ifdetach(&sc->arpcom.ac_if, ETHER_BPF_SUPPORTED);
+
+ /*
+ * Free all media structures.
+ */
+ ifmedia_removeall(&sc->sc_media);
+
+ splx(s);
+
+ /* Release our allocated resources. */
+ fxp_release(sc);
+
+ return (0);
+}
+
+/*
+ * Device shutdown routine. Called at system shutdown after sync. The
+ * main purpose of this routine is to shut off receiver DMA so that
+ * kernel memory doesn't get clobbered during warmboot.
+ */
+static int
+fxp_shutdown(device_t dev)
+{
+ /*
+ * Make sure that DMA is disabled prior to reboot. Not doing
+ * do could allow DMA to corrupt kernel memory during the
+ * reboot before the driver initializes.
+ */
+ fxp_stop((struct fxp_softc *) device_get_softc(dev));
+ return (0);
+}
+#endif
+
+/*
+ * Show interface statistics
+ */
+static void
+fxp_stats(struct fxp_softc *sc)
+{
+ struct ifnet *ifp = &sc->sc_if;
+
+ printf (" Output packets:%-8" PRIu64, ifp->if_opackets);
+ printf (" Collisions:%-8" PRIu64, ifp->if_collisions);
+ printf (" Output errors:%-8" PRIu64 "\n", ifp->if_oerrors);
+ printf (" Input packets:%-8" PRIu64, ifp->if_ipackets);
+ printf (" Input errors:%-8" PRIu64 "\n", ifp->if_ierrors);
+}
+
+static void
+fxp_eeprom_shiftin(struct fxp_softc *sc, int data, int length)
+{
+ u_int16_t reg;
+ int x;
+
+ /*
+ * Shift in data.
+ */
+ for (x = 1 << (length - 1); x; x >>= 1) {
+ if (data & x)
+ reg = FXP_EEPROM_EECS | FXP_EEPROM_EEDI;
+ else
+ reg = FXP_EEPROM_EECS;
+ CSR_WRITE_2(sc, FXP_CSR_EEPROMCONTROL, reg);
+ DELAY(1);
+ CSR_WRITE_2(sc, FXP_CSR_EEPROMCONTROL, reg | FXP_EEPROM_EESK);
+ DELAY(1);
+ CSR_WRITE_2(sc, FXP_CSR_EEPROMCONTROL, reg);
+ DELAY(1);
+ }
+}
+
+/*
+ * Read from the serial EEPROM. Basically, you manually shift in
+ * the read opcode (one bit at a time) and then shift in the address,
+ * and then you shift out the data (all of this one bit at a time).
+ * The word size is 16 bits, so you have to provide the address for
+ * every 16 bits of data.
+ */
+static u_int16_t
+fxp_eeprom_getword(struct fxp_softc *sc, int offset, int autosize)
+{
+ u_int16_t reg, data;
+ int x;
+
+ CSR_WRITE_2(sc, FXP_CSR_EEPROMCONTROL, FXP_EEPROM_EECS);
+ /*
+ * Shift in read opcode.
+ */
+ fxp_eeprom_shiftin(sc, FXP_EEPROM_OPC_READ, 3);
+ /*
+ * Shift in address.
+ */
+ data = 0;
+ for (x = 1 << (sc->eeprom_size - 1); x; x >>= 1) {
+ if (offset & x)
+ reg = FXP_EEPROM_EECS | FXP_EEPROM_EEDI;
+ else
+ reg = FXP_EEPROM_EECS;
+ CSR_WRITE_2(sc, FXP_CSR_EEPROMCONTROL, reg);
+ DELAY(1);
+ CSR_WRITE_2(sc, FXP_CSR_EEPROMCONTROL, reg | FXP_EEPROM_EESK);
+ DELAY(1);
+ CSR_WRITE_2(sc, FXP_CSR_EEPROMCONTROL, reg);
+ DELAY(1);
+ reg = CSR_READ_2(sc, FXP_CSR_EEPROMCONTROL) & FXP_EEPROM_EEDO;
+ data++;
+ if (autosize && reg == 0) {
+ sc->eeprom_size = data;
+ break;
+ }
+ }
+ /*
+ * Shift out data.
+ */
+ data = 0;
+ reg = FXP_EEPROM_EECS;
+ for (x = 1 << 15; x; x >>= 1) {
+ CSR_WRITE_2(sc, FXP_CSR_EEPROMCONTROL, reg | FXP_EEPROM_EESK);
+ DELAY(1);
+ if (CSR_READ_2(sc, FXP_CSR_EEPROMCONTROL) & FXP_EEPROM_EEDO)
+ data |= x;
+ CSR_WRITE_2(sc, FXP_CSR_EEPROMCONTROL, reg);
+ DELAY(1);
+ }
+ CSR_WRITE_2(sc, FXP_CSR_EEPROMCONTROL, 0);
+ DELAY(1);
+
+ return (data);
+}
+
+static void
+fxp_eeprom_putword(struct fxp_softc *sc, int offset, u_int16_t data)
+{
+ int i;
+
+ /*
+ * Erase/write enable.
+ */
+ CSR_WRITE_2(sc, FXP_CSR_EEPROMCONTROL, FXP_EEPROM_EECS);
+ fxp_eeprom_shiftin(sc, 0x4, 3);
+ fxp_eeprom_shiftin(sc, 0x03 << (sc->eeprom_size - 2), sc->eeprom_size);
+ CSR_WRITE_2(sc, FXP_CSR_EEPROMCONTROL, 0);
+ DELAY(1);
+ /*
+ * Shift in write opcode, address, data.
+ */
+ CSR_WRITE_2(sc, FXP_CSR_EEPROMCONTROL, FXP_EEPROM_EECS);
+ fxp_eeprom_shiftin(sc, FXP_EEPROM_OPC_WRITE, 3);
+ fxp_eeprom_shiftin(sc, offset, sc->eeprom_size);
+ fxp_eeprom_shiftin(sc, data, 16);
+ CSR_WRITE_2(sc, FXP_CSR_EEPROMCONTROL, 0);
+ DELAY(1);
+ /*
+ * Wait for EEPROM to finish up.
+ */
+ CSR_WRITE_2(sc, FXP_CSR_EEPROMCONTROL, FXP_EEPROM_EECS);
+ DELAY(1);
+ for (i = 0; i < 1000; i++) {
+ if (CSR_READ_2(sc, FXP_CSR_EEPROMCONTROL) & FXP_EEPROM_EEDO)
+ break;
+ DELAY(50);
+ }
+ CSR_WRITE_2(sc, FXP_CSR_EEPROMCONTROL, 0);
+ DELAY(1);
+ /*
+ * Erase/write disable.
+ */
+ CSR_WRITE_2(sc, FXP_CSR_EEPROMCONTROL, FXP_EEPROM_EECS);
+ fxp_eeprom_shiftin(sc, 0x4, 3);
+ fxp_eeprom_shiftin(sc, 0, sc->eeprom_size);
+ CSR_WRITE_2(sc, FXP_CSR_EEPROMCONTROL, 0);
+ DELAY(1);
+}
+
+/*
+ * From NetBSD:
+ *
+ * Figure out EEPROM size.
+ *
+ * 559's can have either 64-word or 256-word EEPROMs, the 558
+ * datasheet only talks about 64-word EEPROMs, and the 557 datasheet
+ * talks about the existance of 16 to 256 word EEPROMs.
+ *
+ * The only known sizes are 64 and 256, where the 256 version is used
+ * by CardBus cards to store CIS information.
+ *
+ * The address is shifted in msb-to-lsb, and after the last
+ * address-bit the EEPROM is supposed to output a `dummy zero' bit,
+ * after which follows the actual data. We try to detect this zero, by
+ * probing the data-out bit in the EEPROM control register just after
+ * having shifted in a bit. If the bit is zero, we assume we've
+ * shifted enough address bits. The data-out should be tri-state,
+ * before this, which should translate to a logical one.
+ */
+static void
+fxp_autosize_eeprom(struct fxp_softc *sc)
+{
+
+ /* guess maximum size of 256 words */
+ sc->eeprom_size = 8;
+
+ /* autosize */
+ (void) fxp_eeprom_getword(sc, 0, 1);
+}
+
+static void
+fxp_read_eeprom(struct fxp_softc *sc, u_short *data, int offset, int words)
+{
+ int i;
+
+ for (i = 0; i < words; i++) {
+ data[i] = fxp_eeprom_getword(sc, offset + i, 0);
+ DBGLVL_PRINTK(4,"fxp_eeprom_read(off=0x%x)=0x%x\n",
+ offset+i,data[i]);
+ }
+}
+
+static void
+fxp_write_eeprom(struct fxp_softc *sc, u_short *data, int offset, int words)
+{
+ int i;
+
+ for (i = 0; i < words; i++)
+ fxp_eeprom_putword(sc, offset + i, data[i]);
+ DBGLVL_PRINTK(4,"fxp_eeprom_write(off=0x%x,0x%x)\n",
+ offset+i,data[i]);
+}
+
+/*
+ * Start packet transmission on the interface.
+ */
+static void
+fxp_start(struct ifnet *ifp)
+{
+ struct fxp_softc *sc = ifp->if_softc;
+ struct fxp_cb_tx *txp;
+
+ DBGLVL_PRINTK(3,"fxp_start called\n");
+
+ /*
+ * See if we need to suspend xmit until the multicast filter
+ * has been reprogrammed (which can only be done at the head
+ * of the command chain).
+ */
+ if (sc->need_mcsetup) {
+ DBGLVL_PRINTK(3,"fxp_start need_mcsetup\n");
+ return;
+ }
+
+ txp = NULL;
+
+ /*
+ * We're finished if there is nothing more to add to the list or if
+ * we're all filled up with buffers to transmit.
+ * NOTE: One TxCB is reserved to guarantee that fxp_mc_setup() can add
+ * a NOP command when needed.
+ */
+ while (ifp->if_snd.ifq_head != NULL && sc->tx_queued < FXP_NTXCB - 1) {
+ struct mbuf *m, *mb_head;
+ int segment;
+
+ /*
+ * Grab a packet to transmit.
+ */
+ IF_DEQUEUE(&ifp->if_snd, mb_head);
+
+ /*
+ * Get pointer to next available tx desc.
+ */
+ txp = sc->cbl_last->next;
+
+ /*
+ * Go through each of the mbufs in the chain and initialize
+ * the transmit buffer descriptors with the physical address
+ * and size of the mbuf.
+ */
+tbdinit:
+ for (m = mb_head, segment = 0; m != NULL; m = m->m_next) {
+ if (m->m_len != 0) {
+ if (segment == FXP_NTXSEG)
+ break;
+ txp->tbd[segment].tb_addr =
+ vtophys(mtod(m, vm_offset_t));
+ txp->tbd[segment].tb_size = m->m_len;
+ segment++;
+ }
+ }
+ if (m != NULL) {
+ struct mbuf *mn;
+
+ /*
+ * We ran out of segments. We have to recopy this
+ * mbuf chain first. Bail out if we can't get the
+ * new buffers.
+ */
+ MGETHDR(mn, M_DONTWAIT, MT_DATA);
+ if (mn == NULL) {
+ m_freem(mb_head);
+ break;
+ }
+ if (mb_head->m_pkthdr.len > MHLEN) {
+ MCLGET(mn, M_DONTWAIT);
+ if ((mn->m_flags & M_EXT) == 0) {
+ m_freem(mn);
+ m_freem(mb_head);
+ break;
+ }
+ }
+ m_copydata(mb_head, 0, mb_head->m_pkthdr.len,
+ mtod(mn, caddr_t));
+ mn->m_pkthdr.len = mn->m_len = mb_head->m_pkthdr.len;
+ m_freem(mb_head);
+ mb_head = mn;
+ goto tbdinit;
+ }
+
+ txp->tbd_number = segment;
+ txp->mb_head = mb_head;
+ txp->cb_status = 0;
+ if (sc->tx_queued != FXP_CXINT_THRESH - 1) {
+ txp->cb_command =
+ FXP_CB_COMMAND_XMIT | FXP_CB_COMMAND_SF |
+ FXP_CB_COMMAND_S;
+ } else {
+ txp->cb_command =
+ FXP_CB_COMMAND_XMIT | FXP_CB_COMMAND_SF |
+ FXP_CB_COMMAND_S | FXP_CB_COMMAND_I;
+ /*
+ * Set a 5 second timer just in case we don't hear
+ * from the card again.
+ */
+ ifp->if_timer = 5;
+ }
+ txp->tx_threshold = tx_threshold;
+
+ /*
+ * Advance the end of list forward.
+ */
+
+#ifdef __alpha__
+ /*
+ * On platforms which can't access memory in 16-bit
+ * granularities, we must prevent the card from DMA'ing
+ * up the status while we update the command field.
+ * This could cause us to overwrite the completion status.
+ */
+ atomic_clear_short(&sc->cbl_last->cb_command,
+ FXP_CB_COMMAND_S);
+#else
+ sc->cbl_last->cb_command &= ~FXP_CB_COMMAND_S;
+#endif /*__alpha__*/
+ sc->cbl_last = txp;
+
+ /*
+ * Advance the beginning of the list forward if there are
+ * no other packets queued (when nothing is queued, cbl_first
+ * sits on the last TxCB that was sent out).
+ */
+ if (sc->tx_queued == 0)
+ sc->cbl_first = txp;
+
+ sc->tx_queued++;
+
+#ifdef NOTUSED
+ /*
+ * Pass packet to bpf if there is a listener.
+ */
+ if (ifp->if_bpf)
+ bpf_mtap(ifp, mb_head);
+#endif
+ }
+
+ /*
+ * We're finished. If we added to the list, issue a RESUME to get DMA
+ * going again if suspended.
+ */
+ if (txp != NULL) {
+ fxp_scb_wait(sc);
+ fxp_scb_cmd(sc, FXP_SCB_COMMAND_CU_RESUME);
+ }
+
+ /*
+ * reenable interrupts
+ */
+ RTEMS_COMPILER_MEMORY_BARRIER();
+ CSR_WRITE_1(sc, FXP_CSR_SCB_INTRCNTL,0);
+ bsp_interrupt_vector_enable(sc->irq_num);
+ RTEMS_COMPILER_MEMORY_BARRIER();
+}
+
+/*
+ * Process interface interrupts.
+ */
+static void fxp_intr(void *arg)
+{
+ /*
+ * Obtain device state
+ */
+ struct fxp_softc *sc = (struct fxp_softc *)arg;
+
+ /*
+ * disable interrupts
+ */
+ CSR_WRITE_1(sc, FXP_CSR_SCB_INTRCNTL, FXP_SCB_INTR_DISABLE);
+ /*
+ * send event to deamon
+ */
+ rtems_bsdnet_event_send (sc->daemonTid, INTERRUPT_EVENT);
+}
+
+static void fxp_daemon(void *xsc)
+{
+ struct fxp_softc *sc = xsc;
+ struct ifnet *ifp = &sc->sc_if;
+ u_int8_t statack;
+ rtems_event_set events;
+
+#ifdef NOTUSED
+ if (sc->suspended) {
+ return;
+ }
+#endif
+ for (;;) {
+
+ DBGLVL_PRINTK(4,"fxp_daemon waiting for event, INTRCNTL 0x%02x\n",
+ CSR_READ_1(sc, FXP_CSR_SCB_INTRCNTL));
+ /*
+ * wait for event to receive from interrupt function
+ */
+ rtems_bsdnet_event_receive (INTERRUPT_EVENT,
+ RTEMS_WAIT|RTEMS_EVENT_ANY,
+ RTEMS_NO_TIMEOUT,
+ &events);
+ while ((statack = CSR_READ_1(sc, FXP_CSR_SCB_STATACK)) != 0) {
+ DBGLVL_PRINTK(4,"fxp_daemon: processing event, statack = 0x%x\n",
+ statack);
+#ifdef NOTUSED
+ /*
+ * It should not be possible to have all bits set; the
+ * FXP_SCB_INTR_SWI bit always returns 0 on a read. If
+ * all bits are set, this may indicate that the card has
+ * been physically ejected, so ignore it.
+ */
+ if (statack == 0xff)
+ return;
+#endif
+
+ /*
+ * First ACK all the interrupts in this pass.
+ */
+ CSR_WRITE_1(sc, FXP_CSR_SCB_STATACK, statack);
+
+ /*
+ * Free any finished transmit mbuf chains.
+ *
+ * Handle the CNA event likt a CXTNO event. It used to
+ * be that this event (control unit not ready) was not
+ * encountered, but it is now with the SMPng modifications.
+ * The exact sequence of events that occur when the interface
+ * is brought up are different now, and if this event
+ * goes unhandled, the configuration/rxfilter setup sequence
+ * can stall for several seconds. The result is that no
+ * packets go out onto the wire for about 5 to 10 seconds
+ * after the interface is ifconfig'ed for the first time.
+ */
+ if (statack & (FXP_SCB_STATACK_CXTNO | FXP_SCB_STATACK_CNA)) {
+ struct fxp_cb_tx *txp;
+
+ for (txp = sc->cbl_first; sc->tx_queued &&
+ (txp->cb_status & FXP_CB_STATUS_C) != 0;
+ txp = txp->next) {
+ if (txp->mb_head != NULL) {
+ m_freem(txp->mb_head);
+ txp->mb_head = NULL;
+ }
+ sc->tx_queued--;
+ }
+ sc->cbl_first = txp;
+ ifp->if_timer = 0;
+ if (sc->tx_queued == 0) {
+ if (sc->need_mcsetup)
+ fxp_mc_setup(sc);
+ }
+ /*
+ * Try to start more packets transmitting.
+ */
+ if (ifp->if_snd.ifq_head != NULL)
+ fxp_start(ifp);
+ }
+ /*
+ * Process receiver interrupts. If a no-resource (RNR)
+ * condition exists, get whatever packets we can and
+ * re-start the receiver.
+ */
+ if (statack & (FXP_SCB_STATACK_FR | FXP_SCB_STATACK_RNR)) {
+ struct mbuf *m;
+ struct fxp_rfa *rfa;
+rcvloop:
+ m = sc->rfa_headm;
+ rfa = (struct fxp_rfa *)(m->m_ext.ext_buf +
+ RFA_ALIGNMENT_FUDGE);
+
+ if (rfa->rfa_status & FXP_RFA_STATUS_C) {
+ /*
+ * Remove first packet from the chain.
+ */
+ sc->rfa_headm = m->m_next;
+ m->m_next = NULL;
+
+ /*
+ * Add a new buffer to the receive chain.
+ * If this fails, the old buffer is recycled
+ * instead.
+ */
+ if (fxp_add_rfabuf(sc, m) == 0) {
+ struct ether_header *eh;
+ int total_len;
+
+ total_len = rfa->actual_size &
+ (MCLBYTES - 1);
+ if (total_len <
+ sizeof(struct ether_header)) {
+ m_freem(m);
+ goto rcvloop;
+ }
+
+ /*
+ * Drop the packet if it has CRC
+ * errors. This test is only needed
+ * when doing 802.1q VLAN on the 82557
+ * chip.
+ */
+ if (rfa->rfa_status &
+ FXP_RFA_STATUS_CRC) {
+ m_freem(m);
+ goto rcvloop;
+ }
+
+ m->m_pkthdr.rcvif = ifp;
+ m->m_pkthdr.len = m->m_len = total_len;
+ eh = mtod(m, struct ether_header *);
+ m->m_data +=
+ sizeof(struct ether_header);
+ m->m_len -=
+ sizeof(struct ether_header);
+ m->m_pkthdr.len = m->m_len;
+ ether_input(ifp, eh, m);
+ }
+ goto rcvloop;
+ }
+ if (statack & FXP_SCB_STATACK_RNR) {
+ fxp_scb_wait(sc);
+ CSR_WRITE_4(sc, FXP_CSR_SCB_GENERAL,
+ vtophys(sc->rfa_headm->m_ext.ext_buf) +
+ RFA_ALIGNMENT_FUDGE);
+ fxp_scb_cmd(sc, FXP_SCB_COMMAND_RU_START);
+ }
+ }
+ }
+ /*
+ * reenable interrupts
+ */
+ RTEMS_COMPILER_MEMORY_BARRIER();
+ CSR_WRITE_1(sc, FXP_CSR_SCB_INTRCNTL,0);
+ RTEMS_COMPILER_MEMORY_BARRIER();
+ }
+}
+
+/*
+ * Update packet in/out/collision statistics. The i82557 doesn't
+ * allow you to access these counters without doing a fairly
+ * expensive DMA to get _all_ of the statistics it maintains, so
+ * we do this operation here only once per second. The statistics
+ * counters in the kernel are updated from the previous dump-stats
+ * DMA and then a new dump-stats DMA is started. The on-chip
+ * counters are zeroed when the DMA completes. If we can't start
+ * the DMA immediately, we don't wait - we just prepare to read
+ * them again next time.
+ */
+static void
+fxp_tick(void *xsc)
+{
+ struct fxp_softc *sc = xsc;
+ struct ifnet *ifp = &sc->sc_if;
+ struct fxp_stats *sp = sc->fxp_stats;
+ struct fxp_cb_tx *txp;
+ int s;
+
+ DBGLVL_PRINTK(4,"fxp_tick called\n");
+
+ ifp->if_opackets += sp->tx_good;
+ ifp->if_collisions += sp->tx_total_collisions;
+ if (sp->rx_good) {
+ ifp->if_ipackets += sp->rx_good;
+ sc->rx_idle_secs = 0;
+ } else {
+ /*
+ * Receiver's been idle for another second.
+ */
+ sc->rx_idle_secs++;
+ }
+ ifp->if_ierrors +=
+ sp->rx_crc_errors +
+ sp->rx_alignment_errors +
+ sp->rx_rnr_errors +
+ sp->rx_overrun_errors;
+ /*
+ * If any transmit underruns occured, bump up the transmit
+ * threshold by another 512 bytes (64 * 8).
+ */
+ if (sp->tx_underruns) {
+ ifp->if_oerrors += sp->tx_underruns;
+ if (tx_threshold < 192)
+ tx_threshold += 64;
+ }
+ s = splimp();
+ /*
+ * Release any xmit buffers that have completed DMA. This isn't
+ * strictly necessary to do here, but it's advantagous for mbufs
+ * with external storage to be released in a timely manner rather
+ * than being defered for a potentially long time. This limits
+ * the delay to a maximum of one second.
+ */
+ for (txp = sc->cbl_first; sc->tx_queued &&
+ (txp->cb_status & FXP_CB_STATUS_C) != 0;
+ txp = txp->next) {
+ if (txp->mb_head != NULL) {
+ m_freem(txp->mb_head);
+ txp->mb_head = NULL;
+ }
+ sc->tx_queued--;
+ }
+ sc->cbl_first = txp;
+ /*
+ * If we haven't received any packets in FXP_MAC_RX_IDLE seconds,
+ * then assume the receiver has locked up and attempt to clear
+ * the condition by reprogramming the multicast filter. This is
+ * a work-around for a bug in the 82557 where the receiver locks
+ * up if it gets certain types of garbage in the syncronization
+ * bits prior to the packet header. This bug is supposed to only
+ * occur in 10Mbps mode, but has been seen to occur in 100Mbps
+ * mode as well (perhaps due to a 10/100 speed transition).
+ */
+ if (sc->rx_idle_secs > FXP_MAX_RX_IDLE) {
+ sc->rx_idle_secs = 0;
+ fxp_mc_setup(sc);
+ }
+ /*
+ * If there is no pending command, start another stats
+ * dump. Otherwise punt for now.
+ */
+ if (CSR_READ_1(sc, FXP_CSR_SCB_COMMAND) == 0) {
+ /*
+ * Start another stats dump.
+ */
+ fxp_scb_cmd(sc, FXP_SCB_COMMAND_CU_DUMPRESET);
+ } else {
+ /*
+ * A previous command is still waiting to be accepted.
+ * Just zero our copy of the stats and wait for the
+ * next timer event to update them.
+ */
+ sp->tx_good = 0;
+ sp->tx_underruns = 0;
+ sp->tx_total_collisions = 0;
+
+ sp->rx_good = 0;
+ sp->rx_crc_errors = 0;
+ sp->rx_alignment_errors = 0;
+ sp->rx_rnr_errors = 0;
+ sp->rx_overrun_errors = 0;
+ }
+#ifdef NOTUSED
+ if (sc->miibus != NULL)
+ mii_tick(device_get_softc(sc->miibus));
+#endif
+ splx(s);
+ /*
+ * Schedule another timeout one second from now.
+ */
+ if (sc->stat_ch == fxp_timeout_running) {
+ timeout(fxp_tick, sc, hz);
+ }
+ else if (sc->stat_ch == fxp_timeout_stop_rq) {
+ sc->stat_ch = fxp_timeout_stopped;
+ }
+}
+
+/*
+ * Stop the interface. Cancels the statistics updater and resets
+ * the interface.
+ */
+static void
+fxp_stop(struct fxp_softc *sc)
+{
+ struct ifnet *ifp = &sc->sc_if;
+ struct fxp_cb_tx *txp;
+ int i;
+
+ DBGLVL_PRINTK(2,"fxp_stop called\n");
+
+ ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE);
+ ifp->if_timer = 0;
+
+ /*
+ * stop stats updater.
+ */
+ if (sc->stat_ch == fxp_timeout_running) {
+ DBGLVL_PRINTK(3,"fxp_stop: trying to stop stat update tick\n");
+ sc->stat_ch = fxp_timeout_stop_rq;
+ while(sc->stat_ch != fxp_timeout_stopped) {
+ rtems_bsdnet_semaphore_release();
+ rtems_task_wake_after(fxp_ticksPerSecond);
+ rtems_bsdnet_semaphore_obtain();
+ }
+ DBGLVL_PRINTK(3,"fxp_stop: stat update tick stopped\n");
+ }
+ /*
+ * Issue software reset
+ */
+ DBGLVL_PRINTK(3,"fxp_stop: issue software reset\n");
+ CSR_WRITE_4(sc, FXP_CSR_PORT, FXP_PORT_SELECTIVE_RESET);
+ DELAY(10);
+
+ /*
+ * Release any xmit buffers.
+ */
+ DBGLVL_PRINTK(3,"fxp_stop: releasing xmit buffers\n");
+ txp = sc->cbl_base;
+ if (txp != NULL) {
+ for (i = 0; i < FXP_NTXCB; i++) {
+ if (txp[i].mb_head != NULL) {
+ m_freem(txp[i].mb_head);
+ txp[i].mb_head = NULL;
+ }
+ }
+ }
+ sc->tx_queued = 0;
+
+ /*
+ * Free all the receive buffers then reallocate/reinitialize
+ */
+ DBGLVL_PRINTK(3,"fxp_stop: free and reinit all receive buffers\n");
+ if (sc->rfa_headm != NULL)
+ m_freem(sc->rfa_headm);
+ sc->rfa_headm = NULL;
+ sc->rfa_tailm = NULL;
+ for (i = 0; i < FXP_NRFABUFS; i++) {
+ if (fxp_add_rfabuf(sc, NULL) != 0) {
+ /*
+ * This "can't happen" - we're at splimp()
+ * and we just freed all the buffers we need
+ * above.
+ */
+ panic("fxp_stop: no buffers!");
+ }
+ }
+ DBGLVL_PRINTK(2,"fxp_stop: finished\n");
+}
+
+/*
+ * Watchdog/transmission transmit timeout handler. Called when a
+ * transmission is started on the interface, but no interrupt is
+ * received before the timeout. This usually indicates that the
+ * card has wedged for some reason.
+ */
+static void
+fxp_watchdog(struct ifnet *ifp)
+{
+ struct fxp_softc *sc = ifp->if_softc;
+
+ device_printf(sc->dev, "device timeout\n");
+ ifp->if_oerrors++;
+
+ fxp_init(sc);
+}
+
+static void
+fxp_init(void *xsc)
+{
+ struct fxp_softc *sc = xsc;
+ struct ifnet *ifp = &sc->sc_if;
+ struct fxp_cb_config *cbp;
+ struct fxp_cb_ias *cb_ias;
+ struct fxp_cb_tx *txp;
+ int i, prm, s;
+ rtems_status_code statcode;
+
+rtems_task_wake_after(100);
+ DBGLVL_PRINTK(2,"fxp_init called\n");
+
+ s = splimp();
+ /*
+ * Cancel any pending I/O
+ */
+ /*
+ * E. Norum 2004-10-11
+ * Add line suggested by "Eugene Denisov" <dea@sendmail.ru>.
+ * Prevents lockup at initialization.
+ */
+ sc->stat_ch = fxp_timeout_stopped;
+ fxp_stop(sc);
+
+ prm = (ifp->if_flags & IFF_PROMISC) ? 1 : 0;
+
+ DBGLVL_PRINTK(5,"fxp_init: Initializing base of CBL and RFA memory\n");
+ /*
+ * Initialize base of CBL and RFA memory. Loading with zero
+ * sets it up for regular linear addressing.
+ */
+ CSR_WRITE_4(sc, FXP_CSR_SCB_GENERAL, 0);
+ fxp_scb_cmd(sc, FXP_SCB_COMMAND_CU_BASE);
+
+ fxp_scb_wait(sc);
+ fxp_scb_cmd(sc, FXP_SCB_COMMAND_RU_BASE);
+
+ /*
+ * Initialize base of dump-stats buffer.
+ */
+ DBGLVL_PRINTK(5,"fxp_init: Initializing base of dump-stats buffer\n");
+ fxp_scb_wait(sc);
+ CSR_WRITE_4(sc, FXP_CSR_SCB_GENERAL, vtophys(sc->fxp_stats));
+ fxp_scb_cmd(sc, FXP_SCB_COMMAND_CU_DUMP_ADR);
+
+ /*
+ * We temporarily use memory that contains the TxCB list to
+ * construct the config CB. The TxCB list memory is rebuilt
+ * later.
+ */
+ cbp = (struct fxp_cb_config *) sc->cbl_base;
+ DBGLVL_PRINTK(5,"fxp_init: cbp = 0x%x\n",cbp);
+
+ /*
+ * This memcpy is kind of disgusting, but there are a bunch of must be
+ * zero and must be one bits in this structure and this is the easiest
+ * way to initialize them all to proper values.
+ */
+ memcpy( (void *)(u_int32_t*)(volatile void *)&cbp->cb_status,
+ fxp_cb_config_template,
+ sizeof(fxp_cb_config_template));
+
+ cbp->cb_status = 0;
+ cbp->cb_command = FXP_CB_COMMAND_CONFIG | FXP_CB_COMMAND_EL;
+ cbp->link_addr = -1; /* (no) next command */
+ cbp->byte_count = 22; /* (22) bytes to config */
+ cbp->rx_fifo_limit = 8; /* rx fifo threshold (32 bytes) */
+ cbp->tx_fifo_limit = 0; /* tx fifo threshold (0 bytes) */
+ cbp->adaptive_ifs = 0; /* (no) adaptive interframe spacing */
+ cbp->mwi_enable = sc->flags & FXP_FLAG_MWI_ENABLE ? 1 : 0;
+ cbp->type_enable = 0; /* actually reserved */
+ cbp->read_align_en = sc->flags & FXP_FLAG_READ_ALIGN ? 1 : 0;
+ cbp->end_wr_on_cl = sc->flags & FXP_FLAG_WRITE_ALIGN ? 1 : 0;
+ cbp->rx_dma_bytecount = 0; /* (no) rx DMA max */
+ cbp->tx_dma_bytecount = 0; /* (no) tx DMA max */
+ cbp->dma_mbce = 0; /* (disable) dma max counters */
+ cbp->late_scb = 0; /* (don't) defer SCB update */
+ cbp->direct_dma_dis = 1; /* disable direct rcv dma mode */
+ cbp->tno_int_or_tco_en =0; /* (disable) tx not okay interrupt */
+ cbp->ci_int = 1; /* interrupt on CU idle */
+ cbp->ext_txcb_dis = sc->flags & FXP_FLAG_EXT_TXCB ? 0 : 1;
+ cbp->ext_stats_dis = 1; /* disable extended counters */
+ cbp->keep_overrun_rx = 0; /* don't pass overrun frames to host */
+ cbp->save_bf = sc->chip == FXP_CHIP_82557 ? 1 : prm;
+ cbp->disc_short_rx = !prm; /* discard short packets */
+ cbp->underrun_retry = 1; /* retry mode (once) on DMA underrun */
+ cbp->two_frames = 0; /* do not limit FIFO to 2 frames */
+ cbp->dyn_tbd = 0; /* (no) dynamic TBD mode */
+ cbp->mediatype = sc->flags & FXP_FLAG_SERIAL_MEDIA ? 0 : 1;
+ cbp->csma_dis = 0; /* (don't) disable link */
+ cbp->tcp_udp_cksum = 0; /* (don't) enable checksum */
+ cbp->vlan_tco = 0; /* (don't) enable vlan wakeup */
+ cbp->link_wake_en = 0; /* (don't) assert PME# on link change */
+ cbp->arp_wake_en = 0; /* (don't) assert PME# on arp */
+ cbp->mc_wake_en = 0; /* (don't) enable PME# on mcmatch */
+ cbp->nsai = 1; /* (don't) disable source addr insert */
+ cbp->preamble_length = 2; /* (7 byte) preamble */
+ cbp->loopback = 0; /* (don't) loopback */
+ cbp->linear_priority = 0; /* (normal CSMA/CD operation) */
+ cbp->linear_pri_mode = 0; /* (wait after xmit only) */
+ cbp->interfrm_spacing = 6; /* (96 bits of) interframe spacing */
+ cbp->promiscuous = prm; /* promiscuous mode */
+ cbp->bcast_disable = 0; /* (don't) disable broadcasts */
+ cbp->wait_after_win = 0; /* (don't) enable modified backoff alg*/
+ cbp->ignore_ul = 0; /* consider U/L bit in IA matching */
+ cbp->crc16_en = 0; /* (don't) enable crc-16 algorithm */
+ cbp->crscdt = sc->flags & FXP_FLAG_SERIAL_MEDIA ? 1 : 0;
+
+ cbp->stripping = !prm; /* truncate rx packet to byte count */
+ cbp->padding = 1; /* (do) pad short tx packets */
+ cbp->rcv_crc_xfer = 0; /* (don't) xfer CRC to host */
+ cbp->long_rx_en = sc->flags & FXP_FLAG_LONG_PKT_EN ? 1 : 0;
+ cbp->ia_wake_en = 0; /* (don't) wake up on address match */
+ cbp->magic_pkt_dis = 0; /* (don't) disable magic packet */
+ /* must set wake_en in PMCSR also */
+ cbp->force_fdx = 0; /* (don't) force full duplex */
+ cbp->fdx_pin_en = 1; /* (enable) FDX# pin */
+ cbp->multi_ia = 0; /* (don't) accept multiple IAs */
+ cbp->mc_all = sc->flags & FXP_FLAG_ALL_MCAST ? 1 : 0;
+
+ DBGLVL_PRINTK(5,"fxp_init: cbp initialized\n");
+ if (sc->chip == FXP_CHIP_82557) {
+ /*
+ * The 82557 has no hardware flow control, the values
+ * below are the defaults for the chip.
+ */
+ cbp->fc_delay_lsb = 0;
+ cbp->fc_delay_msb = 0x40;
+ cbp->pri_fc_thresh = 3;
+ cbp->tx_fc_dis = 0;
+ cbp->rx_fc_restop = 0;
+ cbp->rx_fc_restart = 0;
+ cbp->fc_filter = 0;
+ cbp->pri_fc_loc = 1;
+ } else {
+ cbp->fc_delay_lsb = 0x1f;
+ cbp->fc_delay_msb = 0x01;
+ cbp->pri_fc_thresh = 3;
+ cbp->tx_fc_dis = 0; /* enable transmit FC */
+ cbp->rx_fc_restop = 1; /* enable FC restop frames */
+ cbp->rx_fc_restart = 1; /* enable FC restart frames */
+ cbp->fc_filter = !prm; /* drop FC frames to host */
+ cbp->pri_fc_loc = 1; /* FC pri location (byte31) */
+ }
+
+ /*
+ * Start the config command/DMA.
+ */
+ DBGLVL_PRINTK(5,"fxp_init: starting config command/DMA\n");
+ fxp_scb_wait(sc);
+ CSR_WRITE_4(sc, FXP_CSR_SCB_GENERAL, vtophys(&cbp->cb_status));
+ fxp_scb_cmd(sc, FXP_SCB_COMMAND_CU_START);
+ /* ...and wait for it to complete. */
+ fxp_dma_wait(&cbp->cb_status, sc);
+
+ /*
+ * Now initialize the station address. Temporarily use the TxCB
+ * memory area like we did above for the config CB.
+ */
+ DBGLVL_PRINTK(5,"fxp_init: initialize station address\n");
+ cb_ias = (struct fxp_cb_ias *) sc->cbl_base;
+ cb_ias->cb_status = 0;
+ cb_ias->cb_command = FXP_CB_COMMAND_IAS | FXP_CB_COMMAND_EL;
+ cb_ias->link_addr = -1;
+ memcpy((void *)(u_int32_t*)(volatile void *)cb_ias->macaddr,
+ sc->arpcom.ac_enaddr,
+ sizeof(sc->arpcom.ac_enaddr));
+
+ /*
+ * Start the IAS (Individual Address Setup) command/DMA.
+ */
+ DBGLVL_PRINTK(5,"fxp_init: start IAS command/DMA\n");
+ fxp_scb_wait(sc);
+ fxp_scb_cmd(sc, FXP_SCB_COMMAND_CU_START);
+ /* ...and wait for it to complete. */
+ fxp_dma_wait(&cb_ias->cb_status, sc);
+
+ /*
+ * Initialize transmit control block (TxCB) list.
+ */
+
+ DBGLVL_PRINTK(5,"fxp_init: initialize TxCB list\n");
+ txp = sc->cbl_base;
+ memset(txp, 0, sizeof(struct fxp_cb_tx) * FXP_NTXCB);
+ for (i = 0; i < FXP_NTXCB; i++) {
+ txp[i].cb_status = FXP_CB_STATUS_C | FXP_CB_STATUS_OK;
+ txp[i].cb_command = FXP_CB_COMMAND_NOP;
+ txp[i].link_addr =
+ vtophys(&txp[(i + 1) & FXP_TXCB_MASK].cb_status);
+ if (sc->flags & FXP_FLAG_EXT_TXCB)
+ txp[i].tbd_array_addr = vtophys(&txp[i].tbd[2]);
+ else
+ txp[i].tbd_array_addr = vtophys(&txp[i].tbd[0]);
+ txp[i].next = &txp[(i + 1) & FXP_TXCB_MASK];
+ }
+ /*
+ * Set the suspend flag on the first TxCB and start the control
+ * unit. It will execute the NOP and then suspend.
+ */
+ DBGLVL_PRINTK(5,"fxp_init: setup suspend flag\n");
+ txp->cb_command = FXP_CB_COMMAND_NOP | FXP_CB_COMMAND_S;
+ sc->cbl_first = sc->cbl_last = txp;
+ sc->tx_queued = 1;
+
+ fxp_scb_wait(sc);
+ fxp_scb_cmd(sc, FXP_SCB_COMMAND_CU_START);
+
+ /*
+ * Initialize receiver buffer area - RFA.
+ */
+ DBGLVL_PRINTK(5,"fxp_init: initialize RFA\n");
+ fxp_scb_wait(sc);
+ CSR_WRITE_4(sc, FXP_CSR_SCB_GENERAL,
+ vtophys(sc->rfa_headm->m_ext.ext_buf) + RFA_ALIGNMENT_FUDGE);
+ fxp_scb_cmd(sc, FXP_SCB_COMMAND_RU_START);
+
+#ifdef NOTUSED
+ /*
+ * Set current media.
+ */
+ if (sc->miibus != NULL)
+ mii_mediachg(device_get_softc(sc->miibus));
+#endif
+
+ ifp->if_flags |= IFF_RUNNING;
+ ifp->if_flags &= ~IFF_OACTIVE;
+
+ if (sc->daemonTid == 0) {
+ /*
+ * Start driver task
+ */
+ sc->daemonTid = rtems_bsdnet_newproc ("FXPd", 4096, fxp_daemon, sc);
+
+ /*
+ * Set up interrupts
+ */
+ statcode = rtems_interrupt_handler_install(
+ sc->irq_num,
+ "fxp_intr",
+ RTEMS_INTERRUPT_SHARED,
+ fxp_intr,
+ sc
+ );
+
+ if ( statcode != RTEMS_SUCCESSFUL ) {
+ rtems_panic ("Can't attach fxp interrupt handler for irq %d\n",
+ sc->irq_num);
+ }
+ }
+
+ /*
+ * Enable interrupts.
+ */
+ CSR_WRITE_1(sc, FXP_CSR_SCB_INTRCNTL, 0);
+ splx(s);
+
+ /*
+ * Start stats updater.
+ */
+ sc->stat_ch = fxp_timeout_running;
+ DBGLVL_PRINTK(2,"fxp_init: stats updater timeout called with hz=%d\n", hz);
+ timeout(fxp_tick, sc, hz);
+ DBGLVL_PRINTK(2,"fxp_init finished\n");
+}
+
+#ifdef NOTUSED
+static int
+fxp_serial_ifmedia_upd(struct ifnet *ifp)
+{
+
+ return (0);
+}
+
+static void
+fxp_serial_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr)
+{
+
+ ifmr->ifm_active = IFM_ETHER|IFM_MANUAL;
+}
+
+/*
+ * Change media according to request.
+ */
+static int
+fxp_ifmedia_upd(struct ifnet *ifp)
+{
+ struct fxp_softc *sc = ifp->if_softc;
+ struct mii_data *mii;
+
+ mii = device_get_softc(sc->miibus);
+ mii_mediachg(mii);
+ return (0);
+}
+
+/*
+ * Notify the world which media we're using.
+ */
+static void
+fxp_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr)
+{
+ struct fxp_softc *sc = ifp->if_softc;
+ struct mii_data *mii;
+
+ mii = device_get_softc(sc->miibus);
+ mii_pollstat(mii);
+ ifmr->ifm_active = mii->mii_media_active;
+ ifmr->ifm_status = mii->mii_media_status;
+
+ if (ifmr->ifm_status & IFM_10_T && sc->flags & FXP_FLAG_CU_RESUME_BUG)
+ sc->cu_resume_bug = 1;
+ else
+ sc->cu_resume_bug = 0;
+}
+#endif
+
+/*
+ * Add a buffer to the end of the RFA buffer list.
+ * Return 0 if successful, 1 for failure. A failure results in
+ * adding the 'oldm' (if non-NULL) on to the end of the list -
+ * tossing out its old contents and recycling it.
+ * The RFA struct is stuck at the beginning of mbuf cluster and the
+ * data pointer is fixed up to point just past it.
+ */
+static int
+fxp_add_rfabuf(struct fxp_softc *sc, struct mbuf *oldm)
+{
+ u_int32_t v;
+ struct mbuf *m;
+ struct fxp_rfa *rfa, *p_rfa;
+
+ DBGLVL_PRINTK(4,"fxp_add_rfabuf called\n");
+
+ MGETHDR(m, M_DONTWAIT, MT_DATA);
+ if (m != NULL) {
+ MCLGET(m, M_DONTWAIT);
+ if ((m->m_flags & M_EXT) == 0) {
+ m_freem(m);
+ if (oldm == NULL)
+ return 1;
+ m = oldm;
+ m->m_data = m->m_ext.ext_buf;
+ }
+ } else {
+ if (oldm == NULL)
+ return 1;
+ m = oldm;
+ m->m_data = m->m_ext.ext_buf;
+ }
+
+ /*
+ * Move the data pointer up so that the incoming data packet
+ * will be 32-bit aligned.
+ */
+ m->m_data += RFA_ALIGNMENT_FUDGE;
+
+ /*
+ * Get a pointer to the base of the mbuf cluster and move
+ * data start past it.
+ */
+ rfa = mtod(m, struct fxp_rfa *);
+ m->m_data += sizeof(struct fxp_rfa);
+ rfa->size = (u_int16_t)(MCLBYTES - sizeof(struct fxp_rfa) - RFA_ALIGNMENT_FUDGE);
+
+ /*
+ * Initialize the rest of the RFA. Note that since the RFA
+ * is misaligned, we cannot store values directly. Instead,
+ * we use an optimized, inline copy.
+ */
+
+ rfa->rfa_status = 0;
+ rfa->rfa_control = FXP_RFA_CONTROL_EL;
+ rfa->actual_size = 0;
+
+ v = -1;
+ fxp_lwcopy(&v, (volatile u_int32_t*) rfa->link_addr);
+ fxp_lwcopy(&v, (volatile u_int32_t*) rfa->rbd_addr);
+
+ /*
+ * If there are other buffers already on the list, attach this
+ * one to the end by fixing up the tail to point to this one.
+ */
+ if (sc->rfa_headm != NULL) {
+ p_rfa = (struct fxp_rfa *) (sc->rfa_tailm->m_ext.ext_buf +
+ RFA_ALIGNMENT_FUDGE);
+ sc->rfa_tailm->m_next = m;
+ v = vtophys(rfa);
+ fxp_lwcopy(&v, (volatile u_int32_t*) p_rfa->link_addr);
+ p_rfa->rfa_control = 0;
+ } else {
+ sc->rfa_headm = m;
+ }
+ sc->rfa_tailm = m;
+
+ return (m == oldm);
+}
+
+#ifdef NOTUSED
+static volatile int
+fxp_miibus_readreg(device_t dev, int phy, int reg)
+{
+ struct fxp_softc *sc = device_get_softc(dev);
+ int count = 10000;
+ int value;
+
+ CSR_WRITE_4(sc, FXP_CSR_MDICONTROL,
+ (FXP_MDI_READ << 26) | (reg << 16) | (phy << 21));
+
+ while (((value = CSR_READ_4(sc, FXP_CSR_MDICONTROL)) & 0x10000000) == 0
+ && count--)
+ DELAY(10);
+
+ if (count <= 0)
+ device_printf(dev, "fxp_miibus_readreg: timed out\n");
+
+ return (value & 0xffff);
+}
+
+static void
+fxp_miibus_writereg(device_t dev, int phy, int reg, int value)
+{
+ struct fxp_softc *sc = device_get_softc(dev);
+ int count = 10000;
+
+ CSR_WRITE_4(sc, FXP_CSR_MDICONTROL,
+ (FXP_MDI_WRITE << 26) | (reg << 16) | (phy << 21) |
+ (value & 0xffff));
+
+ while ((CSR_READ_4(sc, FXP_CSR_MDICONTROL) & 0x10000000) == 0 &&
+ count--)
+ DELAY(10);
+
+ if (count <= 0)
+ device_printf(dev, "fxp_miibus_writereg: timed out\n");
+}
+#endif
+
+static int
+fxp_ioctl(struct ifnet *ifp, ioctl_command_t command, caddr_t data)
+{
+ struct fxp_softc *sc = ifp->if_softc;
+#ifdef NOTUSED
+ struct ifreq *ifr = (struct ifreq *)data;
+ struct mii_data *mii;
+#endif
+ int s, error = 0;
+
+ DBGLVL_PRINTK(2,"fxp_ioctl called\n");
+
+ s = splimp();
+
+ switch (command) {
+ case SIOCSIFADDR:
+ case SIOCGIFADDR:
+ case SIOCSIFMTU:
+ error = ether_ioctl(ifp, command, data);
+ break;
+
+ case SIOCSIFFLAGS:
+ if (ifp->if_flags & IFF_ALLMULTI)
+ sc->flags |= FXP_FLAG_ALL_MCAST;
+ else
+ sc->flags &= ~FXP_FLAG_ALL_MCAST;
+
+ /*
+ * If interface is marked up and not running, then start it.
+ * If it is marked down and running, stop it.
+ * XXX If it's up then re-initialize it. This is so flags
+ * such as IFF_PROMISC are handled.
+ */
+ if (ifp->if_flags & IFF_UP) {
+ fxp_init(sc);
+ } else {
+ if (ifp->if_flags & IFF_RUNNING)
+ fxp_stop(sc);
+ }
+ break;
+
+ case SIOCADDMULTI:
+ case SIOCDELMULTI:
+ if (ifp->if_flags & IFF_ALLMULTI)
+ sc->flags |= FXP_FLAG_ALL_MCAST;
+ else
+ sc->flags &= ~FXP_FLAG_ALL_MCAST;
+ /*
+ * Multicast list has changed; set the hardware filter
+ * accordingly.
+ */
+ if ((sc->flags & FXP_FLAG_ALL_MCAST) == 0)
+ fxp_mc_setup(sc);
+ /*
+ * fxp_mc_setup() can set FXP_FLAG_ALL_MCAST, so check it
+ * again rather than else {}.
+ */
+ if (sc->flags & FXP_FLAG_ALL_MCAST)
+ fxp_init(sc);
+ error = 0;
+ break;
+
+#ifdef NOTUSED
+ case SIOCSIFMEDIA:
+ case SIOCGIFMEDIA:
+ if (sc->miibus != NULL) {
+ mii = device_get_softc(sc->miibus);
+ error = ifmedia_ioctl(ifp, ifr,
+ &mii->mii_media, command);
+ } else {
+ error = ifmedia_ioctl(ifp, ifr, &sc->sc_media, command);
+ }
+ break;
+#endif
+
+ case SIO_RTEMS_SHOW_STATS:
+ fxp_stats(sc);
+ break;
+
+ default:
+ error = EINVAL;
+ }
+ splx(s);
+ return (error);
+}
+
+/*
+ * Program the multicast filter.
+ *
+ * We have an artificial restriction that the multicast setup command
+ * must be the first command in the chain, so we take steps to ensure
+ * this. By requiring this, it allows us to keep up the performance of
+ * the pre-initialized command ring (esp. link pointers) by not actually
+ * inserting the mcsetup command in the ring - i.e. its link pointer
+ * points to the TxCB ring, but the mcsetup descriptor itself is not part
+ * of it. We then can do 'CU_START' on the mcsetup descriptor and have it
+ * lead into the regular TxCB ring when it completes.
+ *
+ * This function must be called at splimp.
+ */
+static void
+fxp_mc_setup(struct fxp_softc *sc)
+{
+ struct fxp_cb_mcs *mcsp = sc->mcsp;
+ struct ifnet *ifp = &sc->sc_if;
+#ifdef NOTUSED
+ struct ifmultiaddr *ifma;
+#endif
+ int nmcasts;
+ int count;
+
+ DBGLVL_PRINTK(2,"fxp_mc_setup called\n");
+
+ /*
+ * If there are queued commands, we must wait until they are all
+ * completed. If we are already waiting, then add a NOP command
+ * with interrupt option so that we're notified when all commands
+ * have been completed - fxp_start() ensures that no additional
+ * TX commands will be added when need_mcsetup is true.
+ */
+ if (sc->tx_queued) {
+ struct fxp_cb_tx *txp;
+
+ /*
+ * need_mcsetup will be true if we are already waiting for the
+ * NOP command to be completed (see below). In this case, bail.
+ */
+ if (sc->need_mcsetup)
+ return;
+ sc->need_mcsetup = 1;
+
+ /*
+ * Add a NOP command with interrupt so that we are notified when all
+ * TX commands have been processed.
+ */
+ txp = sc->cbl_last->next;
+ txp->mb_head = NULL;
+ txp->cb_status = 0;
+ txp->cb_command = FXP_CB_COMMAND_NOP |
+ FXP_CB_COMMAND_S | FXP_CB_COMMAND_I;
+ /*
+ * Advance the end of list forward.
+ */
+ sc->cbl_last->cb_command &= ~FXP_CB_COMMAND_S;
+ sc->cbl_last = txp;
+ sc->tx_queued++;
+ /*
+ * Issue a resume in case the CU has just suspended.
+ */
+ fxp_scb_wait(sc);
+ fxp_scb_cmd(sc, FXP_SCB_COMMAND_CU_RESUME);
+ /*
+ * Set a 5 second timer just in case we don't hear from the
+ * card again.
+ */
+ ifp->if_timer = 5;
+
+ return;
+ }
+ sc->need_mcsetup = 0;
+
+ /*
+ * Initialize multicast setup descriptor.
+ */
+ mcsp->next = sc->cbl_base;
+ mcsp->mb_head = NULL;
+ mcsp->cb_status = 0;
+ mcsp->cb_command = FXP_CB_COMMAND_MCAS |
+ FXP_CB_COMMAND_S | FXP_CB_COMMAND_I;
+ mcsp->link_addr = vtophys(&sc->cbl_base->cb_status);
+
+ nmcasts = 0;
+#ifdef NOTUSED /* FIXME: Multicast not supported? */
+ if ((sc->flags & FXP_FLAG_ALL_MCAST) == 0) {
+#if __FreeBSD_version < 500000
+ LIST_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) {
+#else
+ TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) {
+#endif
+ if (ifma->ifma_addr->sa_family != AF_LINK)
+ continue;
+ if (nmcasts >= MAXMCADDR) {
+ sc->flags |= FXP_FLAG_ALL_MCAST;
+ nmcasts = 0;
+ break;
+ }
+ memcpy((void *)(uintptr_t)(volatile void *)
+ &sc->mcsp->mc_addr[nmcasts][0],
+ LLADDR((struct sockaddr_dl *)ifma->ifma_addr), 6);
+ nmcasts++;
+ }
+ }
+#endif
+ mcsp->mc_cnt = nmcasts * 6;
+ sc->cbl_first = sc->cbl_last = (struct fxp_cb_tx *) mcsp;
+ sc->tx_queued = 1;
+
+ /*
+ * Wait until command unit is not active. This should never
+ * be the case when nothing is queued, but make sure anyway.
+ */
+ count = 100;
+ while ((CSR_READ_1(sc, FXP_CSR_SCB_RUSCUS) >> 6) ==
+ FXP_SCB_CUS_ACTIVE && --count)
+ DELAY(10);
+ if (count == 0) {
+ device_printf(sc->dev, "command queue timeout\n");
+ return;
+ }
+
+ /*
+ * Start the multicast setup command.
+ */
+ fxp_scb_wait(sc);
+ CSR_WRITE_4(sc, FXP_CSR_SCB_GENERAL, vtophys(&mcsp->cb_status));
+ fxp_scb_cmd(sc, FXP_SCB_COMMAND_CU_START);
+
+ ifp->if_timer = 2;
+ return;
+ }
+
+#endif /* defined(__i386__) */
diff --git a/bsps/shared/net/if_fxpreg.h b/bsps/shared/net/if_fxpreg.h
new file mode 100644
index 0000000000..9bf4e59ff5
--- /dev/null
+++ b/bsps/shared/net/if_fxpreg.h
@@ -0,0 +1,370 @@
+/*
+ * Copyright (c) 1995, David Greenman
+ * Copyright (c) 2001 Jonathan Lemon <jlemon@freebsd.org>
+ * 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 unmodified, 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 AUTHOR 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 AUTHOR 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.
+ *
+ * $FreeBSD: src/sys/dev/fxp/if_fxpreg.h,v 1.23.2.4 2001/08/31 02:17:02 jlemon Exp $
+ */
+
+#define FXP_VENDORID_INTEL 0x8086
+
+#define FXP_PCI_MMBA 0x10
+#define FXP_PCI_IOBA 0x14
+
+/*
+ * Control/status registers.
+ */
+#define FXP_CSR_SCB_RUSCUS 0 /* scb_rus/scb_cus (1 byte) */
+#define FXP_CSR_SCB_STATACK 1 /* scb_statack (1 byte) */
+#define FXP_CSR_SCB_COMMAND 2 /* scb_command (1 byte) */
+#define FXP_CSR_SCB_INTRCNTL 3 /* scb_intrcntl (1 byte) */
+#define FXP_CSR_SCB_GENERAL 4 /* scb_general (4 bytes) */
+#define FXP_CSR_PORT 8 /* port (4 bytes) */
+#define FXP_CSR_FLASHCONTROL 12 /* flash control (2 bytes) */
+#define FXP_CSR_EEPROMCONTROL 14 /* eeprom control (2 bytes) */
+#define FXP_CSR_MDICONTROL 16 /* mdi control (4 bytes) */
+#define FXP_CSR_FLOWCONTROL 0x19 /* flow control (2 bytes) */
+#define FXP_CSR_GENCONTROL 0x1C /* general control (1 byte) */
+
+/*
+ * FOR REFERENCE ONLY, the old definition of FXP_CSR_SCB_RUSCUS:
+ *
+ * volatile u_int8_t :2,
+ * scb_rus:4,
+ * scb_cus:2;
+ */
+
+#define FXP_PORT_SOFTWARE_RESET 0
+#define FXP_PORT_SELFTEST 1
+#define FXP_PORT_SELECTIVE_RESET 2
+#define FXP_PORT_DUMP 3
+
+#define FXP_SCB_RUS_IDLE 0
+#define FXP_SCB_RUS_SUSPENDED 1
+#define FXP_SCB_RUS_NORESOURCES 2
+#define FXP_SCB_RUS_READY 4
+#define FXP_SCB_RUS_SUSP_NORBDS 9
+#define FXP_SCB_RUS_NORES_NORBDS 10
+#define FXP_SCB_RUS_READY_NORBDS 12
+
+#define FXP_SCB_CUS_IDLE 0
+#define FXP_SCB_CUS_SUSPENDED 1
+#define FXP_SCB_CUS_ACTIVE 2
+
+#define FXP_SCB_INTR_DISABLE 0x01 /* Disable all interrupts */
+#define FXP_SCB_INTR_SWI 0x02 /* Generate SWI */
+#define FXP_SCB_INTMASK_FCP 0x04
+#define FXP_SCB_INTMASK_ER 0x08
+#define FXP_SCB_INTMASK_RNR 0x10
+#define FXP_SCB_INTMASK_CNA 0x20
+#define FXP_SCB_INTMASK_FR 0x40
+#define FXP_SCB_INTMASK_CXTNO 0x80
+
+#define FXP_SCB_STATACK_FCP 0x01 /* Flow Control Pause */
+#define FXP_SCB_STATACK_ER 0x02 /* Early Receive */
+#define FXP_SCB_STATACK_SWI 0x04
+#define FXP_SCB_STATACK_MDI 0x08
+#define FXP_SCB_STATACK_RNR 0x10
+#define FXP_SCB_STATACK_CNA 0x20
+#define FXP_SCB_STATACK_FR 0x40
+#define FXP_SCB_STATACK_CXTNO 0x80
+
+#define FXP_SCB_COMMAND_CU_NOP 0x00
+#define FXP_SCB_COMMAND_CU_START 0x10
+#define FXP_SCB_COMMAND_CU_RESUME 0x20
+#define FXP_SCB_COMMAND_CU_DUMP_ADR 0x40
+#define FXP_SCB_COMMAND_CU_DUMP 0x50
+#define FXP_SCB_COMMAND_CU_BASE 0x60
+#define FXP_SCB_COMMAND_CU_DUMPRESET 0x70
+
+#define FXP_SCB_COMMAND_RU_NOP 0
+#define FXP_SCB_COMMAND_RU_START 1
+#define FXP_SCB_COMMAND_RU_RESUME 2
+#define FXP_SCB_COMMAND_RU_ABORT 4
+#define FXP_SCB_COMMAND_RU_LOADHDS 5
+#define FXP_SCB_COMMAND_RU_BASE 6
+#define FXP_SCB_COMMAND_RU_RBDRESUME 7
+
+/*
+ * Command block definitions
+ */
+struct fxp_cb_nop {
+ void *fill[2];
+ volatile u_int16_t cb_status;
+ volatile u_int16_t cb_command;
+ volatile u_int32_t link_addr;
+};
+struct fxp_cb_ias {
+ void *fill[2];
+ volatile u_int16_t cb_status;
+ volatile u_int16_t cb_command;
+ volatile u_int32_t link_addr;
+ volatile u_int8_t macaddr[6];
+};
+/* I hate bit-fields :-( */
+struct fxp_cb_config {
+ void *fill[2];
+ volatile u_int16_t cb_status;
+ volatile u_int16_t cb_command;
+ volatile u_int32_t link_addr;
+ volatile u_int byte_count:6,
+ :2;
+ volatile u_int rx_fifo_limit:4,
+ tx_fifo_limit:3,
+ :1;
+ volatile u_int8_t adaptive_ifs;
+ volatile u_int mwi_enable:1, /* 8,9 */
+ type_enable:1, /* 8,9 */
+ read_align_en:1, /* 8,9 */
+ end_wr_on_cl:1, /* 8,9 */
+ :4;
+ volatile u_int rx_dma_bytecount:7,
+ :1;
+ volatile u_int tx_dma_bytecount:7,
+ dma_mbce:1;
+ volatile u_int late_scb:1, /* 7 */
+ direct_dma_dis:1, /* 8,9 */
+ tno_int_or_tco_en:1, /* 7,9 */
+ ci_int:1,
+ ext_txcb_dis:1, /* 8,9 */
+ ext_stats_dis:1, /* 8,9 */
+ keep_overrun_rx:1,
+ save_bf:1;
+ volatile u_int disc_short_rx:1,
+ underrun_retry:2,
+ :3,
+ two_frames:1, /* 8,9 */
+ dyn_tbd:1; /* 8,9 */
+ volatile u_int mediatype:1, /* 7 */
+ :6,
+ csma_dis:1; /* 8,9 */
+ volatile u_int tcp_udp_cksum:1, /* 9 */
+ :3,
+ vlan_tco:1, /* 8,9 */
+ link_wake_en:1, /* 8,9 */
+ arp_wake_en:1, /* 8 */
+ mc_wake_en:1; /* 8 */
+ volatile u_int :3,
+ nsai:1,
+ preamble_length:2,
+ loopback:2;
+ volatile u_int linear_priority:3, /* 7 */
+ :5;
+ volatile u_int linear_pri_mode:1, /* 7 */
+ :3,
+ interfrm_spacing:4;
+ volatile u_int :8;
+ volatile u_int :8;
+ volatile u_int promiscuous:1,
+ bcast_disable:1,
+ wait_after_win:1, /* 8,9 */
+ :1,
+ ignore_ul:1, /* 8,9 */
+ crc16_en:1, /* 9 */
+ :1,
+ crscdt:1;
+ volatile u_int fc_delay_lsb:8; /* 8,9 */
+ volatile u_int fc_delay_msb:8; /* 8,9 */
+ volatile u_int stripping:1,
+ padding:1,
+ rcv_crc_xfer:1,
+ long_rx_en:1, /* 8,9 */
+ pri_fc_thresh:3, /* 8,9 */
+ :1;
+ volatile u_int ia_wake_en:1, /* 8 */
+ magic_pkt_dis:1, /* 8,9,!9ER */
+ tx_fc_dis:1, /* 8,9 */
+ rx_fc_restop:1, /* 8,9 */
+ rx_fc_restart:1, /* 8,9 */
+ fc_filter:1, /* 8,9 */
+ force_fdx:1,
+ fdx_pin_en:1;
+ volatile u_int :5,
+ pri_fc_loc:1, /* 8,9 */
+ multi_ia:1,
+ :1;
+ volatile u_int :3,
+ mc_all:1,
+ :4;
+};
+
+#define MAXMCADDR 80
+struct fxp_cb_mcs {
+ struct fxp_cb_tx *next;
+ struct mbuf *mb_head;
+ volatile u_int16_t cb_status;
+ volatile u_int16_t cb_command;
+ volatile u_int32_t link_addr;
+ volatile u_int16_t mc_cnt;
+ volatile u_int8_t mc_addr[MAXMCADDR][6];
+};
+
+/*
+ * Number of DMA segments in a TxCB. Note that this is carefully
+ * chosen to make the total struct size an even power of two. It's
+ * critical that no TxCB be split across a page boundry since
+ * no attempt is made to allocate physically contiguous memory.
+ *
+ */
+#ifdef __alpha__ /* XXX - should be conditional on pointer size */
+#define FXP_NTXSEG 28
+#else
+#define FXP_NTXSEG 29
+#endif
+
+struct fxp_tbd {
+ volatile u_int32_t tb_addr;
+ volatile u_int32_t tb_size;
+};
+struct fxp_cb_tx {
+ struct fxp_cb_tx *next;
+ struct mbuf *mb_head;
+ volatile u_int16_t cb_status;
+ volatile u_int16_t cb_command;
+ volatile u_int32_t link_addr;
+ volatile u_int32_t tbd_array_addr;
+ volatile u_int16_t byte_count;
+ volatile u_int8_t tx_threshold;
+ volatile u_int8_t tbd_number;
+ /*
+ * The following structure isn't actually part of the TxCB,
+ * unless the extended TxCB feature is being used. In this
+ * case, the first two elements of the structure below are
+ * fetched along with the TxCB.
+ */
+ volatile struct fxp_tbd tbd[FXP_NTXSEG];
+};
+
+/*
+ * Control Block (CB) definitions
+ */
+
+/* status */
+#define FXP_CB_STATUS_OK 0x2000
+#define FXP_CB_STATUS_C 0x8000
+/* commands */
+#define FXP_CB_COMMAND_NOP 0x0
+#define FXP_CB_COMMAND_IAS 0x1
+#define FXP_CB_COMMAND_CONFIG 0x2
+#define FXP_CB_COMMAND_MCAS 0x3
+#define FXP_CB_COMMAND_XMIT 0x4
+#define FXP_CB_COMMAND_RESRV 0x5
+#define FXP_CB_COMMAND_DUMP 0x6
+#define FXP_CB_COMMAND_DIAG 0x7
+/* command flags */
+#define FXP_CB_COMMAND_SF 0x0008 /* simple/flexible mode */
+#define FXP_CB_COMMAND_I 0x2000 /* generate interrupt on completion */
+#define FXP_CB_COMMAND_S 0x4000 /* suspend on completion */
+#define FXP_CB_COMMAND_EL 0x8000 /* end of list */
+
+/*
+ * RFA definitions
+ */
+
+struct fxp_rfa {
+ volatile u_int16_t rfa_status;
+ volatile u_int16_t rfa_control;
+ volatile u_int8_t link_addr[4];
+ volatile u_int8_t rbd_addr[4];
+ volatile u_int16_t actual_size;
+ volatile u_int16_t size;
+};
+#define FXP_RFA_STATUS_RCOL 0x0001 /* receive collision */
+#define FXP_RFA_STATUS_IAMATCH 0x0002 /* 0 = matches station address */
+#define FXP_RFA_STATUS_S4 0x0010 /* receive error from PHY */
+#define FXP_RFA_STATUS_TL 0x0020 /* type/length */
+#define FXP_RFA_STATUS_FTS 0x0080 /* frame too short */
+#define FXP_RFA_STATUS_OVERRUN 0x0100 /* DMA overrun */
+#define FXP_RFA_STATUS_RNR 0x0200 /* no resources */
+#define FXP_RFA_STATUS_ALIGN 0x0400 /* alignment error */
+#define FXP_RFA_STATUS_CRC 0x0800 /* CRC error */
+#define FXP_RFA_STATUS_OK 0x2000 /* packet received okay */
+#define FXP_RFA_STATUS_C 0x8000 /* packet reception complete */
+#define FXP_RFA_CONTROL_SF 0x08 /* simple/flexible memory mode */
+#define FXP_RFA_CONTROL_H 0x10 /* header RFD */
+#define FXP_RFA_CONTROL_S 0x4000 /* suspend after reception */
+#define FXP_RFA_CONTROL_EL 0x8000 /* end of list */
+
+/*
+ * Statistics dump area definitions
+ */
+struct fxp_stats {
+ volatile u_int32_t tx_good;
+ volatile u_int32_t tx_maxcols;
+ volatile u_int32_t tx_latecols;
+ volatile u_int32_t tx_underruns;
+ volatile u_int32_t tx_lostcrs;
+ volatile u_int32_t tx_deffered;
+ volatile u_int32_t tx_single_collisions;
+ volatile u_int32_t tx_multiple_collisions;
+ volatile u_int32_t tx_total_collisions;
+ volatile u_int32_t rx_good;
+ volatile u_int32_t rx_crc_errors;
+ volatile u_int32_t rx_alignment_errors;
+ volatile u_int32_t rx_rnr_errors;
+ volatile u_int32_t rx_overrun_errors;
+ volatile u_int32_t rx_cdt_errors;
+ volatile u_int32_t rx_shortframes;
+ volatile u_int32_t completion_status;
+};
+#define FXP_STATS_DUMP_COMPLETE 0xa005
+#define FXP_STATS_DR_COMPLETE 0xa007
+
+/*
+ * Serial EEPROM control register bits
+ */
+#define FXP_EEPROM_EESK 0x01 /* shift clock */
+#define FXP_EEPROM_EECS 0x02 /* chip select */
+#define FXP_EEPROM_EEDI 0x04 /* data in */
+#define FXP_EEPROM_EEDO 0x08 /* data out */
+
+/*
+ * Serial EEPROM opcodes, including start bit
+ */
+#define FXP_EEPROM_OPC_ERASE 0x4
+#define FXP_EEPROM_OPC_WRITE 0x5
+#define FXP_EEPROM_OPC_READ 0x6
+
+/*
+ * Management Data Interface opcodes
+ */
+#define FXP_MDI_WRITE 0x1
+#define FXP_MDI_READ 0x2
+
+/*
+ * PHY device types
+ */
+#define FXP_PHY_DEVICE_MASK 0x3f00
+#define FXP_PHY_SERIAL_ONLY 0x8000
+#define FXP_PHY_NONE 0
+#define FXP_PHY_82553A 1
+#define FXP_PHY_82553C 2
+#define FXP_PHY_82503 3
+#define FXP_PHY_DP83840 4
+#define FXP_PHY_80C240 5
+#define FXP_PHY_80C24 6
+#define FXP_PHY_82555 7
+#define FXP_PHY_DP83840A 10
+#define FXP_PHY_82555B 11
diff --git a/bsps/shared/net/open_eth.c b/bsps/shared/net/open_eth.c
new file mode 100644
index 0000000000..88df0882cf
--- /dev/null
+++ b/bsps/shared/net/open_eth.c
@@ -0,0 +1,767 @@
+/*
+ * RTEMS driver for Opencores Ethernet Controller
+ *
+ * Weakly based on dec21140 rtems driver and open_eth linux driver
+ * Written by Jiri Gaisler, Gaisler Research
+ *
+ * The license and distribution terms for this file may be
+ * found in the file LICENSE in this distribution or at
+ * http://www.rtems.org/license/LICENSE.
+ *
+ */
+
+/*
+ * This driver current only supports architectures with the old style
+ * exception processing. The following checks try to keep this
+ * from being compiled on systems which can't support this driver.
+ *
+ * NOTE: The i386, ARM, and PowerPC use a different interrupt API than
+ * that used by this driver.
+ */
+
+#define __INSIDE_RTEMS_BSD_TCPIP_STACK__
+
+#if defined(__i386__) || defined(__arm__) || defined(__PPC__)
+ #define OPENETH_NOT_SUPPORTED
+#endif
+
+#if !defined(OPENETH_NOT_SUPPORTED)
+#include <bsp.h>
+#include <rtems.h>
+
+#include <bsp.h>
+
+#include <inttypes.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <errno.h>
+
+#include <rtems/error.h>
+#include <rtems/rtems_bsdnet.h>
+#include <libchip/open_eth.h>
+
+#include <sys/param.h>
+#include <sys/mbuf.h>
+
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <netinet/if_ether.h>
+
+#ifdef malloc
+#undef malloc
+#endif
+#ifdef free
+#undef free
+#endif
+
+extern rtems_isr_entry set_vector( rtems_isr_entry, rtems_vector_number, int );
+
+ /*
+#define OPEN_ETH_DEBUG
+ */
+
+#ifdef CPU_U32_FIX
+extern void ipalign(struct mbuf *m);
+#endif
+
+/* message descriptor entry */
+struct MDTX
+{
+ char *buf;
+};
+
+struct MDRX
+{
+ struct mbuf *m;
+};
+
+/*
+ * Number of OCs supported by this driver
+ */
+#define NOCDRIVER 1
+
+/*
+ * Receive buffer size -- Allow for a full ethernet packet including CRC
+ */
+#define RBUF_SIZE 1536
+
+#define ET_MINLEN 64 /* minimum message length */
+
+/*
+ * RTEMS event used by interrupt handler to signal driver tasks.
+ * This must not be any of the events used by the network task synchronization.
+ */
+#define INTERRUPT_EVENT RTEMS_EVENT_1
+
+/*
+ * RTEMS event used to start transmit daemon.
+ * This must not be the same as INTERRUPT_EVENT.
+ */
+#define START_TRANSMIT_EVENT RTEMS_EVENT_2
+
+ /* event to send when tx buffers become available */
+#define OPEN_ETH_TX_WAIT_EVENT RTEMS_EVENT_3
+
+ /* suspend when all TX descriptors exhausted */
+ /*
+#define OETH_SUSPEND_NOTXBUF
+ */
+
+#if (MCLBYTES < RBUF_SIZE)
+# error "Driver must have MCLBYTES > RBUF_SIZE"
+#endif
+
+/*
+ * Per-device data
+ */
+struct open_eth_softc
+{
+
+ struct arpcom arpcom;
+
+ oeth_regs *regs;
+
+ int acceptBroadcast;
+ rtems_id rxDaemonTid;
+ rtems_id txDaemonTid;
+
+ unsigned int tx_ptr;
+ unsigned int rx_ptr;
+ unsigned int txbufs;
+ unsigned int rxbufs;
+ struct MDTX *txdesc;
+ struct MDRX *rxdesc;
+ rtems_vector_number vector;
+ unsigned int en100MHz;
+
+ /*
+ * Statistics
+ */
+ unsigned long rxInterrupts;
+ unsigned long rxPackets;
+ unsigned long rxLengthError;
+ unsigned long rxNonOctet;
+ unsigned long rxBadCRC;
+ unsigned long rxOverrun;
+ unsigned long rxMiss;
+ unsigned long rxCollision;
+
+ unsigned long txInterrupts;
+ unsigned long txDeferred;
+ unsigned long txHeartbeat;
+ unsigned long txLateCollision;
+ unsigned long txRetryLimit;
+ unsigned long txUnderrun;
+ unsigned long txLostCarrier;
+ unsigned long txRawWait;
+};
+
+static struct open_eth_softc oc;
+
+/* OPEN_ETH interrupt handler */
+
+static rtems_isr
+open_eth_interrupt_handler (rtems_vector_number v)
+{
+ uint32_t status;
+
+ /* read and clear interrupt cause */
+
+ status = oc.regs->int_src;
+ oc.regs->int_src = status;
+
+ /* Frame received? */
+
+ if (status & (OETH_INT_RXF | OETH_INT_RXE))
+ {
+ oc.rxInterrupts++;
+ rtems_bsdnet_event_send (oc.rxDaemonTid, INTERRUPT_EVENT);
+ }
+#ifdef OETH_SUSPEND_NOTXBUF
+ if (status & (OETH_INT_MASK_TXB | OETH_INT_MASK_TXC | OETH_INT_MASK_TXE))
+ {
+ oc.txInterrupts++;
+ rtems_bsdnet_event_send (oc.txDaemonTid, OPEN_ETH_TX_WAIT_EVENT);
+ }
+#endif
+ /*
+#ifdef __leon__
+ LEON_Clear_interrupt(v-0x10);
+#endif
+ */
+}
+
+static uint32_t read_mii(uint32_t addr)
+{
+ while (oc.regs->miistatus & OETH_MIISTATUS_BUSY) {}
+ oc.regs->miiaddress = addr << 8;
+ oc.regs->miicommand = OETH_MIICOMMAND_RSTAT;
+ while (oc.regs->miistatus & OETH_MIISTATUS_BUSY) {}
+ if (!(oc.regs->miistatus & OETH_MIISTATUS_NVALID))
+ return(oc.regs->miirx_data);
+ else {
+ printf("open_eth: failed to read mii\n");
+ return (0);
+ }
+}
+
+static void write_mii(uint32_t addr, uint32_t data)
+{
+ while (oc.regs->miistatus & OETH_MIISTATUS_BUSY) {}
+ oc.regs->miiaddress = addr << 8;
+ oc.regs->miitx_data = data;
+ oc.regs->miicommand = OETH_MIICOMMAND_WCTRLDATA;
+ while (oc.regs->miistatus & OETH_MIISTATUS_BUSY) {}
+}
+/*
+ * Initialize the ethernet hardware
+ */
+static void
+open_eth_initialize_hardware (struct open_eth_softc *sc)
+{
+ struct mbuf *m;
+ int i;
+ int mii_cr = 0;
+
+ oeth_regs *regs;
+
+ regs = sc->regs;
+
+ /* Reset the controller. */
+
+ regs->ctrlmoder = 0;
+ regs->moder = OETH_MODER_RST; /* Reset ON */
+ regs->moder = 0; /* Reset OFF */
+
+ /* reset PHY and wait for complettion */
+ mii_cr = 0x3300;
+ if (!sc->en100MHz) mii_cr = 0;
+ write_mii(0, mii_cr | 0x8000);
+ while (read_mii(0) & 0x8000) {}
+ if (!sc->en100MHz) write_mii(0, 0);
+ mii_cr = read_mii(0);
+ printf("open_eth: driver attached, PHY config : 0x%04" PRIx32 "\n", read_mii(0));
+
+#ifdef OPEN_ETH_DEBUG
+ printf("mii_cr: %04x\n", mii_cr);
+ for (i=0;i<21;i++)
+ printf("mii_reg %2d : 0x%04x\n", i, read_mii(i));
+#endif
+
+ /* Setting TXBD base to sc->txbufs */
+
+ regs->tx_bd_num = sc->txbufs;
+
+ /* Initialize rx/tx pointers. */
+
+ sc->rx_ptr = 0;
+ sc->tx_ptr = 0;
+
+ /* Set min/max packet length */
+ regs->packet_len = 0x00400600;
+
+ /* Set IPGT register to recomended value */
+ regs->ipgt = 0x00000015;
+
+ /* Set IPGR1 register to recomended value */
+ regs->ipgr1 = 0x0000000c;
+
+ /* Set IPGR2 register to recomended value */
+ regs->ipgr2 = 0x00000012;
+
+ /* Set COLLCONF register to recomended value */
+ regs->collconf = 0x000f003f;
+
+ /* initialize TX descriptors */
+
+ sc->txdesc = calloc(sc->txbufs, sizeof(*sc->txdesc));
+ for (i = 0; i < sc->txbufs; i++)
+ {
+ sc->regs->xd[i].len_status = OETH_TX_BD_PAD | OETH_TX_BD_CRC;
+ sc->txdesc[i].buf = calloc(1, OETH_MAXBUF_LEN);
+#ifdef OPEN_ETH_DEBUG
+ printf("TXBUF: %08x\n", (int) sc->txdesc[i].buf);
+#endif
+ }
+ sc->regs->xd[sc->txbufs - 1].len_status |= OETH_TX_BD_WRAP;
+
+ /* allocate RX buffers */
+
+ sc->rxdesc = calloc(sc->rxbufs, sizeof(*sc->rxdesc));
+ for (i = 0; i < sc->rxbufs; i++)
+ {
+
+ MGETHDR (m, M_WAIT, MT_DATA);
+ MCLGET (m, M_WAIT);
+ m->m_pkthdr.rcvif = &sc->arpcom.ac_if;
+ sc->rxdesc[i].m = m;
+ sc->regs->xd[i + sc->txbufs].addr = mtod (m, uint32_t*);
+ sc->regs->xd[i + sc->txbufs].len_status =
+ OETH_RX_BD_EMPTY | OETH_RX_BD_IRQ;
+#ifdef OPEN_ETH_DEBUG
+ printf("RXBUF: %08x\n", (int) sc->rxdesc[i].m);
+#endif
+ }
+ sc->regs->xd[sc->rxbufs + sc->txbufs - 1].len_status |= OETH_RX_BD_WRAP;
+
+
+ /* set ethernet address. */
+
+ regs->mac_addr1 = sc->arpcom.ac_enaddr[0] << 8 | sc->arpcom.ac_enaddr[1];
+
+ uint32_t mac_addr0;
+ mac_addr0 = sc->arpcom.ac_enaddr[2];
+ mac_addr0 <<= 8;
+ mac_addr0 |= sc->arpcom.ac_enaddr[3];
+ mac_addr0 <<= 8;
+ mac_addr0 |= sc->arpcom.ac_enaddr[4];
+ mac_addr0 <<= 8;
+ mac_addr0 |= sc->arpcom.ac_enaddr[5];
+
+ regs->mac_addr0 = mac_addr0;
+
+ /* install interrupt vector */
+ set_vector (open_eth_interrupt_handler, sc->vector, 1);
+
+ /* clear all pending interrupts */
+
+ regs->int_src = 0xffffffff;
+
+ /* MAC mode register: PAD, IFG, CRCEN */
+
+ regs->moder = OETH_MODER_PAD | OETH_MODER_CRCEN | ((mii_cr & 0x100) << 2);
+
+ /* enable interrupts */
+
+ regs->int_mask = OETH_INT_MASK_RXF | OETH_INT_MASK_RXE | OETH_INT_MASK_RXC;
+
+#ifdef OETH_SUSPEND_NOTXBUF
+ regs->int_mask |= OETH_INT_MASK_TXB | OETH_INT_MASK_TXC | OETH_INT_MASK_TXE | OETH_INT_BUSY;*/
+ sc->regs->xd[(sc->txbufs - 1)/2].len_status |= OETH_TX_BD_IRQ;
+ sc->regs->xd[sc->txbufs - 1].len_status |= OETH_TX_BD_IRQ;
+#endif
+
+ regs->moder |= OETH_MODER_RXEN | OETH_MODER_TXEN;
+}
+
+static void
+open_eth_rxDaemon (void *arg)
+{
+ struct ether_header *eh;
+ struct open_eth_softc *dp = (struct open_eth_softc *) &oc;
+ struct ifnet *ifp = &dp->arpcom.ac_if;
+ struct mbuf *m;
+ unsigned int len;
+ uint32_t len_status;
+ unsigned int bad;
+ rtems_event_set events;
+
+
+ for (;;)
+ {
+
+ rtems_bsdnet_event_receive (INTERRUPT_EVENT,
+ RTEMS_WAIT | RTEMS_EVENT_ANY,
+ RTEMS_NO_TIMEOUT, &events);
+#ifdef OPEN_ETH_DEBUG
+ printf ("r\n");
+#endif
+
+ while (!
+ ((len_status =
+ dp->regs->xd[dp->rx_ptr+dp->txbufs].len_status) & OETH_RX_BD_EMPTY))
+ {
+ bad = 0;
+ if (len_status & (OETH_RX_BD_TOOLONG | OETH_RX_BD_SHORT))
+ {
+ dp->rxLengthError++;
+ bad = 1;
+ }
+ if (len_status & OETH_RX_BD_DRIBBLE)
+ {
+ dp->rxNonOctet++;
+ bad = 1;
+ }
+ if (len_status & OETH_RX_BD_CRCERR)
+ {
+ dp->rxBadCRC++;
+ bad = 1;
+ }
+ if (len_status & OETH_RX_BD_OVERRUN)
+ {
+ dp->rxOverrun++;
+ bad = 1;
+ }
+ if (len_status & OETH_RX_BD_MISS)
+ {
+ dp->rxMiss++;
+ bad = 1;
+ }
+ if (len_status & OETH_RX_BD_LATECOL)
+ {
+ dp->rxCollision++;
+ bad = 1;
+ }
+
+ if (!bad)
+ {
+ /* pass on the packet in the receive buffer */
+ len = len_status >> 16;
+ m = (struct mbuf *) (dp->rxdesc[dp->rx_ptr].m);
+ m->m_len = m->m_pkthdr.len =
+ len - sizeof (struct ether_header);
+ eh = mtod (m, struct ether_header *);
+ m->m_data += sizeof (struct ether_header);
+#ifdef CPU_U32_FIX
+ ipalign(m); /* Align packet on 32-bit boundary */
+#endif
+
+ ether_input (ifp, eh, m);
+
+ /* get a new mbuf */
+ MGETHDR (m, M_WAIT, MT_DATA);
+ MCLGET (m, M_WAIT);
+ m->m_pkthdr.rcvif = ifp;
+ dp->rxdesc[dp->rx_ptr].m = m;
+ dp->regs->xd[dp->rx_ptr + dp->txbufs].addr =
+ (uint32_t*) mtod (m, void *);
+ dp->rxPackets++;
+ }
+
+ dp->regs->xd[dp->rx_ptr+dp->txbufs].len_status =
+ (dp->regs->xd[dp->rx_ptr+dp->txbufs].len_status &
+ ~OETH_TX_BD_STATS) | OETH_TX_BD_READY;
+ dp->rx_ptr = (dp->rx_ptr + 1) % dp->rxbufs;
+ }
+ }
+}
+
+static int inside = 0;
+static void
+sendpacket (struct ifnet *ifp, struct mbuf *m)
+{
+ struct open_eth_softc *dp = ifp->if_softc;
+ unsigned char *temp;
+ struct mbuf *n;
+ uint32_t len, len_status;
+
+ if (inside) printf ("error: sendpacket re-entered!!\n");
+ inside = 1;
+ /*
+ * Waiting for Transmitter ready
+ */
+ n = m;
+
+ while (dp->regs->xd[dp->tx_ptr].len_status & OETH_TX_BD_READY)
+ {
+#ifdef OETH_SUSPEND_NOTXBUF
+ rtems_event_set events;
+ rtems_bsdnet_event_receive (OPEN_ETH_TX_WAIT_EVENT,
+ RTEMS_WAIT | RTEMS_EVENT_ANY,
+ RTEMS_MILLISECONDS_TO_TICKS(500), &events);
+#endif
+ }
+
+ len = 0;
+ temp = (unsigned char *) dp->txdesc[dp->tx_ptr].buf;
+ dp->regs->xd[dp->tx_ptr].addr = (uint32_t*) temp;
+
+#ifdef OPEN_ETH_DEBUG
+ printf("TXD: 0x%08x\n", (int) m->m_data);
+#endif
+ for (;;)
+ {
+#ifdef OPEN_ETH_DEBUG
+ int i;
+ printf("MBUF: 0x%08x : ", (int) m->m_data);
+ for (i=0;i<m->m_len;i++)
+ printf("%x%x", (m->m_data[i] >> 4) & 0x0ff, m->m_data[i] & 0x0ff);
+ printf("\n");
+#endif
+ len += m->m_len;
+ if (len <= RBUF_SIZE)
+ memcpy ((void *) temp, (char *) m->m_data, m->m_len);
+ temp += m->m_len;
+ if ((m = m->m_next) == NULL)
+ break;
+ }
+
+ m_freem (n);
+
+ /* don't send long packets */
+
+ if (len <= RBUF_SIZE) {
+
+ /* Clear all of the status flags. */
+ len_status = dp->regs->xd[dp->tx_ptr].len_status & ~OETH_TX_BD_STATS;
+
+ /* If the frame is short, tell CPM to pad it. */
+ if (len < ET_MINLEN) {
+ len_status |= OETH_TX_BD_PAD;
+ len = ET_MINLEN;
+ }
+ else
+ len_status &= ~OETH_TX_BD_PAD;
+
+ /* write buffer descriptor length and status */
+ len_status &= 0x0000ffff;
+ len_status |= (len << 16) | (OETH_TX_BD_READY | OETH_TX_BD_CRC);
+ dp->regs->xd[dp->tx_ptr].len_status = len_status;
+ dp->tx_ptr = (dp->tx_ptr + 1) % dp->txbufs;
+
+ }
+ inside = 0;
+}
+
+/*
+ * Driver transmit daemon
+ */
+static void
+open_eth_txDaemon (void *arg)
+{
+ struct open_eth_softc *sc = (struct open_eth_softc *) arg;
+ struct ifnet *ifp = &sc->arpcom.ac_if;
+ struct mbuf *m;
+ rtems_event_set events;
+
+ for (;;)
+ {
+ /*
+ * Wait for packet
+ */
+
+ rtems_bsdnet_event_receive (START_TRANSMIT_EVENT,
+ RTEMS_EVENT_ANY | RTEMS_WAIT,
+ RTEMS_NO_TIMEOUT, &events);
+#ifdef OPEN_ETH_DEBUG
+ printf ("t\n");
+#endif
+
+ /*
+ * Send packets till queue is empty
+ */
+ for (;;)
+ {
+ /*
+ * Get the next mbuf chain to transmit.
+ */
+ IF_DEQUEUE (&ifp->if_snd, m);
+ if (!m)
+ break;
+ sendpacket (ifp, m);
+ }
+ ifp->if_flags &= ~IFF_OACTIVE;
+ }
+}
+
+
+static void
+open_eth_start (struct ifnet *ifp)
+{
+ struct open_eth_softc *sc = ifp->if_softc;
+
+ rtems_bsdnet_event_send (sc->txDaemonTid, START_TRANSMIT_EVENT);
+ ifp->if_flags |= IFF_OACTIVE;
+}
+
+/*
+ * Initialize and start the device
+ */
+static void
+open_eth_init (void *arg)
+{
+ struct open_eth_softc *sc = arg;
+ struct ifnet *ifp = &sc->arpcom.ac_if;
+
+ if (sc->txDaemonTid == 0)
+ {
+
+ /*
+ * Set up OPEN_ETH hardware
+ */
+ open_eth_initialize_hardware (sc);
+
+ /*
+ * Start driver tasks
+ */
+ sc->rxDaemonTid = rtems_bsdnet_newproc ("DCrx", 4096,
+ open_eth_rxDaemon, sc);
+ sc->txDaemonTid = rtems_bsdnet_newproc ("DCtx", 4096,
+ open_eth_txDaemon, sc);
+ }
+
+ /*
+ * Tell the world that we're running.
+ */
+ ifp->if_flags |= IFF_RUNNING;
+
+}
+
+/*
+ * Stop the device
+ */
+static void
+open_eth_stop (struct open_eth_softc *sc)
+{
+ struct ifnet *ifp = &sc->arpcom.ac_if;
+
+ ifp->if_flags &= ~IFF_RUNNING;
+
+ sc->regs->moder = 0; /* RX/TX OFF */
+ sc->regs->moder = OETH_MODER_RST; /* Reset ON */
+ sc->regs->moder = 0; /* Reset OFF */
+}
+
+
+/*
+ * Show interface statistics
+ */
+static void
+open_eth_stats (struct open_eth_softc *sc)
+{
+ printf (" Rx Packets:%-8lu", sc->rxPackets);
+ printf (" Rx Interrupts:%-8lu", sc->rxInterrupts);
+ printf (" Length:%-8lu", sc->rxLengthError);
+ printf (" Non-octet:%-8lu\n", sc->rxNonOctet);
+ printf (" Bad CRC:%-8lu", sc->rxBadCRC);
+ printf (" Overrun:%-8lu", sc->rxOverrun);
+ printf (" Miss:%-8lu", sc->rxMiss);
+ printf (" Collision:%-8lu\n", sc->rxCollision);
+
+ printf (" Tx Interrupts:%-8lu", sc->txInterrupts);
+ printf (" Deferred:%-8lu", sc->txDeferred);
+ printf (" Missed Hearbeat:%-8lu\n", sc->txHeartbeat);
+ printf (" No Carrier:%-8lu", sc->txLostCarrier);
+ printf ("Retransmit Limit:%-8lu", sc->txRetryLimit);
+ printf (" Late Collision:%-8lu\n", sc->txLateCollision);
+ printf (" Underrun:%-8lu", sc->txUnderrun);
+ printf (" Raw output wait:%-8lu\n", sc->txRawWait);
+}
+
+/*
+ * Driver ioctl handler
+ */
+static int
+open_eth_ioctl (struct ifnet *ifp, ioctl_command_t command, caddr_t data)
+{
+ struct open_eth_softc *sc = ifp->if_softc;
+ int error = 0;
+
+ switch (command)
+ {
+ case SIOCGIFADDR:
+ case SIOCSIFADDR:
+ ether_ioctl (ifp, command, data);
+ break;
+
+ case SIOCSIFFLAGS:
+ switch (ifp->if_flags & (IFF_UP | IFF_RUNNING))
+ {
+ case IFF_RUNNING:
+ open_eth_stop (sc);
+ break;
+
+ case IFF_UP:
+ open_eth_init (sc);
+ break;
+
+ case IFF_UP | IFF_RUNNING:
+ open_eth_stop (sc);
+ open_eth_init (sc);
+ break;
+
+ default:
+ break;
+ }
+ break;
+
+ case SIO_RTEMS_SHOW_STATS:
+ open_eth_stats (sc);
+ break;
+
+ /*
+ * FIXME: All sorts of multicast commands need to be added here!
+ */
+ default:
+ error = EINVAL;
+ break;
+ }
+
+ return error;
+}
+
+/*
+ * Attach an OPEN_ETH driver to the system
+ */
+int
+rtems_open_eth_driver_attach (struct rtems_bsdnet_ifconfig *config,
+ open_eth_configuration_t * chip)
+{
+ struct open_eth_softc *sc;
+ struct ifnet *ifp;
+ int mtu;
+ int unitNumber;
+ char *unitName;
+
+ /* parse driver name */
+ if ((unitNumber = rtems_bsdnet_parse_driver_name (config, &unitName)) < 0)
+ return 0;
+
+ sc = &oc;
+ ifp = &sc->arpcom.ac_if;
+ memset (sc, 0, sizeof (*sc));
+
+ if (config->hardware_address)
+ {
+ memcpy (sc->arpcom.ac_enaddr, config->hardware_address,
+ ETHER_ADDR_LEN);
+ }
+ else
+ {
+ memset (sc->arpcom.ac_enaddr, 0x08, ETHER_ADDR_LEN);
+ }
+
+ if (config->mtu)
+ mtu = config->mtu;
+ else
+ mtu = ETHERMTU;
+
+ sc->acceptBroadcast = !config->ignore_broadcast;
+ sc->regs = chip->base_address;
+ sc->vector = chip->vector;
+ sc->txbufs = chip->txd_count;
+ sc->rxbufs = chip->rxd_count;
+ sc->en100MHz = chip->en100MHz;
+
+
+ /*
+ * Set up network interface values
+ */
+ ifp->if_softc = sc;
+ ifp->if_unit = unitNumber;
+ ifp->if_name = unitName;
+ ifp->if_mtu = mtu;
+ ifp->if_init = open_eth_init;
+ ifp->if_ioctl = open_eth_ioctl;
+ ifp->if_start = open_eth_start;
+ ifp->if_output = ether_output;
+ ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX;
+ if (ifp->if_snd.ifq_maxlen == 0)
+ ifp->if_snd.ifq_maxlen = ifqmaxlen;
+
+ /*
+ * Attach the interface
+ */
+ if_attach (ifp);
+ ether_ifattach (ifp);
+
+#ifdef OPEN_ETH_DEBUG
+ printf ("OPEN_ETH : driver has been attached\n");
+#endif
+ return 1;
+};
+
+#endif /* OPENETH_NOT_SUPPORTED */
diff --git a/bsps/shared/net/smc91111.c b/bsps/shared/net/smc91111.c
new file mode 100644
index 0000000000..45c87e8245
--- /dev/null
+++ b/bsps/shared/net/smc91111.c
@@ -0,0 +1,1653 @@
+#define __INSIDE_RTEMS_BSD_TCPIP_STACK__
+
+#include <rtems.h>
+#include <errno.h>
+
+#include <bsp.h>
+
+/*
+ * This driver currently only supports SPARC with the old style
+ * exception processing and the Phytec Phycore MPC5554.
+ * This test keeps it from being compiled on systems which haven't been
+ * tested.
+ *
+ */
+
+#if defined(HAS_SMC91111)
+ #define SMC91111_SUPPORTED
+#endif
+
+#if defined(HAS_SMC91111)
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <inttypes.h>
+#include <rtems/error.h>
+#include <rtems/rtems_bsdnet.h>
+#include <rtems/irq-extension.h>
+
+#include <sys/param.h>
+#include <sys/mbuf.h>
+
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <netinet/if_ether.h>
+
+#define SMC91111_INTERRUPT_EVENT RTEMS_EVENT_1 /* RTEMS event used by interrupt handler to signal driver tasks. This must not be any of the events used by the network task synchronization. */
+#define SMC91111_START_TRANSMIT_EVENT RTEMS_EVENT_2 /* RTEMS event used to start transmit/receive daemon. This must not be the same as INTERRUPT_EVENT. */
+#define SMC91111_TX_WAIT_EVENT RTEMS_EVENT_3 /* event to send when tx buffers become available */
+
+/* Set to perms of:
+ 0 disables all debug output
+ 1 for process debug output
+ 2 for added data IO output: get_reg, put_reg
+ 4 for packet allocation/free output
+ 8 for only startup status, so we can tell we're installed OK
+ 16 dump phy read/write
+ 32 precise register dump
+ 64 dump packets
+*/
+/*#define DEBUG (-1)*/
+/*#define DEBUG (-1 & ~(16))*/
+#define DEBUG (0)
+/*#define DEBUG (1)*/
+
+#include "smc91111config.h"
+#include <libchip/smc91111.h>
+
+#ifdef BSP_FEATURE_IRQ_EXTENSION
+ #include <rtems/irq-extension.h>
+#endif
+
+struct lan91cxx_priv_data smc91111;
+
+int lan91cxx_hardware_init(struct lan91cxx_priv_data *cpd);
+static uint16_t lan91cxx_read_phy(struct lan91cxx_priv_data *cpd,
+ uint8_t phyaddr, uint8_t phyreg);
+static void lan91cxx_write_phy(struct lan91cxx_priv_data *cpd,
+ uint8_t phyaddr, uint8_t phyreg,
+ uint16_t value);
+static void lan91cxx_start(struct ifnet *ifp);
+static void smc91111_start(struct ifnet *ifp);
+static int smc_probe(struct lan91cxx_priv_data *cpd);
+static void smc91111_stop(struct lan91cxx_priv_data *cpd);
+static void smc91111_init(void *arg);
+#ifndef BSP_FEATURE_IRQ_EXTENSION
+static void lan91cxx_finish_sent(struct lan91cxx_priv_data *cpd);
+#endif
+#if 0
+static int lan91cxx_phy_fixed(struct lan91cxx_priv_data *cpd);
+static void lan91cxx_phy_configure(struct lan91cxx_priv_data *cpd);
+#endif
+
+#define min(l,r) ((l) < (r) ? (l) : (r))
+#define max(l,r) ((l) > (r) ? (l) : (r))
+
+#ifndef BSP_FEATURE_IRQ_EXTENSION
+/* \ ------------- Interrupt ------------- \ */
+static void lan91cxx_interrupt_handler(void *arg)
+{
+ struct lan91cxx_priv_data *cpd = arg;
+ unsigned short irq, event;
+ unsigned short oldbase;
+ unsigned short oldpointer;
+ INCR_STAT(cpd, interrupts);
+ DEBUG_FUNCTION();
+
+ HAL_READ_UINT16(cpd->base + (LAN91CXX_BS), oldbase);
+ oldpointer = get_reg(cpd, LAN91CXX_POINTER);
+
+ /* Get the (unmasked) requests */
+ irq = get_reg(cpd, LAN91CXX_INTERRUPT);
+ event = irq & (irq >> 8) & 0xff;
+ if (0 == event)
+ return;
+
+ /*put_reg(cpd, LAN91CXX_INTERRUPT, irq ); */ /* ack interrupts */
+
+ if (event & LAN91CXX_INTERRUPT_ERCV_INT) {
+ db_printf("Early receive interrupt");
+ } else if (event & LAN91CXX_INTERRUPT_EPH_INT) {
+ db_printf("ethernet protocol handler failures");
+ } else if (event & LAN91CXX_INTERRUPT_RX_OVRN_INT) {
+ db_printf("receive overrun");
+ } else if (event & LAN91CXX_INTERRUPT_ALLOC_INT) {
+ db_printf("allocation interrupt");
+ } else {
+
+ if (event & LAN91CXX_INTERRUPT_TX_SET) {
+ db_printf("#*tx irq\n");
+ lan91cxx_finish_sent(cpd);
+ put_reg(cpd, LAN91CXX_INTERRUPT,
+ (irq & 0xff00) | LAN91CXX_INTERRUPT_TX_INT);
+
+ /*rtems_bsdnet_event_send (cpd->txDaemonTid, SMC91111_INTERRUPT_EVENT); */
+ /*put_reg(cpd, LAN91CXX_INTERRUPT, (irq & 0xff00) | LAN91CXX_INTERRUPT_TX_INT); */
+ /*rtems_bsdnet_event_send (cpd->txDaemonTid, SMC91111_TX_WAIT_EVENT); */
+ }
+ if (event & LAN91CXX_INTERRUPT_RCV_INT) {
+ db_printf("#*rx irq\n");
+ rtems_bsdnet_event_send(cpd->rxDaemonTid,
+ SMC91111_INTERRUPT_EVENT);
+ }
+ if (event &
+ ~(LAN91CXX_INTERRUPT_TX_SET | LAN91CXX_INTERRUPT_RCV_INT))
+ db_printf("Unknown interrupt\n");
+ }
+ db_printf("out %s\n", __FUNCTION__);
+
+ put_reg(cpd, LAN91CXX_POINTER, oldpointer);
+ HAL_WRITE_UINT16(cpd->base + (LAN91CXX_BS), oldbase);
+}
+#endif
+
+/* \ ------------- Rx receive ------------- \ */
+
+ /**/
+/* This function is called as a result of the "readpacket()" call.*/
+/* Its job is to actually fetch data for a packet from the hardware once*/
+/* memory buffers have been allocated for the packet. Note that the buffers*/
+/* may come in pieces, using a mbuf list. */
+static void lan91cxx_recv(struct lan91cxx_priv_data *cpd, struct mbuf *m)
+{
+ struct ifnet *ifp = &cpd->arpcom.ac_if;
+ struct ether_header *eh;
+ short mlen = 0, plen;
+ char *start;
+ rxd_t *data = NULL, val;
+#if DEBUG & 64
+ rxd_t lp = 0;
+#else
+ /* start is only read with debug enabled */
+ (void)start;
+#endif
+ struct mbuf *n;
+ dbg_prefix = "<";
+
+ DEBUG_FUNCTION();
+ INCR_STAT(cpd, rx_deliver);
+
+ /* ############ read packet ############ */
+
+ put_reg(cpd, LAN91CXX_POINTER,
+ (LAN91CXX_POINTER_RCV | LAN91CXX_POINTER_READ |
+ LAN91CXX_POINTER_AUTO_INCR));
+ val = get_data(cpd);
+
+ /* packet length (minus header/footer) */
+#ifdef LAN91CXX_32BIT_RX
+ val = CYG_LE32_TO_CPU(val);
+ plen = (val >> 16) - 6;
+#else
+ val = CYG_LE16_TO_CPU(val);
+ plen = get_data(cpd);
+ plen = CYG_LE16_TO_CPU(plen) - 6;
+#endif
+
+ if ( cpd->c111_reva || LAN91CXX_RX_STATUS_IS_ODD(cpd, val) ) /* RevA Odd-bit BUG */
+ plen++;
+
+ for (n = m; n; n = n->m_next) {
+#ifdef LAN91CXX_32BIT_RX
+ if (mlen == 2) {
+#if DEBUG & 64
+ db_printf("Appending to last apacket\n");
+#endif
+
+ val = get_data(cpd);
+ *((unsigned short *)data) = (val >> 16) & 0xffff;
+ plen -= 2;
+ data = (rxd_t *) n->m_data;
+ start = (char *)data;
+ mlen = n->m_len;
+ if ((data) && (mlen > 1)) {
+ *(unsigned short *)data = (val & 0xffff);
+ data = (rxd_t *)((unsigned short *)data + 1);
+ plen -= 2;
+ mlen -= 2;
+ }
+ } else {
+ data = (rxd_t *) n->m_data;
+ start = (char *)data;
+ mlen = n->m_len;
+ }
+#else
+ data = (rxd_t *) n->m_data;
+ start = (char *)data;
+ mlen = n->m_len;
+#endif
+
+ db1_printf("<[packet : mlen 0x%x, plen 0x%x]\n", mlen, plen);
+
+ if (data) {
+ while (mlen >= sizeof(*data)) {
+#ifdef LAN91CXX_32BIT_RX
+ val = get_data(cpd);
+ *(unsigned short *)data = (val >> 16) & 0xffff;
+ data = (rxd_t *)((unsigned short *)data + 1);
+ *(unsigned short *)data = (val & 0xffff);
+ data = (rxd_t *)((unsigned short *)data + 1);
+#else
+ *data++ = get_data(cpd);
+#endif
+ mlen -= sizeof(*data);
+ plen -= sizeof(*data);
+ }
+ } else { /* must actively discard ie. read it from the chip anyway. */
+ while (mlen >= sizeof(*data)) {
+ (void)get_data(cpd);
+ mlen -= sizeof(*data);
+ plen -= sizeof(*data);
+ }
+ }
+
+#if DEBUG & 64
+ lp = 0;
+ while (((int)start) < ((int)data)) {
+ unsigned char a = *(start++);
+ unsigned char b = *(start++);
+ db64_printf("%02x %02x ", a, b);
+ lp += 2;
+ if (lp >= 16) {
+ db64_printf("\n");
+ lp = 0;
+ }
+ }
+ db64_printf(" \n");
+#endif
+ }
+ val = get_data(cpd); /* Read control word (and potential data) unconditionally */
+#ifdef LAN91CXX_32BIT_RX
+ if (plen & 2) {
+ if (data && (mlen>1) ) {
+ *(unsigned short *)data = (val >> 16) & 0xffff;
+ data = (rxd_t *)((unsigned short *)data + 1);
+ val <<= 16;
+ mlen-=2;
+ }
+ }
+ if ( (plen & 1) && data && (mlen>0) )
+ *(unsigned char *)data = val >> 24;
+#else
+ val = CYG_LE16_TO_CPU(val);
+ cp = (unsigned char *)data;
+
+ CYG_ASSERT(val & LAN91CXX_CONTROLBYTE_RX, "Controlbyte is not for Rx");
+ CYG_ASSERT((1 == mlen) == (0 != LAN91CXX_CONTROLBYTE_IS_ODD(cpd, val)),
+ "Controlbyte does not match");
+ if (data && (1 == mlen) && LAN91CXX_CONTROLBYTE_IS_ODD(cpd, val)) {
+ cval = val & 0x00ff; /* last byte contains data */
+ *cp = cval;
+ }
+#endif
+
+ val = get_reg(cpd, LAN91CXX_FIFO_PORTS);
+ if (0x8000 & val) { /* Then the Rx FIFO is empty */
+ db4_printf
+ ("<+Rx packet NOT freed, stat is %x (expected %x)\n",
+ val, cpd->rxpacket);
+ } else {
+ db4_printf("<+Rx packet freed %x (expected %x)\n",
+ 0xff & (val >> 8), cpd->rxpacket);
+ }
+
+ CYG_ASSERT((0xff & (val >> 8)) == cpd->rxpacket,
+ "Unexpected rx packet");
+
+ /* ############ free packet ############ */
+ /* Free packet */
+ put_reg(cpd, LAN91CXX_MMU_COMMAND, LAN91CXX_MMU_remrel_rx_frame);
+
+ dbg_prefix = "";
+
+ /* Remove the mac header. This is different from the NetBSD stack. */
+ eh = mtod(m, struct ether_header *);
+ m->m_data += sizeof(struct ether_header);
+ m->m_len -= sizeof(struct ether_header);
+ m->m_pkthdr.len -= sizeof(struct ether_header);
+
+ ether_input(ifp, eh, m);
+
+}
+
+/* allocate mbuf chain */
+static struct mbuf *smc91111_allocmbufchain(int totlen, struct ifnet *ifp)
+{
+
+ struct mbuf *m, *m0, *newm;
+ int len;
+
+ MGETHDR(m0, M_DONTWAIT, MT_DATA);
+ if (m0 == 0)
+ return (0);
+ m0->m_pkthdr.rcvif = ifp;
+ m0->m_pkthdr.len = totlen;
+ len = MHLEN;
+ m = m0;
+
+ /* This loop goes through and allocates mbufs for all the data we will be copying in. */
+ while (totlen > 0) {
+ if (totlen >= MINCLSIZE) {
+ MCLGET(m, M_DONTWAIT);
+ if ((m->m_flags & M_EXT) == 0)
+ goto bad;
+ len = MCLBYTES;
+ }
+
+ if (m == m0) {
+ caddr_t newdata = (caddr_t)
+ ALIGN(m->m_data +
+ sizeof(struct ether_header)) -
+ sizeof(struct ether_header);
+ len -= newdata - m->m_data;
+ m->m_data = newdata;
+ }
+
+ m->m_len = len = min(totlen, len);
+
+ totlen -= len;
+ if (totlen > 0) {
+ MGET(newm, M_DONTWAIT, MT_DATA);
+ if (newm == 0)
+ goto bad;
+ len = MLEN;
+ m = m->m_next = newm;
+ }
+ }
+ return (m0);
+
+ bad:
+ m_freem(m0);
+ return (0);
+}
+
+static int readpacket(struct lan91cxx_priv_data *cpd)
+{
+ struct mbuf *m;
+ unsigned short stat, complen;
+ struct ifnet *ifp = &cpd->arpcom.ac_if;
+#ifdef LAN91CXX_32BIT_RX
+ uint32_t val;
+#endif
+
+ DEBUG_FUNCTION();
+
+ /* ############ read packet nr ############ */
+ stat = get_reg(cpd, LAN91CXX_FIFO_PORTS);
+ db1_printf("+LAN91CXX_FIFO_PORTS: 0x%04x\n", stat);
+
+ if (0x8000 & stat) {
+ /* Then the Rx FIFO is empty */
+ db4_printf("!RxEvent with empty fifo\n");
+ return 0;
+ }
+
+ INCR_STAT(cpd, rx_count);
+
+ db4_printf("+Rx packet allocated %x (previous %x)\n",
+ 0xff & (stat >> 8), cpd->rxpacket);
+
+ /* There is an Rx Packet ready */
+ cpd->rxpacket = 0xff & (stat >> 8);
+
+ /* ############ read packet header ############ */
+ /* Read status and (word) length */
+ put_reg(cpd, LAN91CXX_POINTER,
+ (LAN91CXX_POINTER_RCV | LAN91CXX_POINTER_READ |
+ LAN91CXX_POINTER_AUTO_INCR | 0x0000));
+#ifdef LAN91CXX_32BIT_RX
+ val = get_data(cpd);
+ val = CYG_LE32_TO_CPU(val);
+ stat = val & 0xffff;
+ complen = ((val >> 16) & 0xffff) - 6; /* minus header/footer words */
+#else
+ stat = get_data(cpd);
+ stat = CYG_LE16_TO_CPU(stat);
+ complen = get_data(cpd);
+ complen = CYG_LE16_TO_CPU(len) - 6; /* minus header/footer words */
+#endif
+
+#ifdef KEEP_STATISTICS
+ if (stat & LAN91CXX_RX_STATUS_ALIGNERR)
+ INCR_STAT(cpd, rx_align_errors);
+ /*if ( stat & LAN91CXX_RX_STATUS_BCAST ) INCR_STAT( ); */
+ if (stat & LAN91CXX_RX_STATUS_BADCRC)
+ INCR_STAT(cpd, rx_crc_errors);
+ if (stat & LAN91CXX_RX_STATUS_TOOLONG)
+ INCR_STAT(cpd, rx_too_long_frames);
+ if (stat & LAN91CXX_RX_STATUS_TOOSHORT)
+ INCR_STAT(cpd, rx_short_frames);
+ /*if ( stat & LAN91CXX_RX_STATUS_MCAST ) INCR_STAT( ); */
+#endif /* KEEP_STATISTICS */
+
+ if ((stat & LAN91CXX_RX_STATUS_BAD) == 0) {
+ INCR_STAT(cpd, rx_good);
+ /* Then it's OK */
+
+ if (cpd->c111_reva || LAN91CXX_RX_STATUS_IS_ODD(cpd, stat)) /* RevA Odd-bit BUG */
+ complen++;
+
+#if DEBUG & 1
+ db_printf("good rx - stat: 0x%04x, len: 0x%04x\n", stat,
+ complen);
+#endif
+ /* Check for bogusly short packets; can happen in promisc mode: */
+ /* Asserted against and checked by upper layer driver. */
+ if (complen > sizeof(struct ether_header)) {
+ /* then it is acceptable; offer the data to the network stack */
+
+ complen = ((complen + 3) & ~3);
+
+ m = smc91111_allocmbufchain(complen, ifp);
+ {
+ struct mbuf *n = m;
+ db_printf("mbuf-chain:");
+ while (n) {
+ db_printf("[%" PRIxPTR ":%x]",
+ mtod(n, uintptr_t),
+ (unsigned int)(n->m_len));
+ n = n->m_next;
+ }
+ db_printf("\n");
+ }
+
+ if (m) {
+ /* fetch packet data into mbuf chain */
+ lan91cxx_recv(cpd, m);
+ return 1;
+ }
+ }
+ /*(sc->funs->eth_drv->recv)(sc, len); */
+ }
+
+ /* Not OK for one reason or another... */
+ db1_printf("!bad rx: stat: 0x%04x, len: 0x%04x\n", stat, complen);
+
+ /* ############ free packet ############ */
+ /* Free packet */
+ put_reg(cpd, LAN91CXX_MMU_COMMAND, LAN91CXX_MMU_remrel_rx_frame);
+ return 1;
+
+}
+
+static void smc91111_rxDaemon(void *arg)
+{
+ struct lan91cxx_priv_data *cpd = arg;
+ rtems_event_set events;
+ DEBUG_FUNCTION();
+
+ for (;;) {
+ rtems_bsdnet_event_receive(INTERRUPT_EVENT,
+ RTEMS_WAIT | RTEMS_EVENT_ANY,
+ RTEMS_NO_TIMEOUT, &events);
+
+ /* read until read fifo is empty */
+ while (!(get_reg(cpd, LAN91CXX_FIFO_PORTS) & 0x8000)) {
+ readpacket(cpd);
+ }
+ }
+}
+
+/* \ ------------- Tx send ------------- \ */
+
+static void sendpacket(struct ifnet *ifp, struct mbuf *m)
+{
+ struct lan91cxx_priv_data *cpd = ifp->if_softc;
+ int i, len, plen, tcr, odd;
+ struct mbuf *n = m;
+ unsigned short *sdata = NULL;
+ unsigned short ints, control;
+ uint16_t packet, status;
+ dbg_prefix = ">";
+ DEBUG_FUNCTION();
+
+ cpd->txbusy = 1;
+
+ /* Worry about the TX engine stopping. */
+ tcr = get_reg(cpd, LAN91CXX_TCR);
+ if (0 == (LAN91CXX_TCR_TXENA & tcr)) {
+ db1_printf("> ENGINE RESTART: tcr %x\n", tcr);
+ tcr |= LAN91CXX_TCR_TXENA;
+ put_reg(cpd, LAN91CXX_TCR, tcr);
+ }
+
+ /* ############ packet allocation ############ */
+
+ /* Find packet length */
+ plen = 0;
+ while (n) {
+ plen += n->m_len;
+ n = n->m_next;
+ }
+
+ /* Alloc new TX packet */
+ do {
+ put_reg(cpd, LAN91CXX_MMU_COMMAND,
+ LAN91CXX_MMU_alloc_for_tx | ((plen >> 8) & 0x07));
+
+ i = 1024 * 1024;
+ do {
+ status = get_reg(cpd, LAN91CXX_INTERRUPT);
+ } while (0 == (status & LAN91CXX_INTERRUPT_ALLOC_INT)
+ && (--i > 0));
+ if (i)
+ packet = get_reg(cpd, LAN91CXX_PNR);
+ else
+ packet = 0xffff;
+ db1_printf(">+allocated packet %04x\n", packet);
+
+ packet = packet >> 8;
+ if (packet & 0x80) {
+ /* Hm.. Isn't this a dead end? */
+ db1_printf("Allocation failed! Retrying...\n");
+ continue;
+ }
+ } while (0);
+
+ db4_printf(">+Tx packet allocated %x (previous %x)\n",
+ packet, cpd->txpacket);
+
+ cpd->txpacket = packet;
+
+ /* ############ assemble packet data ############ */
+
+ /* prepare send */
+ put_reg(cpd, LAN91CXX_PNR, packet);
+ /* Note: Check FIFO state here before continuing? */
+ put_reg(cpd, LAN91CXX_POINTER, LAN91CXX_POINTER_AUTO_INCR | 0x0000);
+ /* Pointer is now set, and the proper bank is selected for */
+ /* data writes. */
+
+ /* Prepare header: */
+ put_data(cpd, CYG_CPU_TO_LE16(0)); /* reserve space for status word */
+ /* packet length (includes status, byte-count and control shorts) */
+ put_data(cpd, CYG_CPU_TO_LE16(0x7FE & (plen + 6))); /* Always even, always < 15xx(dec) */
+
+ /* Put data into buffer */
+ odd = 0;
+ n = m;
+ while (n) {
+ sdata = (unsigned short *)n->m_data;
+ len = n->m_len;
+
+ CYG_ASSERT(sdata, "!No sg data pointer here");
+
+ /* start on an odd offset?
+ * If last byte also (1byte mbuf with different pointer should not occur)
+ * let following code handle it
+ */
+ if ( ((unsigned int)sdata & 1) && (len>1) ){
+ put_data8(cpd,*(unsigned char *)sdata);
+ sdata = (unsigned short *)((unsigned int)sdata + 1);
+ odd = ~odd;
+ len--;
+ }
+
+ /* speed up copying a bit, never copy last word */
+ while(len >= 17){
+ put_data(cpd, *(sdata));
+ put_data(cpd, *(sdata+1));
+ put_data(cpd, *(sdata+2));
+ put_data(cpd, *(sdata+3));
+ put_data(cpd, *(sdata+4));
+ put_data(cpd, *(sdata+5));
+ put_data(cpd, *(sdata+6));
+ put_data(cpd, *(sdata+7));
+ sdata += 8;
+ len -= 16;
+ }
+
+ /* copy word wise, skip last word */
+ while (len >= 3) {
+ put_data(cpd, *sdata++);
+ len -= sizeof(*sdata);
+ }
+
+ /* one or two bytes left to put into fifo */
+ if ( len > 1 ){
+ /* the last 2bytes */
+ if ( !odd || n->m_next ){
+ put_data(cpd, *sdata++);
+ len -= sizeof(*sdata);
+ }else{
+ /* write next byte, mark that we are not at an odd offset any more,
+ * remaining byte will be written outside while together with control byte.
+ */
+ put_data8(cpd,*(unsigned char *)sdata);
+ sdata = (unsigned short *)((unsigned int)sdata + 1);
+ odd = 0;
+ len--;
+ /*break;*/
+ }
+ }else if ( (len>0) && (n->m_next) ){
+ /* one byte left to write, and more bytes is comming in next mbuf */
+ put_data8(cpd,*(unsigned char *)sdata);
+ odd = ~odd;
+ }
+
+ n = n->m_next;
+ }
+
+ /* Lay down the control short unconditionally at the end. */
+ /* (or it might use random memory contents) */
+ control = 0;
+ if ( len > 0 ){
+ if ( !odd ) {
+ /* Need to set ODD flag and insert the data */
+ unsigned char onebyte = *(unsigned char *)sdata;
+ control = onebyte;
+ control |= LAN91CXX_CONTROLBYTE_ODD;
+ }else{
+ put_data8(cpd,*(unsigned char *)sdata);
+ }
+ }
+ control |= LAN91CXX_CONTROLBYTE_CRC; /* Just in case... */
+ put_data(cpd, CYG_CPU_TO_LE16(control));
+
+ m_freem(m);
+ CYG_ASSERT(sdata, "!No sg data pointer outside");
+
+ /* ############ start transmit ############ */
+
+ /* Ack TX empty int and unmask it. */
+ ints = get_reg(cpd, LAN91CXX_INTERRUPT) & 0xff00;
+ put_reg(cpd, LAN91CXX_INTERRUPT,
+ ints | LAN91CXX_INTERRUPT_TX_EMPTY_INT);
+ put_reg(cpd, LAN91CXX_INTERRUPT, ints | LAN91CXX_INTERRUPT_TX_INT_M); /* notify on error only (Autorelease) */
+
+ /* Enqueue the packet */
+ put_reg(cpd, LAN91CXX_MMU_COMMAND, LAN91CXX_MMU_enq_packet);
+
+ ints = get_reg(cpd, LAN91CXX_INTERRUPT);
+ db1_printf(">END: ints at TX: %04x\n", ints);
+ dbg_prefix = "";
+}
+
+static void smc91111_txDaemon(void *arg)
+{
+ struct lan91cxx_priv_data *cpd = arg;
+ struct ifnet *ifp = &cpd->arpcom.ac_if;
+ struct mbuf *m;
+ rtems_event_set events;
+ DEBUG_FUNCTION();
+
+ for (;;) {
+ /*
+ * Wait for packet
+ */
+
+ rtems_bsdnet_event_receive
+ (SMC91111_START_TRANSMIT_EVENT,
+ RTEMS_EVENT_ANY | RTEMS_WAIT, RTEMS_NO_TIMEOUT, &events);
+
+ /*IF_DEQUEUE (&ifp->if_snd, m);
+ if (m) {
+ sendpacket (ifp, m);
+ } */
+
+ for (;;) {
+ IF_DEQUEUE(&ifp->if_snd, m);
+ if (!m)
+ break;
+ sendpacket(ifp, m);
+ }
+ ifp->if_flags &= ~IFF_OACTIVE;
+
+ }
+
+}
+
+/* start transmit */
+static void smc91111_start(struct ifnet *ifp)
+{
+ struct lan91cxx_priv_data *cpd = ifp->if_softc;
+
+ if ((ifp->if_flags & (IFF_RUNNING | IFF_OACTIVE)) != IFF_RUNNING)
+ return;
+
+ rtems_bsdnet_event_send(cpd->txDaemonTid, START_TRANSMIT_EVENT);
+ ifp->if_flags |= IFF_OACTIVE;
+
+}
+
+#ifndef BSP_FEATURE_IRQ_EXTENSION
+/* called after a tx error interrupt, freet the packet */
+static void lan91cxx_finish_sent(struct lan91cxx_priv_data *cpd)
+{
+ unsigned short packet, tcr;
+ int saved_packet;
+
+ DEBUG_FUNCTION();
+
+ INCR_STAT(cpd, tx_complete);
+
+ saved_packet = get_reg(cpd, LAN91CXX_PNR);
+
+ /* Ack and mask TX interrupt set */
+ /*ints = get_reg(cpd, LAN91CXX_INTERRUPT) & 0xff00;
+ ints |= LAN91CXX_INTERRUPT_TX_SET_ACK;
+ ints &= ~LAN91CXX_INTERRUPT_TX_SET_M;
+ put_reg(cpd, LAN91CXX_INTERRUPT, ints); */
+
+ /* Get number of completed packet and read the status word */
+ packet = get_reg(cpd, LAN91CXX_FIFO_PORTS);
+ db1_printf("%s:START: fifo %04x \n", __FUNCTION__, packet);
+
+ {
+ unsigned short reg;
+
+ reg = get_reg(cpd, LAN91CXX_EPH_STATUS);
+
+ /* Covering each bit in turn... */
+ if (reg & LAN91CXX_STATUS_TX_UNRN)
+ INCR_STAT(cpd, tx_underrun);
+ if (reg & LAN91CXX_STATUS_LOST_CARR)
+ INCR_STAT(cpd, tx_carrier_loss);
+ if (reg & LAN91CXX_STATUS_LATCOL)
+ INCR_STAT(cpd, tx_late_collisions);
+ if (reg & LAN91CXX_STATUS_TX_DEFR)
+ INCR_STAT(cpd, tx_deferred);
+ if (reg & LAN91CXX_STATUS_SQET)
+ INCR_STAT(cpd, tx_sqetesterrors);
+ if (reg & LAN91CXX_STATUS_16COL)
+ INCR_STAT(cpd, tx_max_collisions);
+ if (reg & LAN91CXX_STATUS_MUL_COL)
+ INCR_STAT(cpd, tx_mult_collisions);
+ if (reg & LAN91CXX_STATUS_SNGL_COL)
+ INCR_STAT(cpd, tx_single_collisions);
+ if (reg & LAN91CXX_STATUS_TX_SUC)
+ INCR_STAT(cpd, tx_good);
+
+ cpd->stats.tx_total_collisions =
+ cpd->stats.tx_late_collisions +
+ cpd->stats.tx_max_collisions +
+ cpd->stats.tx_mult_collisions +
+ cpd->stats.tx_single_collisions;
+
+ /* We do not need to look in the Counter Register (LAN91CXX_COUNTER)
+ because it just mimics the info we already have above. */
+ }
+
+ /* We do not really care about Tx failure. Ethernet is not a reliable
+ medium. But we do care about the TX engine stopping. */
+ tcr = get_reg(cpd, LAN91CXX_TCR);
+ if (0 == (LAN91CXX_TCR_TXENA & tcr)) {
+ db1_printf("%s: ENGINE RESTART: tcr %x \n", __FUNCTION__, tcr);
+ tcr |= LAN91CXX_TCR_TXENA;
+ put_reg(cpd, LAN91CXX_TCR, tcr);
+ }
+
+ packet &= 0xff;
+
+ /* and then free the packet */
+ put_reg(cpd, LAN91CXX_PNR, cpd->txpacket);
+ put_reg(cpd, LAN91CXX_MMU_COMMAND, LAN91CXX_MMU_rel_packet);
+
+ while (get_reg(cpd, LAN91CXX_MMU_COMMAND) & LAN91CXX_MMU_COMMAND_BUSY) ;
+ /* Don't change Packet Number Reg until busy bit is cleared */
+ /* Per LAN91C111 Spec, Page 50 */
+ put_reg(cpd, LAN91CXX_PNR, saved_packet);
+
+}
+#endif
+
+/* \ ------------- Helpers ------------- \ */
+
+/*
+ * Show interface statistics
+ */
+static void smc91111_stats(struct lan91cxx_priv_data *priv)
+{
+ printf("tx_good :%-8d\n", priv->stats.tx_good);
+ printf("tx_max_collisions :%-8d\n", priv->stats.tx_max_collisions);
+ printf("tx_late_collisions :%-8d\n", priv->stats.tx_late_collisions);
+ printf("tx_underrun :%-8d\n", priv->stats.tx_underrun);
+ printf("tx_carrier_loss :%-8d\n", priv->stats.tx_carrier_loss);
+ printf("tx_deferred :%-8d\n", priv->stats.tx_deferred);
+ printf("tx_sqetesterrors :%-8d\n", priv->stats.tx_sqetesterrors);
+ printf("tx_single_collisions:%-8d\n", priv->stats.tx_single_collisions);
+ printf("tx_mult_collisions :%-8d\n", priv->stats.tx_mult_collisions);
+ printf("tx_total_collisions :%-8d\n", priv->stats.tx_total_collisions);
+ printf("rx_good :%-8d\n", priv->stats.rx_good);
+ printf("rx_crc_errors :%-8d\n", priv->stats.rx_crc_errors);
+ printf("rx_align_errors :%-8d\n", priv->stats.rx_align_errors);
+ printf("rx_resource_errors :%-8d\n", priv->stats.rx_resource_errors);
+ printf("rx_overrun_errors :%-8d\n", priv->stats.rx_overrun_errors);
+ printf("rx_collisions :%-8d\n", priv->stats.rx_collisions);
+ printf("rx_short_frames :%-8d\n", priv->stats.rx_short_frames);
+ printf("rx_too_long_frames :%-8d\n", priv->stats.rx_too_long_frames);
+ printf("rx_symbol_errors :%-8d\n", priv->stats.rx_symbol_errors);
+ printf("interrupts :%-8d\n", priv->stats.interrupts);
+ printf("rx_count :%-8d\n", priv->stats.rx_count);
+ printf("rx_deliver :%-8d\n", priv->stats.rx_deliver);
+ printf("rx_resource :%-8d\n", priv->stats.rx_resource);
+ printf("rx_restart :%-8d\n", priv->stats.rx_restart);
+ printf("tx_count :%-8d\n", priv->stats.tx_count);
+ printf("tx_complete :%-8d\n", priv->stats.tx_complete);
+ printf("tx_dropped :%-8d\n", priv->stats.tx_dropped);
+}
+
+/*
+ * Driver ioctl handler
+ */
+static int smc91111_ioctl(struct ifnet *ifp, ioctl_command_t command, caddr_t data)
+{
+ struct lan91cxx_priv_data *cpd = ifp->if_softc;
+ int error = 0;
+ DEBUG_FUNCTION();
+
+ switch (command) {
+ case SIOCGIFADDR:
+ case SIOCSIFADDR:
+ db_printf("SIOCSIFADDR\n");
+ ether_ioctl(ifp, command, data);
+ break;
+
+ case SIOCSIFFLAGS:
+ db_printf("SIOCSIFFLAGS\n");
+ switch (ifp->if_flags & (IFF_UP | IFF_RUNNING)) {
+ case IFF_RUNNING:
+ smc91111_stop(cpd);
+ break;
+
+ case IFF_UP:
+ smc91111_init(cpd);
+ break;
+
+ case IFF_UP | IFF_RUNNING:
+ smc91111_stop(cpd);
+ smc91111_init(cpd);
+ break;
+
+ default:
+ break;
+ }
+ break;
+
+ case SIO_RTEMS_SHOW_STATS:
+ db_printf("SIO_RTEMS_SHOW_STATS\n");
+ smc91111_stats(cpd);
+ break;
+
+ /*
+ * FIXME: All sorts of multicast commands need to be added here!
+ */
+ default:
+ error = EINVAL;
+ break;
+ }
+
+ return error;
+}
+
+/*
+ * Attach an SMC91111 driver to the system
+ */
+int _rtems_smc91111_driver_attach (struct rtems_bsdnet_ifconfig *config,
+ struct scmv91111_configuration * chip)
+{
+ struct ifnet *ifp;
+ struct lan91cxx_priv_data *cpd;
+ int unitNumber;
+ char *unitName;
+ int mtu;
+ DEBUG_FUNCTION();
+
+ /* parse driver name */
+ if ((unitNumber = rtems_bsdnet_parse_driver_name(config, &unitName)) < 0) {
+ db_printf("Unitnumber < 0: %d\n", unitNumber);
+ return 0;
+ }
+
+ db_printf("Unitnumber: %d, baseaddr: 0x%p\n", unitNumber, chip->baseaddr);
+
+ cpd = &smc91111;
+ ifp = &cpd->arpcom.ac_if;
+ memset(cpd, 0, sizeof(*cpd));
+
+ cpd->config = *chip;
+ cpd->base = chip->baseaddr;
+
+ if (smc_probe(cpd)) {
+ return 0;
+ }
+
+ if (config->hardware_address) {
+ memcpy(cpd->arpcom.ac_enaddr, config->hardware_address, ETHER_ADDR_LEN);
+ } else {
+#ifdef SMC91111_ENADDR_IS_SETUP
+ /* The address was put in the chip at reset time. Retrieve it. */
+ int i;
+ for (i = 0; i < sizeof(cpd->enaddr); i += 2) {
+ unsigned short r = get_reg(cpd, LAN91CXX_IA01 + i / 2);
+ cpd->arpcom.ac_enaddr[i] = r;
+ cpd->arpcom.ac_enaddr[i+1] = r >> 8;
+ }
+#else
+ /* dummy default address */
+ cpd->arpcom.ac_enaddr[0] = 0x12;
+ cpd->arpcom.ac_enaddr[1] = 0x13;
+ cpd->arpcom.ac_enaddr[2] = 0x14;
+ cpd->arpcom.ac_enaddr[3] = 0x15;
+ cpd->arpcom.ac_enaddr[4] = 0x16;
+ cpd->arpcom.ac_enaddr[5] = 0x17;
+#endif
+ }
+
+ cpd->enaddr[0] = cpd->arpcom.ac_enaddr[0];
+ cpd->enaddr[1] = cpd->arpcom.ac_enaddr[1];
+ cpd->enaddr[2] = cpd->arpcom.ac_enaddr[2];
+ cpd->enaddr[3] = cpd->arpcom.ac_enaddr[3];
+ cpd->enaddr[4] = cpd->arpcom.ac_enaddr[4];
+ cpd->enaddr[5] = cpd->arpcom.ac_enaddr[5];
+ cpd->rpc_cur_mode =
+ LAN91CXX_RPCR_LEDA_RX | LAN91CXX_RPCR_LEDB_LINK |
+ LAN91CXX_RPCR_ANEG;
+
+ if (config->mtu)
+ mtu = config->mtu;
+ else
+ mtu = ETHERMTU;
+
+ /*
+ * Set up network interface values
+ */
+ ifp->if_softc = cpd;
+ ifp->if_unit = unitNumber;
+ ifp->if_name = unitName;
+ ifp->if_mtu = mtu;
+ ifp->if_init = smc91111_init;
+ ifp->if_ioctl = smc91111_ioctl;
+ ifp->if_start = smc91111_start;
+ ifp->if_output = ether_output;
+ ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX;
+ if (ifp->if_snd.ifq_maxlen == 0)
+ ifp->if_snd.ifq_maxlen = ifqmaxlen;
+
+ /*
+ * Attach the interface
+ */
+ if_attach(ifp);
+ ether_ifattach(ifp);
+
+#if DEBUG
+ printf("SMC91111 : driver has been attached\n");
+#endif
+
+ return 1;
+};
+
+/* \ ------------- Initialization ------------- \ */
+
+/*
+ * Initialize and start the device
+ */
+static void smc91111_init(void *arg)
+{
+ struct lan91cxx_priv_data *cpd = arg;
+ struct ifnet *ifp = &cpd->arpcom.ac_if;
+ DEBUG_FUNCTION();
+
+ if (cpd->txDaemonTid == 0) {
+
+ lan91cxx_hardware_init(cpd);
+ lan91cxx_start(ifp);
+
+ cpd->rxDaemonTid = rtems_bsdnet_newproc("DCrx", 4096,
+ smc91111_rxDaemon, cpd);
+ cpd->txDaemonTid =
+ rtems_bsdnet_newproc("DCtx", 4096, smc91111_txDaemon, cpd);
+ } else {
+ lan91cxx_start(ifp);
+ }
+
+ /*
+ * Tell the world that we're running.
+ */
+ ifp->if_flags |= IFF_RUNNING;
+}
+
+/*
+ * Stop the device
+ */
+static void smc91111_stop(struct lan91cxx_priv_data *cpd)
+{
+ struct ifnet *ifp = &cpd->arpcom.ac_if;
+ DEBUG_FUNCTION();
+
+ ifp->if_flags &= ~IFF_RUNNING;
+
+ /* Reset chip */
+ put_reg(cpd, LAN91CXX_RCR, LAN91CXX_RCR_SOFT_RST);
+ put_reg(cpd, LAN91CXX_RCR, 0);
+ cpd->txbusy = cpd->within_send = 0;
+
+}
+
+int lan91cxx_hardware_init(struct lan91cxx_priv_data *cpd)
+{
+ unsigned short val;
+ int i;
+
+ DEBUG_FUNCTION();
+
+ cpd->txbusy = cpd->within_send = 0;
+
+ /* install interrupt vector */
+#ifdef BSP_FEATURE_IRQ_EXTENSION
+ {
+ rtems_status_code sc = RTEMS_SUCCESSFUL;
+
+ sc = rtems_interrupt_handler_install(
+ cpd->config.vector,
+ cpd->config.info,
+ cpd->config.options,
+ cpd->config.interrupt_wrapper,
+ cpd
+ );
+ if (sc != RTEMS_SUCCESSFUL) {
+ printf("rtems_interrupt_handler_install returned %d.\n", sc);
+ return 0;
+ }
+ }
+#else
+ {
+ int rc;
+
+ db_printf("Install lan91cxx isr at vec/irq %" PRIu32 "\n", cpd->config.vector);
+ rc = rtems_interrupt_handler_install(cpd->config.vector, "smc91cxx",
+ RTEMS_INTERRUPT_SHARED, lan91cxx_interrupt_handler, cpd);
+ if (rc != RTEMS_SUCCESSFUL)
+ return 0;
+ }
+#endif
+
+ /* Reset chip */
+ put_reg(cpd, LAN91CXX_RCR, LAN91CXX_RCR_SOFT_RST);
+ put_reg(cpd, LAN91CXX_RCR, 0);
+ HAL_DELAY_US(100000);
+ put_reg(cpd, LAN91CXX_CONFIG, 0x9000);
+ put_reg(cpd, LAN91CXX_RCR, 0);
+ put_reg(cpd, LAN91CXX_TCR, 0);
+ put_reg(cpd, LAN91CXX_MMU_COMMAND, LAN91CXX_MMU_reset_mmu);
+
+ val = get_reg(cpd, LAN91CXX_EPH_STATUS);
+ /* probe chip by reading the signature in BS register */
+ val = get_banksel(cpd);
+ db9_printf("LAN91CXX - supposed BankReg @ %x = %04x\n",
+ (unsigned int)(cpd->base + LAN91CXX_BS), val);
+
+ if ((0xff00 & val) != 0x3300) {
+ printf("No 91Cxx signature");
+ printf("smsc_lan91cxx_init: No 91Cxx signature found\n");
+ return 0;
+ }
+
+ val = get_reg(cpd, LAN91CXX_REVISION);
+
+ db9_printf("LAN91CXX - type: %01x, rev: %01x\n",
+ (val >> 4) & 0xf, val & 0xf);
+
+ /* Set RevA flag for LAN91C111 so we can cope with the odd-bit bug. */
+ cpd->c111_reva = (val == 0x3390); /* 90=A, 91=B, 92=C */
+
+ /* The controller may provide a function used to set up the ESA */
+ if (cpd->config_enaddr)
+ (*cpd->config_enaddr) (cpd);
+
+ db9_printf("LAN91CXX - status: %04x\n", val);
+ /* Use statically configured ESA from the private data */
+ db9_printf
+ ("LAN91CXX - static ESA: %02x:%02x:%02x:%02x:%02x:%02x\n",
+ cpd->enaddr[0], cpd->enaddr[1], cpd->enaddr[2],
+ cpd->enaddr[3], cpd->enaddr[4], cpd->enaddr[5]);
+ /* Set up hardware address */
+ for (i = 0; i < sizeof(cpd->enaddr); i += 2)
+ put_reg(cpd, LAN91CXX_IA01 + i / 2,
+ cpd->enaddr[i] | (cpd->enaddr[i + 1] << 8));
+
+ return 1;
+}
+
+/*
+ This function is called to "start up" the interface. It may be called
+ multiple times, even when the hardware is already running. It will be
+ called whenever something "hardware oriented" changes and should leave
+ the hardware ready to send/receive packets.
+*/
+static void lan91cxx_start(struct ifnet *ifp)
+{
+ struct lan91cxx_priv_data *cpd = ifp->if_softc;
+
+ uint16_t intr;
+ uint16_t phy_ctl;
+ int delay;
+ DEBUG_FUNCTION();
+
+ HAL_DELAY_US(100000);
+
+ /* 91C111 Errata. Internal PHY comes up disabled. Must enable here. */
+ phy_ctl = lan91cxx_read_phy(cpd, 0, LAN91CXX_PHY_CTRL);
+ phy_ctl &= ~LAN91CXX_PHY_CTRL_MII_DIS;
+ lan91cxx_write_phy(cpd, 0, LAN91CXX_PHY_CTRL, phy_ctl);
+
+ /* Start auto-negotiation */
+ put_reg(cpd, LAN91CXX_RPCR,
+ LAN91CXX_RPCR_LEDA_RX | LAN91CXX_RPCR_LEDB_LINK |
+ LAN91CXX_RPCR_ANEG);
+ cpd->rpc_cur_mode =
+ LAN91CXX_RPCR_LEDA_RX | LAN91CXX_RPCR_LEDB_LINK |
+ LAN91CXX_RPCR_ANEG;
+
+ /* wait for auto-negotiation to finish. */
+ /* give it ~5 seconds before giving up (no cable?) */
+ delay = 50;
+ while (!(lan91cxx_read_phy(cpd, 0, LAN91CXX_PHY_STAT) & 0x20)) {
+ if (--delay <= 0) {
+ printf("Timeout autonegotiation\n");
+ break;
+ }
+ HAL_DELAY_US(100000);
+ }
+
+ put_reg(cpd, LAN91CXX_MMU_COMMAND, LAN91CXX_MMU_reset_mmu);
+
+ put_reg(cpd, LAN91CXX_INTERRUPT, 0); /* disable interrupts */
+ intr = get_reg(cpd, LAN91CXX_INTERRUPT);
+ put_reg(cpd, LAN91CXX_INTERRUPT, intr & /* ack old interrupts */
+ (LAN91CXX_INTERRUPT_TX_INT |
+ LAN91CXX_INTERRUPT_TX_EMPTY_INT |
+ LAN91CXX_INTERRUPT_RX_OVRN_INT | LAN91CXX_INTERRUPT_ERCV_INT));
+ put_reg(cpd, LAN91CXX_RCR,
+ LAN91CXX_RCR_STRIP_CRC | LAN91CXX_RCR_RXEN |
+ LAN91CXX_RCR_ALMUL);
+ put_reg(cpd, LAN91CXX_TCR, LAN91CXX_TCR_TXENA | LAN91CXX_TCR_PAD_EN);
+ put_reg(cpd, LAN91CXX_CONTROL, LAN91CXX_CONTROL_AUTO_RELEASE); /* */
+ put_reg(cpd, LAN91CXX_INTERRUPT, /* enable interrupts */
+ LAN91CXX_INTERRUPT_RCV_INT_M);
+
+ if ((0
+#ifdef ETH_DRV_FLAGS_PROMISC_MODE
+ != (flags & ETH_DRV_FLAGS_PROMISC_MODE)
+#endif
+ ) || (ifp->if_flags & IFF_PROMISC)
+ ) {
+ /* Then we select promiscuous mode. */
+ unsigned short rcr;
+ rcr = get_reg(cpd, LAN91CXX_RCR);
+ rcr |= LAN91CXX_RCR_PRMS;
+ put_reg(cpd, LAN91CXX_RCR, rcr);
+ }
+}
+
+/* \ ------------- Probe ------------- \ */
+
+static const char *chip_ids[15] = {
+ NULL, NULL, NULL,
+ /* 3 */ "SMC91C90/91C92",
+ /* 4 */ "SMC91C94",
+ /* 5 */ "SMC91C95",
+ /* 6 */ "SMC91C96",
+ /* 7 */ "SMC91C100",
+ /* 8 */ "SMC91C100FD",
+ /* 9 */ "SMC91C11xFD",
+ NULL, NULL,
+ NULL, NULL, NULL
+};
+
+static int smc_probe(struct lan91cxx_priv_data *cpd)
+{
+ unsigned short bank;
+ unsigned short revision_register;
+
+ DEBUG_FUNCTION();
+
+ /* First, see if the high byte is 0x33 */
+ HAL_READ_UINT16(cpd->base + (LAN91CXX_BS), bank);
+ bank = CYG_LE16_TO_CPU(bank);
+ if ((bank & 0xFF00) != 0x3300) {
+ db_printf("<1>Smc probe bank check 1 failed.\n");
+ return -ENODEV;
+ }
+ /* The above MIGHT indicate a device, but I need to write to further
+ test this. */
+ HAL_WRITE_UINT16(cpd->base + (LAN91CXX_BS), CYG_CPU_TO_LE16(0 >> 3));
+ HAL_READ_UINT16(cpd->base + (LAN91CXX_BS), bank);
+ bank = CYG_LE16_TO_CPU(bank);
+ if ((bank & 0xFF00) != 0x3300) {
+ db_printf("<1>Smc probe bank check 2 failed.\n");
+ return -ENODEV;
+ }
+#if SMC_DEBUG > 3
+ {
+ unsigned short bank16, bank16_0, bank16_1;
+ HAL_READ_UINT16(cpd->base + (LAN91CXX_BS), bank16);
+ bank = CYG_LE16_TO_CPU(bank);
+ HAL_READ_UINT8(cpd->base + (LAN91CXX_BS), bank16_0);
+ HAL_READ_UINT8(cpd->base + (LAN91CXX_BS + 1), bank16_1);
+
+ db_printf
+ ("smc_probe:Bank read as a 16 bit value:0x%04x\n", bank16);
+ db_printf
+ ("smc_probe:Bank read as an 8 bit value:0x%02x\n",
+ bank16_0);
+ db_printf
+ ("smc_probe:Bank + 1 read as an 8 bit value:0x%02x\n",
+ bank16_1);
+ }
+#endif
+
+ /* check if the revision register is something that I recognize.
+ These might need to be added to later, as future revisions
+ could be added. */
+ revision_register = get_reg(cpd, LAN91CXX_REVISION);
+ if (!chip_ids[(revision_register >> 4) & 0xF]) {
+ /* I don't recognize this chip, so... */
+ db_printf
+ ("smc_probe: IO %" PRIxPTR ": Unrecognized revision register:"
+ " %x, Contact author. \n", (uintptr_t)cpd->base,
+ revision_register);
+
+ return -ENODEV;
+ }
+ db_printf("LAN91CXX(0x%x) - type: %s, rev: %01x\n",
+ revision_register,
+ chip_ids[(revision_register >> 4) & 0xF],
+ revision_register & 0xf);
+
+ /* Set RevA flag for LAN91C111 so we can cope with the odd-bit bug. */
+ if (revision_register == 0x3390) {
+ db_printf("!Revision A\n");
+ }
+
+ return 0;
+}
+
+#if 0
+/* \ ------------- PHY read/write ------------- \ */
+/*Sets the PHY to a configuration as determined by the user*/
+static int lan91cxx_phy_fixed(struct lan91cxx_priv_data *cpd)
+{
+ int my_fixed_caps;
+ int cfg1;
+
+ DEBUG_FUNCTION();
+ db4_printf("lan91cxx_phy_fixed: full duplex: %d, speed: %d\n",
+ cpd->config.ctl_rfduplx, cpd->config.ctl_rspeed);
+
+ /* Enter Link Disable state */
+ cfg1 = lan91cxx_read_phy(cpd, 0, LAN91CXX_PHY_CONFIG1);
+ cfg1 |= PHY_CFG1_LNKDIS;
+ lan91cxx_write_phy(cpd, 0, LAN91CXX_PHY_CONFIG1, cfg1);
+
+ /* Set our fixed capabilities, Disable auto-negotiation */
+ my_fixed_caps = 0;
+
+ if (cpd->config.ctl_rfduplx)
+ my_fixed_caps |= LAN91CXX_PHY_CTRL_DPLX;
+
+ if (cpd->config.ctl_rspeed == 100)
+ my_fixed_caps |= LAN91CXX_PHY_CTRL_SPEED;
+
+ /* Write capabilities to the phy control register */
+ lan91cxx_write_phy(cpd, 0, LAN91CXX_PHY_CTRL, my_fixed_caps);
+
+ /* Re-Configure the Receive/Phy Control register */
+ put_reg(cpd, LAN91CXX_RPCR, cpd->rpc_cur_mode);
+
+ return (1);
+}
+#endif
+
+#if 0
+/*Configures the specified PHY using Autonegotiation. */
+static void lan91cxx_phy_configure(struct lan91cxx_priv_data *cpd)
+{
+
+ unsigned int phyaddr;
+ unsigned int my_phy_caps; /* My PHY capabilities */
+ unsigned int my_ad_caps; /* My Advertised capabilities */
+ unsigned int status = 0;
+ int failed = 0, delay;
+
+ DEBUG_FUNCTION();
+
+ /* Set the blocking flag */
+ cpd->autoneg_active = 1;
+
+ /* Get the detected phy address */
+ phyaddr = cpd->phyaddr;
+
+ /* Reset the PHY, setting all other bits to zero */
+ lan91cxx_write_phy(cpd, 0, PHY_CNTL_REG, PHY_CNTL_RST);
+
+ /* Wait for the reset to complete, or time out */
+ delay = 50;
+ while (delay--) {
+ if (!(lan91cxx_read_phy(cpd, 0, PHY_CNTL_REG)
+ & PHY_CNTL_RST)) {
+ break;
+ }
+ HAL_DELAY_US(100000);
+ }
+
+ if (delay < 1) {
+ db_printf("smc91111:!PHY reset timed out\n");
+ goto smc_phy_configure_exit;
+ }
+
+ /* Read PHY Register 18, Status Output */
+ cpd->lastPhy18 = lan91cxx_read_phy(cpd, 0, PHY_INT_REG);
+
+ /* Enable PHY Interrupts (for register 18) */
+ /* Interrupts listed here are disabled */
+ lan91cxx_write_phy(cpd, 0, PHY_MASK_REG,
+ PHY_INT_LOSSSYNC | PHY_INT_CWRD | PHY_INT_SSD
+ | PHY_INT_ESD | PHY_INT_RPOL | PHY_INT_JAB |
+ PHY_INT_SPDDET | PHY_INT_DPLXDET);
+
+ /* Configure the Receive/Phy Control register */
+ put_reg(cpd, LAN91CXX_RPCR, cpd->rpc_cur_mode);
+
+ /* Copy our capabilities from PHY_STAT_REG to PHY_AD_REG */
+ my_phy_caps = lan91cxx_read_phy(cpd, phyaddr, PHY_STAT_REG);
+ my_ad_caps = PHY_AD_CSMA; /* I am CSMA capable */
+
+ if (my_phy_caps & PHY_STAT_CAP_T4)
+ my_ad_caps |= PHY_AD_T4;
+
+ if (my_phy_caps & PHY_STAT_CAP_TXF)
+ my_ad_caps |= PHY_AD_TX_FDX;
+
+ if (my_phy_caps & PHY_STAT_CAP_TXH)
+ my_ad_caps |= PHY_AD_TX_HDX;
+
+ if (my_phy_caps & PHY_STAT_CAP_TF)
+ my_ad_caps |= PHY_AD_10_FDX;
+
+ if (my_phy_caps & PHY_STAT_CAP_TH)
+ my_ad_caps |= PHY_AD_10_HDX;
+
+ /* Disable capabilities not selected by our user */
+ if (cpd->config.ctl_rspeed != 100) {
+ my_ad_caps &= ~(PHY_AD_T4 | PHY_AD_TX_FDX | PHY_AD_TX_HDX);
+ }
+
+ if (!cpd->config.ctl_rfduplx) {
+ my_ad_caps &= ~(PHY_AD_TX_FDX | PHY_AD_10_FDX);
+ }
+
+ /* Update our Auto-Neg Advertisement Register */
+ lan91cxx_write_phy(cpd, 0, PHY_AD_REG, my_ad_caps);
+
+ db4_printf("smc91111:phy caps=%x\n", my_phy_caps);
+ db4_printf("smc91111:phy advertised caps=%x\n", my_ad_caps);
+
+ /* If the user requested no auto neg, then go set his request */
+ if (!(cpd->config.ctl_autoneg)) {
+ lan91cxx_phy_fixed(cpd);
+
+ goto smc_phy_configure_exit;
+ }
+
+ /* Restart auto-negotiation process in order to advertise my caps */
+ lan91cxx_write_phy(cpd, 0, PHY_CNTL_REG,
+ PHY_CNTL_ANEG_EN | PHY_CNTL_ANEG_RST);
+
+ /* wait for auto-negotiation to finish. */
+ /* give it ~5 seconds before giving up (no cable?) */
+ delay = 50;
+ while (!
+ ((status =
+ lan91cxx_read_phy(cpd, 0, LAN91CXX_PHY_STAT)) & 0x20)) {
+ if (--delay <= 0) {
+ printf("Timeout autonegotiation\n");
+ failed = 1;
+ break;
+ }
+
+ /* Restart auto-negotiation if remote fault */
+ if (status & PHY_STAT_REM_FLT) {
+ db_printf("smc91111:PHY remote fault detected\n");
+
+ /* Restart auto-negotiation */
+ db_printf("smc91111:PHY restarting auto-negotiation\n");
+ lan91cxx_write_phy(cpd, 0, PHY_CNTL_REG,
+ PHY_CNTL_ANEG_EN |
+ PHY_CNTL_ANEG_RST |
+ PHY_CNTL_SPEED | PHY_CNTL_DPLX);
+ }
+ HAL_DELAY_US(100000);
+ }
+
+ /* Fail if we detected an auto-negotiate remote fault */
+ if (status & PHY_STAT_REM_FLT) {
+ db_printf("smc91111:PHY remote fault detected\n");
+ failed = 1;
+ }
+
+ /* The smc_phy_interrupt() routine will be called to update lastPhy18 */
+
+ /* Set our sysctl parameters to match auto-negotiation results */
+ if (cpd->lastPhy18 & PHY_INT_SPDDET) {
+ db_printf("smc91111:PHY 100BaseT\n");
+ cpd->rpc_cur_mode |= LAN91CXX_RPCR_SPEED;
+ } else {
+ db_printf("smc91111:PHY 10BaseT\n");
+ cpd->rpc_cur_mode &= ~LAN91CXX_RPCR_SPEED;
+ }
+
+ if (cpd->lastPhy18 & PHY_INT_DPLXDET) {
+ db_printf("smc91111:PHY Full Duplex\n");
+ cpd->rpc_cur_mode |= LAN91CXX_RPCR_DPLX;
+ } else {
+ db_printf("smc91111:PHY Half Duplex\n");
+ cpd->rpc_cur_mode &= ~LAN91CXX_RPCR_DPLX;
+ }
+
+ /* Re-Configure the Receive/Phy Control register */
+ put_reg(cpd, LAN91CXX_RPCR, cpd->rpc_cur_mode);
+
+ smc_phy_configure_exit:
+
+ /* Exit auto-negotiation */
+ cpd->autoneg_active = 0;
+}
+#endif
+
+static uint16_t
+lan91cxx_read_phy(struct lan91cxx_priv_data *cpd, uint8_t phyaddr,
+ uint8_t phyreg)
+{
+ int i, mask, input_idx, clk_idx = 0;
+ uint16_t mii_reg, value;
+ uint8_t bits[64];
+
+ /* 32 consecutive ones on MDO to establish sync */
+ for (i = 0; i < 32; ++i)
+ bits[clk_idx++] = LAN91CXX_MGMT_MDOE | LAN91CXX_MGMT_MDO;
+
+ /* Start code <01> */
+ bits[clk_idx++] = LAN91CXX_MGMT_MDOE;
+ bits[clk_idx++] = LAN91CXX_MGMT_MDOE | LAN91CXX_MGMT_MDO;
+
+ /* Read command <10> */
+ bits[clk_idx++] = LAN91CXX_MGMT_MDOE | LAN91CXX_MGMT_MDO;
+ bits[clk_idx++] = LAN91CXX_MGMT_MDOE;
+
+ /* Output the PHY address, msb first */
+ for (mask = 0x10; mask; mask >>= 1) {
+ if (phyaddr & mask)
+ bits[clk_idx++] =
+ LAN91CXX_MGMT_MDOE | LAN91CXX_MGMT_MDO;
+ else
+ bits[clk_idx++] = LAN91CXX_MGMT_MDOE;
+ }
+
+ /* Output the phy register number, msb first */
+ for (mask = 0x10; mask; mask >>= 1) {
+ if (phyreg & mask)
+ bits[clk_idx++] =
+ LAN91CXX_MGMT_MDOE | LAN91CXX_MGMT_MDO;
+ else
+ bits[clk_idx++] = LAN91CXX_MGMT_MDOE;
+ }
+
+ /* Tristate and turnaround (1 bit times) */
+ bits[clk_idx++] = 0;
+
+ /* Input starts at this bit time */
+ input_idx = clk_idx;
+
+ /* Will input 16 bits */
+ for (i = 0; i < 16; ++i)
+ bits[clk_idx++] = 0;
+
+ /* Final clock bit */
+ bits[clk_idx++] = 0;
+
+ /* Get the current MII register value */
+ mii_reg = get_reg(cpd, LAN91CXX_MGMT);
+
+ /* Turn off all MII Interface bits */
+ mii_reg &= ~(LAN91CXX_MGMT_MDOE | LAN91CXX_MGMT_MCLK |
+ LAN91CXX_MGMT_MDI | LAN91CXX_MGMT_MDO);
+ HAL_DELAY_US(50);
+
+ /* Clock all 64 cycles */
+ for (i = 0; i < sizeof(bits); ++i) {
+ /* Clock Low - output data */
+ put_reg(cpd, LAN91CXX_MGMT, mii_reg | bits[i]);
+ HAL_DELAY_US(50);
+
+ /* Clock Hi - input data */
+ put_reg(cpd, LAN91CXX_MGMT,
+ mii_reg | bits[i] | LAN91CXX_MGMT_MCLK);
+ HAL_DELAY_US(50);
+
+ bits[i] |= get_reg(cpd, LAN91CXX_MGMT) & LAN91CXX_MGMT_MDI;
+ }
+
+ /* Return to idle state */
+ put_reg(cpd, LAN91CXX_MGMT, mii_reg);
+ HAL_DELAY_US(50);
+
+ /* Recover input data */
+ for (value = 0, i = 0; i < 16; ++i) {
+ value <<= 1;
+ if (bits[input_idx++] & LAN91CXX_MGMT_MDI)
+ value |= 1;
+ }
+
+ db16_printf("phy_read : %d : %04x\n", phyreg, value);
+ return value;
+}
+
+static void
+lan91cxx_write_phy(struct lan91cxx_priv_data *cpd, uint8_t phyaddr,
+ uint8_t phyreg, uint16_t value)
+{
+ int i, mask, clk_idx = 0;
+ uint16_t mii_reg;
+ uint8_t bits[65];
+
+ /* 32 consecutive ones on MDO to establish sync */
+ for (i = 0; i < 32; ++i)
+ bits[clk_idx++] = LAN91CXX_MGMT_MDOE | LAN91CXX_MGMT_MDO;
+
+ /* Start code <01> */
+ bits[clk_idx++] = LAN91CXX_MGMT_MDOE;
+ bits[clk_idx++] = LAN91CXX_MGMT_MDOE | LAN91CXX_MGMT_MDO;
+
+ /* Write command <01> */
+ bits[clk_idx++] = LAN91CXX_MGMT_MDOE;
+ bits[clk_idx++] = LAN91CXX_MGMT_MDOE | LAN91CXX_MGMT_MDO;
+
+ /* Output the PHY address, msb first */
+ for (mask = 0x10; mask; mask >>= 1) {
+ if (phyaddr & mask)
+ bits[clk_idx++] =
+ LAN91CXX_MGMT_MDOE | LAN91CXX_MGMT_MDO;
+ else
+ bits[clk_idx++] = LAN91CXX_MGMT_MDOE;
+ }
+
+ /* Output the phy register number, msb first */
+ for (mask = 0x10; mask; mask >>= 1) {
+ if (phyreg & mask)
+ bits[clk_idx++] =
+ LAN91CXX_MGMT_MDOE | LAN91CXX_MGMT_MDO;
+ else
+ bits[clk_idx++] = LAN91CXX_MGMT_MDOE;
+ }
+
+ /* Tristate and turnaround (2 bit times) */
+ bits[clk_idx++] = 0;
+ bits[clk_idx++] = 0;
+
+ /* Write out 16 bits of data, msb first */
+ for (mask = 0x8000; mask; mask >>= 1) {
+ if (value & mask)
+ bits[clk_idx++] =
+ LAN91CXX_MGMT_MDOE | LAN91CXX_MGMT_MDO;
+ else
+ bits[clk_idx++] = LAN91CXX_MGMT_MDOE;
+ }
+
+ /* Final clock bit (tristate) */
+ bits[clk_idx++] = 0;
+
+ /* Get the current MII register value */
+ mii_reg = get_reg(cpd, LAN91CXX_MGMT);
+
+ /* Turn off all MII Interface bits */
+ mii_reg &= ~(LAN91CXX_MGMT_MDOE | LAN91CXX_MGMT_MCLK |
+ LAN91CXX_MGMT_MDI | LAN91CXX_MGMT_MDO);
+ HAL_DELAY_US(50);
+
+ /* Clock all cycles */
+ for (i = 0; i < sizeof(bits); ++i) {
+ /* Clock Low - output data */
+ put_reg(cpd, LAN91CXX_MGMT, mii_reg | bits[i]);
+ HAL_DELAY_US(50);
+
+ /* Clock Hi - input data */
+ put_reg(cpd, LAN91CXX_MGMT,
+ mii_reg | bits[i] | LAN91CXX_MGMT_MCLK);
+ HAL_DELAY_US(50);
+
+/* bits[i] |= get_reg(cpd, LAN91CXX_MGMT) & LAN91CXX_MGMT_MDI;*/
+ }
+
+ /* Return to idle state */
+ put_reg(cpd, LAN91CXX_MGMT, mii_reg);
+ HAL_DELAY_US(50);
+
+ db16_printf("phy_write: %d : %04x\n", phyreg, value);
+}
+
+#if 0
+void lan91cxx_print_bank(int bank){
+ struct lan91cxx_priv_data *cpd = &smc91111;
+ int regno;
+ unsigned short regval[8];
+ int i;
+
+ if ( bank >= 4 )
+ return;
+ for(i=0; i<8; i++){
+ regno=i+bank<<3;
+ regval[i] = get_reg(cpd, regno);
+ }
+ printk("---- BANK %d ----\n\r",bank);
+ for(i=0; i<8; i++){
+ printk("0x%x: 0x%x\n\r",i,regval[i]);
+ }
+
+}
+#endif
+
+#endif
diff --git a/bsps/shared/net/smc91111config.h b/bsps/shared/net/smc91111config.h
new file mode 100644
index 0000000000..8340ca23bb
--- /dev/null
+++ b/bsps/shared/net/smc91111config.h
@@ -0,0 +1,118 @@
+#ifndef _SMC91111_CONFIG_H_
+#define _SMC91111_CONFIG_H_
+
+/*
+ * RTEMS event used by interrupt handler to signal driver tasks.
+ * This must not be any of the events used by the network task synchronization.
+ */
+#define INTERRUPT_EVENT RTEMS_EVENT_1
+
+/*
+ * RTEMS event used to start transmit daemon.
+ * This must not be the same as INTERRUPT_EVENT.
+ */
+#define START_TRANSMIT_EVENT RTEMS_EVENT_2
+
+ /* event to send when tx buffers become available */
+#define SMC91111_TX_WAIT_EVENT RTEMS_EVENT_3
+
+
+/* Number of OCs supported by this driver*/
+#define NOCDRIVER 1
+
+/* Receive buffer size -- Allow for a full ethernet packet including CRC */
+#define RBUF_SIZE 1536
+
+#define ET_MINLEN 64 /* minimum message length */
+
+#if (MCLBYTES < RBUF_SIZE)
+# error "Driver must have MCLBYTES > RBUF_SIZE"
+#endif
+
+/* ----------------- cygdriver params ----------------- */
+
+#define LAN91CXX_32BIT_RX
+#define LAN91CXX_IS_LAN91C111
+
+/* ----------------- compat layer ----------------- */
+
+#include <stdint.h>
+
+typedef uint32_t CYG_WORD;
+typedef uint8_t CYG_BYTE;
+typedef uint16_t CYG_WORD16;
+typedef uint32_t CYG_WORD32;
+
+#ifndef CYG_SWAP16
+# define CYG_SWAP16(_x_) \
+ ({ uint16_t _x = (_x_); ((_x << 8) | (_x >> 8)); })
+#endif
+
+#ifndef CYG_SWAP32
+# define CYG_SWAP32(_x_) \
+ ({ uint32_t _x = (_x_); \
+ ((_x << 24) | \
+ ((0x0000FF00UL & _x) << 8) | \
+ ((0x00FF0000UL & _x) >> 8) | \
+ (_x >> 24)); })
+#endif
+
+# define CYG_CPU_TO_BE16(_x_) (_x_)
+# define CYG_CPU_TO_BE32(_x_) (_x_)
+# define CYG_BE16_TO_CPU(_x_) (_x_)
+# define CYG_BE32_TO_CPU(_x_) (_x_)
+
+# define CYG_CPU_TO_LE16(_x_) CYG_SWAP16((_x_))
+# define CYG_CPU_TO_LE32(_x_) CYG_SWAP32((_x_))
+# define CYG_LE16_TO_CPU(_x_) CYG_SWAP16((_x_))
+# define CYG_LE32_TO_CPU(_x_) CYG_SWAP32((_x_))
+
+#define CYG_MACRO_START do {
+#define CYG_MACRO_END } while (0)
+#define HAL_IO_BARRIER() \
+ __asm__ volatile ( "" : : : "memory" )
+
+#define HAL_READ_UINT8( _register_, _value_ ) \
+ CYG_MACRO_START \
+ ((_value_) = *((volatile CYG_BYTE *)(_register_))); \
+ HAL_IO_BARRIER (); \
+ CYG_MACRO_END
+
+#define HAL_WRITE_UINT8( _register_, _value_ ) \
+ CYG_MACRO_START \
+ (*((volatile CYG_BYTE *)(_register_)) = (_value_)); \
+ HAL_IO_BARRIER (); \
+ CYG_MACRO_END
+
+#define HAL_READ_UINT16( _register_, _value_ ) \
+ CYG_MACRO_START \
+ ((_value_) = *((volatile CYG_WORD16 *)(_register_))); \
+ HAL_IO_BARRIER (); \
+ CYG_MACRO_END
+
+#define HAL_WRITE_UINT16( _register_, _value_ ) \
+ CYG_MACRO_START \
+ (*((volatile CYG_WORD16 *)(_register_)) = (_value_)); \
+ HAL_IO_BARRIER (); \
+ CYG_MACRO_END
+
+#define HAL_READ_UINT32( _register_, _value_ ) \
+ CYG_MACRO_START \
+ ((_value_) = *((volatile CYG_WORD32 *)(_register_))); \
+ HAL_IO_BARRIER (); \
+ CYG_MACRO_END
+
+#define HAL_READ_UINT16( _register_, _value_ ) \
+ CYG_MACRO_START \
+ ((_value_) = *((volatile CYG_WORD16 *)(_register_))); \
+ HAL_IO_BARRIER (); \
+ CYG_MACRO_END
+
+#define CYG_ASSERT(c,p) do { if (!(c)) { while(1) { printf(p);} }; } while(0)
+
+#define HAL_DELAY_US(p) rtems_task_wake_after (RTEMS_MICROSECONDS_TO_TICKS (p))
+
+
+#endif /* _SMC_91111_CONFIG_H_ */
+
+
diff --git a/bsps/shared/net/sonic.c b/bsps/shared/net/sonic.c
new file mode 100644
index 0000000000..dc97008b8d
--- /dev/null
+++ b/bsps/shared/net/sonic.c
@@ -0,0 +1,1685 @@
+/*
+ * RTEMS NETWORK DRIVER FOR NATIONAL DP83932 `SONIC'
+ * SYSTEMS-ORIENTED NETWORK INTERFACE CONTROLLER
+ *
+ * REUSABLE CHIP DRIVER
+ *
+ * References:
+ *
+ * 1) DP83932C-20/25/33 MHz SONIC(TM) Systems-Oriented Network Interface
+ * Controller data sheet. TL/F/10492, RRD-B30M105, National Semiconductor,
+ * 1995.
+ *
+ * 2) Software Driver Programmer's Guide for the DP83932 SONIC(TM),
+ * Application Note 746, Wesley Lee and Mike Lui, TL/F/11140,
+ * RRD-B30M75, National Semiconductor, March, 1991.
+ *
+ * COPYRIGHT (c) 1989-1997.
+ * On-Line Applications Research Corporation (OAR).
+ *
+ * The license and distribution terms for this file may be
+ * found in the file LICENSE in this distribution or at
+ * http://www.rtems.org/license/LICENSE.
+ *
+ * This driver was originally written and tested on a DY-4 DMV177,
+ * which had a 100 Mhz PPC603e.
+ *
+ * This driver also works with DP83934CVUL-20/25 MHz, tested on
+ * Tharsys ERC32 VME board.
+ *
+ * Rehaul to fix lost interrupts and buffers, and to use to use
+ * interrupt-free transmission by Jiri, 22/03/1999.
+ */
+
+#define __INSIDE_RTEMS_BSD_TCPIP_STACK__
+
+#include <rtems.h>
+#include <rtems/rtems_bsdnet.h>
+#include <libchip/sonic.h>
+
+#include <stdio.h>
+#include <string.h>
+
+#include <errno.h>
+#include <rtems/error.h>
+
+#include <sys/param.h>
+#include <sys/mbuf.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <sys/sockio.h>
+
+#include <net/if.h>
+
+#include <netinet/in.h>
+#include <netinet/if_ether.h>
+
+extern rtems_isr_entry set_vector( rtems_isr_entry, rtems_vector_number, int );
+
+#if (SONIC_DEBUG & SONIC_DEBUG_DUMP_MBUFS)
+#include <rtems/dumpbuf.h>
+#endif
+
+/*
+ * Use the top line if you want more symbols.
+ */
+
+#define SONIC_STATIC static
+
+/*
+ * Number of devices supported by this driver
+ */
+#ifndef NSONIC
+# define NSONIC 1
+#endif
+
+/*
+ *
+ * As suggested by National Application Note 746, make the
+ * receive resource area bigger than the receive descriptor area.
+ *
+ * NOTE: Changing this may break this driver since it currently
+ * assumes a 1<->1 mapping.
+ */
+#define RRA_EXTRA_COUNT 0
+
+/*
+ * RTEMS event used by interrupt handler to signal daemons.
+ */
+#define INTERRUPT_EVENT RTEMS_EVENT_1
+
+/*
+ * RTEMS event used to start transmit daemon.
+ * This must not be the same as INTERRUPT_EVENT.
+ */
+#define START_TRANSMIT_EVENT RTEMS_EVENT_2
+
+/*
+ * Largest Ethernet frame.
+ */
+#define MAXIMUM_FRAME_SIZE 1518
+
+/*
+ * Receive buffer size.
+ * Allow for a pointer, plus a full ethernet frame (including Frame
+ * Check Sequence) rounded up to a 4-byte boundary.
+ */
+#define RBUF_SIZE ((sizeof (void *) + (MAXIMUM_FRAME_SIZE) + 3) & ~3)
+/* #define RBUF_WC ((((MAXIMUM_FRAME_SIZE) + 3) & ~3) / 2) */
+#define RBUF_WC (RBUF_SIZE / 2)
+
+/*
+ * Macros for manipulating 32-bit pointers as 16-bit fragments
+ */
+#define LSW(p) ((uint16_t)((uintptr_t)(p)))
+#define MSW(p) ((uint16_t)((uintptr_t)(p) >> 16))
+#define PTR(m,l) ((void*)(((uint16_t)(m)<<16)|(uint16_t)(l)))
+
+/*
+ * Hardware-specific storage
+ */
+struct sonic_softc {
+ /*
+ * Connection to networking code
+ * This entry *must* be the first in the sonic_softc structure.
+ */
+ struct arpcom arpcom;
+
+ /*
+ * Default location of device registers
+ * ===CACHE===
+ * This area must be non-cacheable, guarded.
+ */
+ void *sonic;
+
+ /*
+ * Register access routines
+ */
+ sonic_write_register_t write_register;
+ sonic_read_register_t read_register;
+
+ /*
+ * Interrupt vector
+ */
+ rtems_vector_number vector;
+
+ /*
+ * Data Configuration Register values
+ */
+ uint32_t dcr_value;
+ uint32_t dc2_value;
+
+ /*
+ * Indicates configuration
+ */
+ int acceptBroadcast;
+
+ /*
+ * Task waiting for interrupts
+ */
+ rtems_id rxDaemonTid;
+ rtems_id txDaemonTid;
+
+ /*
+ * Receive resource area
+ */
+ int rdaCount;
+ ReceiveResourcePointer_t rsa;
+ ReceiveResourcePointer_t rea;
+ CamDescriptorPointer_t cdp;
+ ReceiveDescriptorPointer_t rda;
+ ReceiveDescriptorPointer_t rdp_last;
+
+ /*
+ * Transmit descriptors
+ */
+ int tdaCount;
+ TransmitDescriptorPointer_t tdaHead; /* Last filled */
+ TransmitDescriptorPointer_t tdaTail; /* Next to retire */
+
+ /*
+ * Statistics
+ */
+ unsigned long Interrupts;
+ unsigned long rxInterrupts;
+ unsigned long rxMissed;
+ unsigned long rxGiant;
+ unsigned long rxNonOctet;
+ unsigned long rxBadCRC;
+ unsigned long rxCollision;
+
+ unsigned long txInterrupts;
+ unsigned long txSingleCollision;
+ unsigned long txMultipleCollision;
+ unsigned long txCollision;
+ unsigned long txDeferred;
+ unsigned long txUnderrun;
+ unsigned long txLateCollision;
+ unsigned long txExcessiveCollision;
+ unsigned long txExcessiveDeferral;
+ unsigned long txLostCarrier;
+ unsigned long txRawWait;
+};
+SONIC_STATIC struct sonic_softc sonic_softc[NSONIC];
+
+
+/*
+ ******************************************************************
+ * *
+ * Debug Routines *
+ * *
+ ******************************************************************
+ */
+
+#if (SONIC_DEBUG & SONIC_DEBUG_MEMORY_DESCRIPTORS)
+void sonic_print_tx_descriptor(
+ TransmitDescriptorPointer_t tdp
+)
+{
+ printf( "TXD ==> %p", tdp );
+ printf( " pkt_config = 0x%04x", tdp->pkt_config & 0xffff);
+ printf( " pkt_size = 0x%04x\n", tdp->pkt_size & 0xffff );
+ printf( " frag_count = %d", tdp->frag_count & 0xffff );
+ /* could print all the fragments */
+ printf( " next = %p", tdp->next );
+ printf( " linkp = %p\n", tdp->linkp );
+ printf( " mbufp = %p", tdp->mbufp );
+ if ( tdp->mbufp )
+ printf( " mbufp->data = %p", mtod ( tdp->mbufp, void *) );
+ puts("");
+}
+
+void sonic_print_rx_descriptor(
+ ReceiveDescriptorPointer_t rdp
+)
+{
+ printf( "RXD ==> %p\n", rdp );
+ printf( " status = 0x%04x", rdp->status & 0xffff );
+ printf( " byte_count = 0x%04x\n", rdp->byte_count & 0xffff );
+ printf( " pkt = 0x%04x%04x", rdp->pkt_msw, rdp->pkt_lsw );
+ printf( " seq_no = %d", rdp->seq_no );
+ printf( " link = %d\n", rdp->link );
+ printf( " in_use = %d", rdp->in_use );
+ printf( " next = %p", rdp->next );
+ printf( " mbufp = %p", rdp->mbufp );
+ if ( rdp->mbufp )
+ printf( " mbufp->data = %p", mtod ( rdp->mbufp, void *) );
+ puts("");
+}
+#endif
+
+/*
+ ******************************************************************
+ * *
+ * Support Routines *
+ * *
+ ******************************************************************
+ */
+
+static void sonic_enable_interrupts(
+ struct sonic_softc *sc,
+ uint32_t mask
+)
+{
+ void *rp = sc->sonic;
+ rtems_interrupt_level level;
+
+ rtems_interrupt_disable( level );
+ (*sc->write_register)(
+ rp,
+ SONIC_REG_IMR,
+ (*sc->read_register)(rp, SONIC_REG_IMR) | mask
+ );
+ rtems_interrupt_enable( level );
+}
+
+static void sonic_disable_interrupts(
+ struct sonic_softc *sc,
+ uint32_t mask
+)
+{
+ void *rp = sc->sonic;
+ rtems_interrupt_level level;
+
+ rtems_interrupt_disable( level );
+ (*sc->write_register)(
+ rp,
+ SONIC_REG_IMR,
+ (*sc->read_register)(rp, SONIC_REG_IMR) & ~mask
+ );
+ rtems_interrupt_enable( level );
+}
+
+static void sonic_clear_interrupts(
+ struct sonic_softc *sc,
+ uint32_t mask
+)
+{
+ void *rp = sc->sonic;
+ rtems_interrupt_level level;
+
+ rtems_interrupt_disable( level );
+ (*sc->write_register)( rp, SONIC_REG_ISR, mask);
+ rtems_interrupt_enable( level );
+}
+
+static void sonic_command(
+ struct sonic_softc *sc,
+ uint32_t mask
+)
+{
+ void *rp = sc->sonic;
+ rtems_interrupt_level level;
+
+ rtems_interrupt_disable( level );
+ (*sc->write_register)( rp, SONIC_REG_CR, mask);
+ rtems_interrupt_enable( level );
+}
+
+/*
+ * Allocate non-cacheable memory on a single 64k page.
+ * Very simple minded -- just keeps trying till the memory is on a single page.
+ */
+SONIC_STATIC void * sonic_allocate(unsigned int nbytes)
+{
+ void *p;
+ unsigned long a1, a2;
+
+ for (;;) {
+ /*
+ * ===CACHE===
+ * Change malloc to malloc_noncacheable_guarded.
+ */
+ p = malloc( nbytes, M_MBUF, M_NOWAIT );
+ if (p == NULL)
+ rtems_panic ("No memory!");
+ memset (p, '\0', nbytes);
+ a1 = (unsigned long)p;
+ a2 = a1 + nbytes - 1;
+ if ((a1 >> 16) == (a2 >> 16))
+ break;
+ }
+#if (SONIC_DEBUG & SONIC_DEBUG_MEMORY_ALLOCATE)
+ printf( "sonic_allocate %d bytes at %p\n", nbytes, p );
+#endif
+ return p;
+}
+
+/*
+ * Shut down the interface.
+ */
+
+SONIC_STATIC void sonic_stop (struct sonic_softc *sc)
+{
+ struct ifnet *ifp = &sc->arpcom.ac_if;
+
+ ifp->if_flags &= ~IFF_RUNNING;
+
+ /*
+ * Stop the transmitter and receiver.
+ */
+ sonic_command(sc, CR_HTX | CR_RXDIS );
+}
+
+/*
+ * Show interface statistics
+ */
+SONIC_STATIC void sonic_stats (struct sonic_softc *sc)
+{
+ printf (" Total Interrupts:%-8lu", sc->Interrupts);
+ printf (" Rx Interrupts:%-8lu", sc->rxInterrupts);
+ printf (" Giant:%-8lu", sc->rxGiant);
+ printf (" Non-octet:%-8lu\n", sc->rxNonOctet);
+ printf (" Bad CRC:%-8lu", sc->rxBadCRC);
+ printf (" Collision:%-8lu", sc->rxCollision);
+ printf (" Missed:%-8lu\n", sc->rxMissed);
+
+ printf ( " Tx Interrupts:%-8lu", sc->txInterrupts);
+ printf ( " Deferred:%-8lu", sc->txDeferred);
+ printf (" Lost Carrier:%-8lu\n", sc->txLostCarrier);
+ printf ( "Single Collisions:%-8lu", sc->txSingleCollision);
+ printf ( "Multiple Collisions:%-8lu", sc->txMultipleCollision);
+ printf ("Excessive Collisions:%-8lu\n", sc->txExcessiveCollision);
+ printf ( " Total Collisions:%-8lu", sc->txCollision);
+ printf ( " Late Collision:%-8lu", sc->txLateCollision);
+ printf (" Underrun:%-8lu\n", sc->txUnderrun);
+ printf ( " Raw output wait:%-8lu\n", sc->txRawWait);
+}
+
+/*
+ ******************************************************************
+ * *
+ * Interrupt Handler *
+ * *
+ ******************************************************************
+ */
+
+SONIC_STATIC rtems_isr sonic_interrupt_handler (rtems_vector_number v)
+{
+ struct sonic_softc *sc = sonic_softc;
+ uint32_t isr, imr;
+ void *rp;
+
+#if (NSONIC > 1)
+ /*
+ * Find the device which requires service
+ */
+ for (;;) {
+ if (sc->vector == v)
+ break;
+ if (++sc == &sonic[NSONIC])
+ return; /* Spurious interrupt? */
+ }
+#endif /* NSONIC > 1 */
+
+ /*
+ * Get pointer to SONIC registers
+ */
+ rp = sc->sonic;
+
+ sc->Interrupts++;
+
+ isr = (*sc->read_register)( rp, SONIC_REG_ISR );
+ imr = (*sc->read_register)( rp, SONIC_REG_IMR );
+
+ /*
+ * Packet received or receive buffer area exceeded?
+ */
+ if (imr & isr & (IMR_PRXEN | IMR_RBAEEN)) {
+ imr &= ~(IMR_PRXEN | IMR_RBAEEN);
+ sc->rxInterrupts++;
+ rtems_bsdnet_event_send (sc->rxDaemonTid, INTERRUPT_EVENT);
+ (*sc->write_register)( rp, SONIC_REG_IMR, imr );
+ (*sc->write_register)( rp, SONIC_REG_ISR, isr & ISR_PKTRX );
+ }
+
+ /*
+ * Packet started, transmitter done or transmitter error?
+ * TX interrupts only occur after an error or when all TDA's are
+ * exhausted and we are waiting for buffer to come free.
+ */
+ if (imr & isr & (IMR_PINTEN | IMR_TXEREN)) {
+ sc->txInterrupts++;
+ rtems_bsdnet_event_send (sc->txDaemonTid, INTERRUPT_EVENT);
+ (*sc->write_register)( rp, SONIC_REG_ISR, ISR_PINT | ISR_TXDN | ISR_TXER );
+ }
+
+}
+
+/*
+ ******************************************************************
+ * *
+ * Transmitter Routines *
+ * *
+ ******************************************************************
+ */
+
+/*
+ * Soak up transmit descriptors that have been sent.
+ */
+
+SONIC_STATIC void sonic_retire_tda (struct sonic_softc *sc)
+{
+ uint16_t status;
+ unsigned int collisions;
+ struct mbuf *m, *n;
+
+ /*
+ * Repeat for all completed transmit descriptors.
+ */
+ while ((status = sc->tdaTail->status) != 0) {
+
+#if (SONIC_DEBUG & SONIC_DEBUG_DESCRIPTORS)
+ printf( "retire TDA %p (0x%04x)\n", sc->tdaTail, status );
+#endif
+
+#if (SONIC_DEBUG & SONIC_DEBUG_ERRORS)
+ /*
+ * If there is an error that was not a collision,
+ * then someone may want to see it.
+ */
+
+ if ( (status & ~(TDA_STATUS_COLLISION_MASK|TDA_STATUS_DEF)) != 0x0001 )
+ printf( "ERROR: retire TDA %p (0x%08x)\n",
+ sc->tdaTail, sc->tdaTail->status );
+#endif
+
+ /*
+ * Check for errors which stop the transmitter.
+ */
+ if (status & (TDA_STATUS_EXD |
+ TDA_STATUS_EXC |
+ TDA_STATUS_FU |
+ TDA_STATUS_BCM)) {
+ /*
+ * Restart the transmitter if there are
+ * packets waiting to go.
+ */
+ uint16_t link;
+#if (SONIC_DEBUG & SONIC_DEBUG_ERRORS)
+ printf("restarting sonic after error\n");
+#endif
+
+ link = *(sc->tdaTail->linkp);
+
+ if ((link & TDA_LINK_EOL) == 0) {
+ void *rp = sc->sonic;
+
+ (*sc->write_register)( rp, SONIC_REG_CTDA, link );
+ sonic_command(sc, CR_TXP );
+ }
+ }
+
+ /*
+ * Update network statistics
+ */
+ collisions = (status & TDA_STATUS_COLLISION_MASK) >> TDA_STATUS_COLLISION_SHIFT;
+ if (collisions) {
+ if (collisions == 1)
+ sc->txSingleCollision++;
+ else
+ sc->txMultipleCollision++;
+ sc->txCollision += collisions;
+ }
+ if (status & TDA_STATUS_EXC)
+ sc->txExcessiveCollision++;
+ if (status & TDA_STATUS_OWC)
+ sc->txLateCollision++;
+ if (status & TDA_STATUS_EXD)
+ sc->txExcessiveDeferral++;
+ if (status & TDA_STATUS_DEF)
+ sc->txDeferred++;
+ if (status & TDA_STATUS_FU)
+ sc->txUnderrun++;
+ if (status & TDA_STATUS_CRSL)
+ sc->txLostCarrier++;
+
+ /*
+ * Free the packet and reset a couple of fields
+ */
+ m = sc->tdaTail->mbufp;
+ while ( m ) {
+ MFREE(m, n);
+ m = n;
+ }
+
+ /*
+ sc->tdaTail->frag[0].frag_link = LSW(sc->tdaTail->link_pad);
+ sc->tdaTail->frag_count = 0;
+ */
+ sc->tdaTail->status = 0;
+
+ /*
+ * Move to the next transmit descriptor
+ */
+ sc->tdaTail = sc->tdaTail->next;
+#if (SONIC_DEBUG & SONIC_DEBUG_DESCRIPTORS)
+ printf( "next TDA %p\n", sc->tdaTail );
+#endif
+ }
+}
+
+/*
+ * Send packet
+ */
+SONIC_STATIC void sonic_sendpacket (struct ifnet *ifp, struct mbuf *m)
+{
+ struct sonic_softc *sc = ifp->if_softc;
+ struct mbuf *l = NULL;
+ TransmitDescriptorPointer_t tdp;
+ volatile struct TransmitDescriptorFragLink *fp;
+ unsigned int packetSize;
+ int i;
+ rtems_event_set events;
+ static char padBuf[64];
+
+ /* printf( "sonic_sendpacket %p\n", m ); */
+
+
+ /*
+ * Wait for transmit descriptor to become available. Only retire TDA's
+ * if there are no more free buffers to minimize TX latency. Retire TDA'a
+ * on the way out.
+ */
+
+ while (sc->tdaHead->next->status != 0) {
+
+ /*
+ * Free up transmit descriptors
+ */
+ sonic_retire_tda (sc);
+
+ if (sc->tdaHead->next->status == 0)
+ break;
+
+#if (SONIC_DEBUG & SONIC_DEBUG_ERRORS)
+ printf("blocking until TDAs are available\n");
+#endif
+ /*
+ * Enable PINT interrupts.
+ sonic_clear_interrupts( sc, ISR_PINT );
+ sonic_enable_interrupts( sc, IMR_PINTEN );
+ */
+
+ /*
+ * Wait for PINT TX interrupt. Every fourth TX buffer will raise PINT.
+ */
+ rtems_bsdnet_event_receive (INTERRUPT_EVENT,
+ RTEMS_WAIT|RTEMS_EVENT_ANY,
+ RTEMS_NO_TIMEOUT,
+ &events);
+ sonic_disable_interrupts( sc, IMR_PINTEN );
+ sonic_retire_tda (sc);
+ }
+
+ /*
+ * Fill in the transmit descriptor fragment descriptors.
+ * ===CACHE===
+ * If data cache is operating in write-back mode, flush cached
+ * data to memory.
+ */
+ tdp = sc->tdaHead->next;
+ tdp->mbufp = m;
+ packetSize = 0;
+ fp = tdp->frag;
+ for (i = 0 ; i < MAXIMUM_FRAGS_PER_DESCRIPTOR ; i++, fp++) {
+ /*
+ * Throw away empty mbufs
+ */
+ if (m->m_len) {
+ void *p = mtod (m, void *);
+ fp->frag_lsw = LSW(p);
+ fp->frag_msw = MSW(p);
+ fp->frag_size = m->m_len;
+ packetSize += m->m_len;
+#if (SONIC_DEBUG & SONIC_DEBUG_FRAGMENTS)
+ printf( "fp %p 0x%04x%04x %d=%d .. %d\n",
+ fp, fp->frag_msw, fp->frag_lsw, fp->frag_size, m->m_len, packetSize );
+#endif
+#if (SONIC_DEBUG & SONIC_DEBUG_DUMP_TX_MBUFS)
+ rtems_print_buffer(
+ p,
+ (fp->frag_size > MAXIMUM_FRAME_SIZE) ? MAXIMUM_FRAME_SIZE : fp->frag_size
+ );
+#endif
+ l = m;
+ m = m->m_next;
+ }
+ else {
+ struct mbuf *n;
+ MFREE (m, n);
+ m = n;
+ if (l != NULL)
+ l->m_next = m;
+ }
+ /*
+ * Break out of the loop if this mbuf is the last in the frame.
+ */
+ if (m == NULL)
+ break;
+ }
+
+ /*
+ * Pad short packets.
+ */
+ if ((packetSize < 64) && (i < MAXIMUM_FRAGS_PER_DESCRIPTOR)) {
+ int padSize = 64 - packetSize;
+ fp++;
+ fp->frag_lsw = LSW(padBuf);
+ fp->frag_msw = MSW(padBuf);
+ fp->frag_size = padSize;
+#if (SONIC_DEBUG & SONIC_DEBUG_FRAGMENTS)
+ printf( "PAD fp %p 0x%04x%04x %d\n",
+ fp, fp->frag_msw, fp->frag_lsw, fp->frag_size );
+#endif
+ packetSize += padSize;
+ i++;
+ }
+
+ /*
+ * Fill Transmit Descriptor
+ */
+ tdp->pkt_size = packetSize;
+ tdp->frag_count = i + 1;
+ tdp->status = 0;
+
+ /*
+ * Chain onto list and start transmission.
+ */
+
+ tdp->linkp = &(fp+1)->frag_link;
+ *tdp->linkp = LSW(tdp->next) | TDA_LINK_EOL;
+ if ( sc->tdaHead->frag_count )
+ *sc->tdaHead->linkp &= ~TDA_LINK_EOL;
+ sc->tdaHead = tdp;
+
+ /* Start transmission */
+
+ sonic_command(sc, CR_TXP );
+
+ /*
+ * Free up transmit descriptors on the way out.
+ */
+ sonic_retire_tda (sc);
+}
+
+/*
+ * Driver transmit daemon
+ */
+SONIC_STATIC void sonic_txDaemon (void *arg)
+{
+ struct sonic_softc *sc = (struct sonic_softc *)arg;
+ struct ifnet *ifp = &sc->arpcom.ac_if;
+ struct mbuf *m;
+ rtems_event_set events;
+
+ for (;;) {
+ /*
+ * Wait for packet
+ */
+ rtems_bsdnet_event_receive (
+ START_TRANSMIT_EVENT,
+ RTEMS_EVENT_ANY | RTEMS_WAIT,
+ RTEMS_NO_TIMEOUT,
+ &events
+ );
+
+ /*
+ * Send packets till queue is empty
+ */
+ for (;;) {
+ /*
+ * Get the next mbuf chain to transmit.
+ */
+ IF_DEQUEUE(&ifp->if_snd, m);
+ if (!m)
+ break;
+ sonic_sendpacket (ifp, m);
+ }
+ ifp->if_flags &= ~IFF_OACTIVE;
+ }
+}
+
+/*
+ ******************************************************************
+ * *
+ * Receiver Routines *
+ * *
+ ******************************************************************
+ */
+
+/*
+ * Wait for SONIC to hand over a Receive Descriptor.
+ */
+
+SONIC_STATIC void sonic_rda_wait(
+ struct sonic_softc *sc,
+ ReceiveDescriptorPointer_t rdp
+)
+{
+ int i;
+ void *rp = sc->sonic;
+ rtems_event_set events;
+
+ /*
+ * Wait for Receive Descriptor.
+ * The order of the tests is very important.
+ * The RDA is checked after RBAE is detected. This ensures that
+ * the driver processes all RDA entries before reusing the RRA
+ * entry holding the giant packet.
+ * The event wait is done after the RDA and RBAE checks. This
+ * catches the possibility that a Receive Descriptor became ready
+ * between the call to this function and the clearing of the
+ * interrupt status register bit.
+ */
+ for (;;) {
+ /*
+ * Has a giant packet arrived?
+ * The National DP83932C data sheet is very vague on what
+ * happens under this condition. The description of the
+ * Interrupt Status Register (Section 4.3.6) states,
+ * ``Reception is aborted and the SONIC fetches the next
+ * available resource descriptors in the RRA. The buffer
+ * space is not re-used and an RDA is not setup for the
+ * truncated packet.''
+ * I take ``Reception is aborted'' to mean that the RXEN
+ * bit in the Command Register is cleared and must be set
+ * by the driver to begin reception again.
+ * Unfortunately, an alternative interpretation could be
+ * that only reception of the current packet is aborted.
+ * This would be more difficult to recover from....
+ */
+ if ((*sc->read_register)( rp, SONIC_REG_ISR ) & ISR_RBAE) {
+
+#if (SONIC_DEBUG & SONIC_DEBUG_ERRORS)
+ printf( "ERROR: looks like a giant packet -- RBAE\n" );
+#endif
+
+ /*
+ * One more check to soak up any Receive Descriptors
+ * that may already have been handed back to the driver.
+ */
+ if (rdp->in_use == RDA_IN_USE) {
+#if (SONIC_DEBUG & SONIC_DEBUG_ERRORS)
+ printf( "ERROR: nope just an RBAE\n" );
+#endif
+ break;
+ }
+
+ /*
+ * Check my interpretation of the SONIC manual.
+ */
+ if ((*sc->read_register)( rp, SONIC_REG_CR ) & CR_RXEN)
+ rtems_panic ("SONIC RBAE/RXEN");
+
+ /*
+ * Update statistics
+ */
+ sc->rxGiant++;
+
+ /*
+ * Reuse receive buffer.
+ * Again, the manual is subject to interpretation. The
+ * RRP register is described as, `the lower address of
+ * the next descriptor the SONIC will read.''
+ * Since, acording to the ISR/RBAE notes, the SONIC has
+ * ``fetched the next available resource descriptor in
+ * the RRA'', I interpret this to mean that that the
+ * driver has to move the RRP back *two* entries to
+ * reuse the receive buffer holding the giant packet.
+ */
+ for (i = 0; i < 2; ++i) {
+ uint32_t rrp = (*sc->read_register)( rp, SONIC_REG_RRP );
+ const uint32_t rsa = (*sc->read_register)( rp, SONIC_REG_RSA );
+
+ if (rrp == rsa) {
+ const uint32_t rea = (*sc->read_register)( rp, SONIC_REG_REA );
+ (*sc->write_register)( rp, SONIC_REG_RRP, rea );
+ }
+
+ rrp = (*sc->read_register)( rp, SONIC_REG_RRP );
+ (*sc->write_register)( rp, SONIC_REG_RRP, rrp - sizeof(ReceiveResource_t) );
+ }
+
+ /*
+ * Restart reception
+ */
+ sonic_clear_interrupts( sc, ISR_RBAE );
+ sonic_command( sc, CR_RXEN );
+ }
+
+ /*
+ * Has Receive Descriptor become available?
+ */
+ if (rdp->in_use == RDA_IN_USE)
+ break;
+
+ /*
+ * Enable interrupts.
+ */
+ sonic_enable_interrupts( sc, (IMR_PRXEN | IMR_RBAEEN) );
+
+ /*
+ * Wait for interrupt.
+ */
+ rtems_bsdnet_event_receive(
+ INTERRUPT_EVENT,
+ RTEMS_WAIT|RTEMS_EVENT_ANY,
+ RTEMS_NO_TIMEOUT,
+ &events
+ );
+ }
+#if (SONIC_DEBUG & SONIC_DEBUG_DESCRIPTORS)
+ printf( "RDA %p\n", rdp );
+#endif
+
+#if (SONIC_DEBUG & SONIC_DEBUG_ERRORS)
+ if (rdp->status & 0x000E)
+ printf( "ERROR: RDA %p (0x%04x)\n", rdp, rdp->status );
+#endif
+
+}
+
+#ifdef CPU_U32_FIX
+
+/*
+ * Routine to align the received packet so that the ip header
+ * is on a 32-bit boundary. Necessary for cpu's that do not
+ * allow unaligned loads and stores and when the 32-bit DMA
+ * mode is used.
+ *
+ * Transfers are done on word basis to avoid possibly slow byte
+ * and half-word writes.
+ */
+
+void ipalign(struct mbuf *m)
+{
+ unsigned int *first, *last, data;
+ unsigned int tmp = 0;
+
+ if ((((int) m->m_data) & 2) && (m->m_len)) {
+ last = (unsigned int *) ((((int) m->m_data) + m->m_len + 8) & ~3);
+ first = (unsigned int *) (((int) m->m_data) & ~3);
+ tmp = *first << 16;
+ first++;
+ do {
+ data = *first;
+ *first = tmp | (data >> 16);
+ tmp = data << 16;
+ first++;
+ } while (first <= last);
+
+ m->m_data = (caddr_t)(((int) m->m_data) + 2);
+ }
+}
+#endif
+
+/*
+ * SONIC reader task
+ */
+SONIC_STATIC void sonic_rxDaemon (void *arg)
+{
+ struct sonic_softc *sc = (struct sonic_softc *)arg;
+ struct ifnet *ifp = &sc->arpcom.ac_if;
+ void *rp = sc->sonic;
+ struct mbuf *m;
+ uint16_t status;
+ ReceiveDescriptorPointer_t rdp;
+ ReceiveResourcePointer_t rwp, rea;
+ uint16_t newMissedTally, oldMissedTally;
+
+ rwp = sc->rsa;
+ rea = sc->rea;
+ rdp = sc->rda;
+
+ /*
+ * Start the receiver
+ */
+ oldMissedTally = (*sc->read_register)( rp, SONIC_REG_MPT );
+
+ /*
+ * Input packet handling loop
+ */
+ for (;;) {
+ /*
+ * Wait till SONIC supplies a Receive Descriptor.
+ */
+ if (rdp->in_use == RDA_FREE) {
+ sonic_rda_wait (sc, rdp);
+ }
+
+#if (SONIC_DEBUG & SONIC_DEBUG_DESCRIPTORS)
+ printf( "Incoming packet %p status=0x%04x\n", rdp, rdp->status );
+#endif
+
+ /*
+ * Check that packet is valid
+ */
+ status = rdp->status;
+ if (status & RDA_STATUS_PRX) {
+ struct ether_header *eh;
+ void *p;
+
+ /*
+ * Pass the packet up the chain.
+ * The mbuf count is reduced to remove
+ * the frame check sequence at the end
+ * of the packet.
+ * ===CACHE===
+ * Invalidate cache entries for this memory.
+ */
+#if (SONIC_DEBUG & SONIC_DEBUG_MEMORY_DESCRIPTORS)
+ sonic_print_rx_descriptor( rdp );
+ if ((LSW(rdp->mbufp->m_data) != rdp->pkt_lsw)
+ || (MSW(rdp->mbufp->m_data) != rdp->pkt_msw))
+ printf ("SONIC RDA/RRA %p, %08x\n",rdp->mbufp->m_data,(rdp->pkt_msw << 16) |
+ (rdp->pkt_lsw & 0x0ffff));
+#endif
+ rdp->byte_count &= 0x0ffff; /* ERC32 pollutes msb of byte_count */
+ m = rdp->mbufp;
+ m->m_len = m->m_pkthdr.len = rdp->byte_count -
+ sizeof(uint32_t) -
+ sizeof(struct ether_header);
+ eh = mtod (m, struct ether_header *);
+ m->m_data += sizeof(struct ether_header);
+
+#ifdef CPU_U32_FIX
+ ipalign(m); /* Align packet on 32-bit boundary */
+#endif
+
+#if (SONIC_DEBUG & SONIC_DEBUG_DUMP_RX_MBUFS)
+ rtems_print_buffer( (void *) eh, sizeof(struct ether_header) );
+ rtems_print_buffer( (void *) m, 96 /* m->m_len*/ );
+#endif
+
+ /* printf( "ether_input %p\n", m ); */
+ /*
+ printf( "pkt %p, seq %04x, mbuf %p, m_data %p\n", rdp, rdp->seq_no, m, m->m_data );
+ printf( "%u, %u\n", ((int*)m->m_data)[6], ((int*)m->m_data)[7]);
+ */
+ ether_input (ifp, eh, m);
+ /*
+ */
+
+ /*
+ * Sanity check that Receive Resource Area is
+ * still in sync with Receive Descriptor Area
+ * The buffer reported in the Receive Descriptor
+ * should be the same as the buffer in the Receive
+ * Resource we are about to reuse.
+ */
+/* XXX figure out whether this is valid or not */
+#if 0
+ if ((LSW(p) != rwp->buff_ptr_lsw)
+ || (MSW(p) != rwp->buff_ptr_msw))
+ rtems_panic ("SONIC RDA/RRA");
+#endif
+
+ /*
+ * Allocate a new mbuf.
+ */
+
+ MGETHDR (m, M_WAIT, MT_DATA);
+ MCLGET (m, M_WAIT);
+ m->m_pkthdr.rcvif = ifp;
+ rdp->mbufp = m;
+ p = mtod (m, void *);
+
+ /*
+ * Reuse Receive Resource.
+ */
+
+ rwp->buff_ptr_lsw = LSW(p);
+ rwp->buff_ptr_msw = MSW(p);
+ rwp->buff_wc_lsw = RBUF_WC;
+ rwp->buff_wc_msw = 0;
+ rwp++;
+
+ if (rwp == rea) {
+#if (SONIC_DEBUG & SONIC_DEBUG_MEMORY)
+ printf( "Wrapping RWP from %p to %p\n", rwp, sc->rsa );
+#endif
+ rwp = sc->rsa;
+ }
+ (*sc->write_register)( rp, SONIC_REG_RWP , LSW(rwp) );
+
+ /*
+ * Tell the SONIC to reread the RRA.
+ */
+ if ((*sc->read_register)( rp, SONIC_REG_ISR ) & ISR_RBE)
+ sonic_clear_interrupts( sc, ISR_RBE );
+ }
+ else {
+ if (status & RDA_STATUS_COL)
+ sc->rxCollision++;
+ if (status & RDA_STATUS_FAER)
+ sc->rxNonOctet++;
+ else if (status & RDA_STATUS_CRCR)
+ sc->rxBadCRC++;
+ }
+
+ /*
+ * Count missed packets
+ */
+ newMissedTally = (*sc->read_register)( rp, SONIC_REG_MPT );
+ if (newMissedTally != oldMissedTally) {
+ sc->rxMissed += (newMissedTally - oldMissedTally) & 0xFFFF;
+ newMissedTally = oldMissedTally;
+ }
+
+ /*
+ * Move to next receive descriptor and update EOL
+ */
+
+ rdp->link |= RDA_LINK_EOL;
+ rdp->in_use = RDA_FREE;
+ sc->rdp_last->link &= ~RDA_LINK_EOL;
+ sc->rdp_last = rdp;
+ rdp = rdp->next;
+
+ }
+}
+
+/*
+ ******************************************************************
+ * *
+ * Initialization Routines *
+ * *
+ ******************************************************************
+ */
+
+/*
+ * Initialize the SONIC hardware
+ */
+SONIC_STATIC void sonic_initialize_hardware(struct sonic_softc *sc)
+{
+ void *rp = sc->sonic;
+ int i;
+ unsigned char *hwaddr;
+ TransmitDescriptorPointer_t tdp;
+ ReceiveDescriptorPointer_t ordp, rdp;
+ ReceiveResourcePointer_t rwp;
+ struct mbuf *m;
+ void *p;
+ CamDescriptorPointer_t cdp;
+
+ /*
+ * The Revision B SONIC has a horrible bug known as the "Zero
+ * Length Packet bug". The initial board used to develop this
+ * driver had a newer revision of the SONIC so there was no reason
+ * to check for this. If you have the Revision B SONIC chip, then
+ * you need to add some code to the RX path to handle this weirdness.
+ */
+
+ if ( (*sc->read_register)( rp, SONIC_REG_SR ) <= SONIC_REVISION_B ) {
+ rtems_fatal_error_occurred( 0x0BADF00D ); /* don't eat this part :) */
+ }
+
+ /*
+ * Set up circular linked list in Transmit Descriptor Area.
+ * Use the PINT bit in the transmit configuration field to
+ * request an interrupt on every other transmitted packet.
+ *
+ * NOTE: sonic_allocate() zeroes all of the memory allocated.
+ */
+
+ sc->tdaTail = sonic_allocate(sc->tdaCount * sizeof *tdp);
+#if (SONIC_DEBUG & SONIC_DEBUG_MEMORY)
+ printf( "tdaTail = %p\n", sc->tdaTail );
+#endif
+ tdp = sc->tdaTail;
+ for (i = 0 ; i < sc->tdaCount ; i++) {
+ /*
+ * Start off with the table of outstanding mbuf's
+ */
+
+ /*
+ * status, pkt_config, pkt_size, and all fragment fields
+ * are set to zero by sonic_allocate.
+ */
+
+/* XXX not used by the BSD drivers
+ tdp->frag[0].frag_link = LSW(tdp + 1);
+*/
+ if (i & 3)
+ tdp->pkt_config = TDA_CONFIG_PINT;
+
+ tdp->status = 0;
+ tdp->frag_count = 0;
+ tdp->link_pad = LSW(tdp + 1) | TDA_LINK_EOL;
+ tdp->linkp = &((tdp + 1)->frag[0].frag_link);
+ tdp->next = (TransmitDescriptor_t *)(tdp + 1);
+#if (SONIC_DEBUG & SONIC_DEBUG_MEMORY_DESCRIPTORS)
+ sonic_print_tx_descriptor( tdp );
+#endif
+ tdp++;
+ }
+ tdp--;
+ sc->tdaHead = tdp;
+ tdp->link_pad = LSW(sc->tdaTail) | TDA_LINK_EOL;
+ tdp->next = (TransmitDescriptor_t *)sc->tdaTail;
+ tdp->linkp = &sc->tdaTail->frag[0].frag_link;
+
+ /*
+ * Set up circular linked list in Receive Descriptor Area.
+ * Leaves sc->rda pointing at the `beginning' of the list.
+ *
+ * NOTE: The RDA and CDP must have the same MSW for their addresses.
+ */
+
+ sc->rda = sonic_allocate(
+ (sc->rdaCount * sizeof(ReceiveDescriptor_t)) +
+ sizeof(CamDescriptor_t) );
+ sc->cdp = (CamDescriptorPointer_t) ((unsigned char *)sc->rda +
+ (sc->rdaCount * sizeof(ReceiveDescriptor_t)));
+#if (SONIC_DEBUG & SONIC_DEBUG_MEMORY)
+ printf( "rda area = %p\n", sc->rda );
+ printf( "cdp area = %p\n", sc->cdp );
+#endif
+
+ ordp = rdp = sc->rda;
+ for (i = 0 ; i < sc->rdaCount ; i++) {
+ /*
+ * status, byte_count, pkt_ptr0, pkt_ptr1, and seq_no are set
+ * to zero by sonic_allocate.
+ */
+ rdp->link = LSW(rdp + 1);
+ rdp->in_use = RDA_FREE;
+ rdp->next = (ReceiveDescriptor_t *)(rdp + 1);
+ ordp = rdp;
+ rdp++;
+ }
+ /*
+ * Link the last desriptor to the 1st one and mark it as the end
+ * of the list.
+ */
+ ordp->next = sc->rda;
+ ordp->link = LSW(sc->rda) | RDA_LINK_EOL;
+ sc->rdp_last = ordp;
+
+ /*
+ * Allocate the receive resource area.
+ * In accordance with National Application Note 746, make the
+ * receive resource area bigger than the receive descriptor area.
+ * This has the useful side effect of making the receive resource
+ * area big enough to hold the CAM descriptor area.
+ */
+
+ sc->rsa = sonic_allocate((sc->rdaCount + RRA_EXTRA_COUNT) * sizeof *sc->rsa);
+#if (SONIC_DEBUG & SONIC_DEBUG_MEMORY)
+ printf( "rsa area = %p\n", sc->rsa );
+#endif
+
+ /*
+ * Set up list in Receive Resource Area.
+ * Allocate space for incoming packets.
+ */
+
+ rwp = sc->rsa;
+ for (i = 0 ; i < (sc->rdaCount + RRA_EXTRA_COUNT) ; i++, rwp++) {
+
+ /*
+ * Allocate memory for buffer.
+ * Place a pointer to the mbuf at the beginning of the buffer
+ * so we can find the mbuf when the SONIC returns the buffer
+ * to the driver.
+ */
+
+ MGETHDR (m, M_WAIT, MT_DATA);
+ MCLGET (m, M_WAIT);
+ m->m_pkthdr.rcvif = &sc->arpcom.ac_if;
+ sc->rda[i].mbufp = m;
+
+ p = mtod (m, void *);
+
+ /*
+ * Set up RRA entry
+ */
+ rwp->buff_ptr_lsw = LSW(p);
+ rwp->buff_ptr_msw = MSW(p);
+ rwp->buff_wc_lsw = RBUF_WC;
+ rwp->buff_wc_msw = 0;
+#if (SONIC_DEBUG & SONIC_DEBUG_MEMORY_DESCRIPTORS)
+ sonic_print_rx_descriptor( &sc->rda[i] );
+#endif
+ }
+ sc->rea = rwp;
+#if (SONIC_DEBUG & SONIC_DEBUG_MEMORY)
+ printf( "rea area = %p\n", sc->rea );
+#endif
+
+
+ /*
+ * Issue a software reset.
+ */
+ (*sc->write_register)( rp, SONIC_REG_CR, CR_RST | CR_STP | CR_RXDIS | CR_HTX );
+
+ /*
+ * Set up data configuration registers.
+ */
+ (*sc->write_register)( rp, SONIC_REG_DCR, sc->dcr_value );
+ (*sc->write_register)( rp, SONIC_REG_DCR2, sc->dc2_value );
+
+ (*sc->write_register)( rp, SONIC_REG_CR, CR_STP | CR_RXDIS | CR_HTX );
+
+ /*
+ * Mask all interrupts
+ */
+ (*sc->write_register)( rp, SONIC_REG_IMR, 0x0 ); /* XXX was backwards */
+
+ /*
+ * Clear outstanding interrupts.
+ */
+ (*sc->write_register)( rp, SONIC_REG_ISR, 0x7FFF );
+
+ /*
+ * Clear the tally counters
+ */
+
+ (*sc->write_register)( rp, SONIC_REG_CRCT, 0xFFFF );
+ (*sc->write_register)( rp, SONIC_REG_FAET, 0xFFFF );
+ (*sc->write_register)( rp, SONIC_REG_MPT, 0xFFFF );
+ (*sc->write_register)( rp, SONIC_REG_RSC, 0 );
+
+ /*
+ * Set the Receiver mode
+ *
+ * Enable/disable reception of broadcast packets
+ */
+
+ if (sc->acceptBroadcast)
+ (*sc->write_register)( rp, SONIC_REG_RCR, RCR_BRD );
+ else
+ (*sc->write_register)( rp, SONIC_REG_RCR, 0 );
+
+ /*
+ * Set up Resource Area pointers
+ */
+
+ (*sc->write_register)( rp, SONIC_REG_URRA, MSW(sc->rsa) );
+ (*sc->write_register)( rp, SONIC_REG_RSA, LSW(sc->rsa) );
+
+ (*sc->write_register)( rp, SONIC_REG_REA, LSW(sc->rea) );
+
+ (*sc->write_register)( rp, SONIC_REG_RRP, LSW(sc->rsa) );
+ (*sc->write_register)( rp, SONIC_REG_RWP, LSW(sc->rsa) ); /* XXX was rea */
+
+ (*sc->write_register)( rp, SONIC_REG_URDA, MSW(sc->rda) );
+ (*sc->write_register)( rp, SONIC_REG_CRDA, LSW(sc->rda) );
+
+ (*sc->write_register)( rp, SONIC_REG_UTDA, MSW(sc->tdaTail) );
+ (*sc->write_register)( rp, SONIC_REG_CTDA, LSW(sc->tdaTail) );
+
+ /*
+ * Set End Of Buffer Count register to the value recommended
+ * in Note 1 of Section 3.4.4.4 of the SONIC data sheet.
+ */
+
+ (*sc->write_register)( rp, SONIC_REG_EOBC, RBUF_WC - 2 );
+
+ /*
+ * Issue the load RRA command
+ */
+
+ (*sc->write_register)( rp, SONIC_REG_CR, CR_RRRA );
+ while ((*sc->read_register)( rp, SONIC_REG_CR ) & CR_RRRA)
+ continue;
+
+ /*
+ * Remove device reset
+ */
+
+ (*sc->write_register)( rp, SONIC_REG_CR, 0 );
+
+ /*
+ * Set up the SONIC CAM with our hardware address.
+ */
+
+ hwaddr = sc->arpcom.ac_enaddr;
+ cdp = sc->cdp;
+
+#if (SONIC_DEBUG & SONIC_DEBUG_CAM)
+ printf( "hwaddr: %2x:%2x:%2x:%2x:%2x:%2x\n",
+ hwaddr[0], hwaddr[1], hwaddr[2], hwaddr[3], hwaddr[4], hwaddr[5] );
+#endif
+
+ cdp->cep = 0; /* Fill first and only entry in CAM */
+ cdp->cap0 = hwaddr[1] << 8 | hwaddr[0];
+ cdp->cap1 = hwaddr[3] << 8 | hwaddr[2];
+ cdp->cap2 = hwaddr[5] << 8 | hwaddr[4];
+ cdp->ce = 0x0001; /* Enable first entry in CAM */
+
+ (*sc->write_register)( rp, SONIC_REG_CDC, 1 ); /* 1 entry in CDA */
+ (*sc->write_register)( rp, SONIC_REG_CDP, LSW(cdp) );
+ (*sc->write_register)( rp, SONIC_REG_CR, CR_LCAM ); /* Load the CAM */
+
+ while ((*sc->read_register)( rp, SONIC_REG_CR ) & CR_LCAM)
+ continue;
+
+ /*
+ * Verify that CAM was properly loaded.
+ */
+
+ (*sc->write_register)( rp, SONIC_REG_CR, CR_RST | CR_STP | CR_RXDIS | CR_HTX );
+
+#if (SONIC_DEBUG & SONIC_DEBUG_CAM)
+ (*sc->write_register)( rp, SONIC_REG_CEP, 0 ); /* Select first entry in CAM */
+ printf ("Loaded Ethernet address into SONIC CAM.\n"
+ " Wrote %04x%04x%04x - %#x\n"
+ " Read %04x%04x%04x - %#x\n",
+ cdp->cap2, cdp->cap1, cdp->cap0, cdp->ce,
+ (*sc->read_register)( rp, SONIC_REG_CAP2 ),
+ (*sc->read_register)( rp, SONIC_REG_CAP1 ),
+ (*sc->read_register)( rp, SONIC_REG_CAP0 ),
+ (*sc->read_register)( rp, SONIC_REG_CE ));
+
+ (*sc->write_register)( rp, SONIC_REG_CEP, 0 ); /* Select first entry in CAM */
+ if (((*sc->read_register)( rp, SONIC_REG_CAP2 ) != cdp->cap2)
+ || ((*sc->read_register)( rp, SONIC_REG_CAP1 ) != cdp->cap1)
+ || ((*sc->read_register)( rp, SONIC_REG_CAP0 ) != cdp->cap0)
+ || ((*sc->read_register)( rp, SONIC_REG_CE ) != cdp->ce)) {
+ printf ("Failed to load Ethernet address into SONIC CAM.\n"
+ " Wrote %04x%04x%04x - %#x\n"
+ " Read %04x%04x%04x - %#x\n",
+ cdp->cap2, cdp->cap1, cdp->cap0, cdp->ce,
+ (*sc->read_register)( rp, SONIC_REG_CAP2 ),
+ (*sc->read_register)( rp, SONIC_REG_CAP1 ),
+ (*sc->read_register)( rp, SONIC_REG_CAP0 ),
+ (*sc->read_register)( rp, SONIC_REG_CE ));
+ rtems_panic ("SONIC LCAM");
+ }
+#endif
+
+ (*sc->write_register)(rp, SONIC_REG_CR, /* CR_TXP | */CR_RXEN | CR_STP);
+
+ /*
+ * Attach SONIC interrupt handler
+ */
+/* XXX
+ (*sc->write_register)( rp, SONIC_REG_IMR, 0 );
+*/
+
+ /* Ignore returned old handler */
+ (void) set_vector(sonic_interrupt_handler, sc->vector, 1);
+
+ /*
+ * Remainder of hardware initialization is
+ * done by the receive and transmit daemons.
+ */
+}
+
+/*
+ * Send packet (caller provides header).
+ */
+
+SONIC_STATIC void sonic_start(struct ifnet *ifp)
+{
+ struct sonic_softc *sc = ifp->if_softc;
+
+ rtems_bsdnet_event_send(sc->txDaemonTid, START_TRANSMIT_EVENT);
+ ifp->if_flags |= IFF_OACTIVE;
+}
+
+/*
+ * Initialize and start the device
+ */
+
+SONIC_STATIC void sonic_init (void *arg)
+{
+ struct sonic_softc *sc = arg;
+ struct ifnet *ifp = &sc->arpcom.ac_if;
+ void *rp = sc->sonic;
+ int rcr;
+
+ if (sc->txDaemonTid == 0) {
+
+ /*
+ * Set up SONIC hardware
+ */
+ sonic_initialize_hardware (sc);
+
+ /*
+ * Start driver tasks
+ */
+ sc->rxDaemonTid = rtems_bsdnet_newproc ("SNrx", 4096, sonic_rxDaemon, sc);
+ sc->txDaemonTid = rtems_bsdnet_newproc ("SNtx", 4096, sonic_txDaemon, sc);
+ }
+
+ /*
+ * Set flags appropriately
+ */
+ rcr = (*sc->read_register)( rp, SONIC_REG_RCR );
+ if (ifp->if_flags & IFF_PROMISC)
+ rcr |= RCR_PRO;
+ else
+ rcr &= ~RCR_PRO;
+ (*sc->write_register)( rp, SONIC_REG_RCR, rcr);
+
+ /*
+ * Tell the world that we're running.
+ */
+ ifp->if_flags |= IFF_RUNNING;
+
+ /*
+ * Enable receiver and transmitter
+ */
+ sonic_enable_interrupts( sc, IMR_TXEREN | (IMR_PRXEN | IMR_RBAEEN) );
+ sonic_command( sc, CR_RXEN );
+}
+
+/*
+ * Driver ioctl handler
+ */
+static int
+sonic_ioctl (struct ifnet *ifp, ioctl_command_t command, caddr_t data)
+{
+ struct sonic_softc *sc = ifp->if_softc;
+ int error = 0;
+
+ switch (command) {
+ case SIOCGIFADDR:
+ case SIOCSIFADDR:
+ ether_ioctl (ifp, command, data);
+ break;
+
+ case SIOCSIFFLAGS:
+ switch (ifp->if_flags & (IFF_UP | IFF_RUNNING)) {
+ case IFF_RUNNING:
+ sonic_stop (sc);
+ break;
+
+ case IFF_UP:
+ sonic_init (sc);
+ break;
+
+ case IFF_UP | IFF_RUNNING:
+ sonic_stop (sc);
+ sonic_init (sc);
+ break;
+
+ default:
+ break;
+ }
+ break;
+
+ case SIO_RTEMS_SHOW_STATS:
+ sonic_stats (sc);
+ break;
+
+ /*
+ * FIXME: All sorts of multicast commands need to be added here!
+ */
+ default:
+ error = EINVAL;
+ break;
+ }
+ return error;
+}
+
+/*
+ * Attach an SONIC driver to the system
+ * This is the only `extern' function in the driver.
+ */
+
+int
+rtems_sonic_driver_attach (
+ struct rtems_bsdnet_ifconfig *config,
+ sonic_configuration_t *chip
+)
+{
+ struct sonic_softc *sc;
+ struct ifnet *ifp;
+ int mtu;
+ int unitNumber;
+ char *unitName;
+
+ /*
+ * Parse driver name
+ */
+ if ((unitNumber = rtems_bsdnet_parse_driver_name (config, &unitName)) < 0)
+ return 0;
+
+ /*
+ * Is driver free?
+ */
+ if ((unitNumber <= 0) || (unitNumber > NSONIC)) {
+ printf ("Bad SONIC unit number.\n");
+ return 0;
+ }
+ sc = &sonic_softc[unitNumber - 1];
+ ifp = &sc->arpcom.ac_if;
+ if (ifp->if_softc != NULL) {
+ printf ("Driver already in use.\n");
+ return 0;
+ }
+
+ /*
+ * zero out the control structure
+ */
+
+ memset( sc, 0, sizeof(*sc) );
+
+
+ /*
+ * Process options
+ */
+ if (config->hardware_address) {
+ memcpy (sc->arpcom.ac_enaddr, config->hardware_address, ETHER_ADDR_LEN);
+ }
+ else {
+ memset (sc->arpcom.ac_enaddr, 0x08, ETHER_ADDR_LEN);
+ }
+ if (config->mtu)
+ mtu = config->mtu;
+ else
+ mtu = ETHERMTU;
+ if (config->rbuf_count)
+ sc->rdaCount = config->rbuf_count;
+ else
+ sc->rdaCount = chip->rda_count;
+ if (config->xbuf_count)
+ sc->tdaCount = config->xbuf_count;
+ else
+ sc->tdaCount = chip->tda_count;
+ sc->acceptBroadcast = !config->ignore_broadcast;
+
+ sc->sonic = chip->base_address;
+ sc->vector = chip->vector;
+ sc->dcr_value = chip->dcr_value;
+ sc->dc2_value = chip->dc2_value;
+ sc->write_register = chip->write_register;
+ sc->read_register = chip->read_register;
+
+ /*
+ * Set up network interface values
+ */
+ ifp->if_softc = sc;
+ ifp->if_unit = unitNumber;
+ ifp->if_name = unitName;
+ ifp->if_mtu = mtu;
+ ifp->if_init = sonic_init;
+ ifp->if_ioctl = sonic_ioctl;
+ ifp->if_start = sonic_start;
+ ifp->if_output = ether_output;
+ ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX;
+ if (ifp->if_snd.ifq_maxlen == 0)
+ ifp->if_snd.ifq_maxlen = ifqmaxlen;
+
+ /*
+ * Attach the interface
+ */
+ if_attach (ifp);
+ ether_ifattach (ifp);
+ return 1;
+}
+
+#if (SONIC_DEBUG & SONIC_DEBUG_PRINT_REGISTERS)
+#include <stdio.h>
+
+char SONIC_Reg_name[64][6]= {
+ "CR", /* 0x00 */
+ "DCR", /* 0x01 */
+ "RCR", /* 0x02 */
+ "TCR", /* 0x03 */
+ "IMR", /* 0x04 */
+ "ISR", /* 0x05 */
+ "UTDA", /* 0x06 */
+ "CTDA", /* 0x07 */
+ "0x08", /* 0x08 */
+ "0x09", /* 0x09 */
+ "0x0A", /* 0x0A */
+ "0x0B", /* 0x0B */
+ "0x0C", /* 0x0C */
+ "URDA", /* 0x0D */
+ "CRDA", /* 0x0E */
+ "0x0F", /* 0x0F */
+ "0x10", /* 0x10 */
+ "0x11", /* 0x11 */
+ "0x12", /* 0x12 */
+ "EOBC", /* 0x13 */
+ "URRA", /* 0x14 */
+ "RSA", /* 0x15 */
+ "REA", /* 0x16 */
+ "RRP", /* 0x17 */
+ "RWP", /* 0x18 */
+ "0x19", /* 0x19 */
+ "0x1A", /* 0x1A */
+ "0x1B", /* 0x1B */
+ "0x1C", /* 0x1C */
+ "0x0D", /* 0x1D */
+ "0x1E", /* 0x1E */
+ "0x1F", /* 0x1F */
+ "0x20", /* 0x20 */
+ "CEP", /* 0x21 */
+ "CAP2", /* 0x22 */
+ "CAP1", /* 0x23 */
+ "CAP0", /* 0x24 */
+ "CE", /* 0x25 */
+ "CDP", /* 0x26 */
+ "CDC", /* 0x27 */
+ "SR", /* 0x28 */
+ "WT0", /* 0x29 */
+ "WT1", /* 0x2A */
+ "RSC", /* 0x2B */
+ "CRCT", /* 0x2C */
+ "FAET", /* 0x2D */
+ "MPT", /* 0x2E */
+ "MDT", /* 0x2F */
+ "0x30", /* 0x30 */
+ "0x31", /* 0x31 */
+ "0x32", /* 0x32 */
+ "0x33", /* 0x33 */
+ "0x34", /* 0x34 */
+ "0x35", /* 0x35 */
+ "0x36", /* 0x36 */
+ "0x37", /* 0x37 */
+ "0x38", /* 0x38 */
+ "0x39", /* 0x39 */
+ "0x3A", /* 0x3A */
+ "0x3B", /* 0x3B */
+ "0x3C", /* 0x3C */
+ "0x3D", /* 0x3D */
+ "0x3E", /* 0x3E */
+ "DCR2" /* 0x3F */
+};
+#endif
diff --git a/bsps/shared/shared-sources.am b/bsps/shared/shared-sources.am
index 854710d804..d3503ddc01 100644
--- a/bsps/shared/shared-sources.am
+++ b/bsps/shared/shared-sources.am
@@ -1 +1,53 @@
+libbsp_a_SOURCES += ../../../../../../bsps/shared/dev/display/disp_hcms29xx.c
+libbsp_a_SOURCES += ../../../../../../bsps/shared/dev/display/font_hcms29xx.c
+libbsp_a_SOURCES += ../../../../../../bsps/shared/dev/flash/am29lv160.c
+libbsp_a_SOURCES += ../../../../../../bsps/shared/dev/i2c/i2c-2b-eeprom.c
+libbsp_a_SOURCES += ../../../../../../bsps/shared/dev/i2c/i2c-ds1621.c
+libbsp_a_SOURCES += ../../../../../../bsps/shared/dev/i2c/i2c-sc620.c
+libbsp_a_SOURCES += ../../../../../../bsps/shared/dev/i2c/spi-flash-m25p40.c
+libbsp_a_SOURCES += ../../../../../../bsps/shared/dev/i2c/spi-fram-fm25l256.c
+libbsp_a_SOURCES += ../../../../../../bsps/shared/dev/i2c/spi-memdrv.c
+libbsp_a_SOURCES += ../../../../../../bsps/shared/dev/i2c/spi-sd-card.c
+libbsp_a_SOURCES += ../../../../../../bsps/shared/dev/ide/ata.c
+libbsp_a_SOURCES += ../../../../../../bsps/shared/dev/ide/ata_util.c
+libbsp_a_SOURCES += ../../../../../../bsps/shared/dev/ide/ide_controller.c
+libbsp_a_SOURCES += ../../../../../../bsps/shared/dev/rtc/ds1375.c
+libbsp_a_SOURCES += ../../../../../../bsps/shared/dev/rtc/icm7170.c
+libbsp_a_SOURCES += ../../../../../../bsps/shared/dev/rtc/icm7170_reg2.c
+libbsp_a_SOURCES += ../../../../../../bsps/shared/dev/rtc/icm7170_reg4.c
+libbsp_a_SOURCES += ../../../../../../bsps/shared/dev/rtc/icm7170_reg8.c
+libbsp_a_SOURCES += ../../../../../../bsps/shared/dev/rtc/icm7170_reg.c
+libbsp_a_SOURCES += ../../../../../../bsps/shared/dev/rtc/m48t08.c
+libbsp_a_SOURCES += ../../../../../../bsps/shared/dev/rtc/m48t08_reg2.c
+libbsp_a_SOURCES += ../../../../../../bsps/shared/dev/rtc/m48t08_reg4.c
+libbsp_a_SOURCES += ../../../../../../bsps/shared/dev/rtc/m48t08_reg8.c
+libbsp_a_SOURCES += ../../../../../../bsps/shared/dev/rtc/m48t08_reg.c
+libbsp_a_SOURCES += ../../../../../../bsps/shared/dev/rtc/mc146818a.c
+libbsp_a_SOURCES += ../../../../../../bsps/shared/dev/rtc/mc146818a_ioreg.c
+libbsp_a_SOURCES += ../../../../../../bsps/shared/dev/rtc/rtcprobe.c
+libbsp_a_SOURCES += ../../../../../../bsps/shared/dev/serial/mc68681_baud.c
+libbsp_a_SOURCES += ../../../../../../bsps/shared/dev/serial/mc68681.c
+libbsp_a_SOURCES += ../../../../../../bsps/shared/dev/serial/mc68681_reg2.c
+libbsp_a_SOURCES += ../../../../../../bsps/shared/dev/serial/mc68681_reg4.c
+libbsp_a_SOURCES += ../../../../../../bsps/shared/dev/serial/mc68681_reg8.c
+libbsp_a_SOURCES += ../../../../../../bsps/shared/dev/serial/mc68681_reg.c
+libbsp_a_SOURCES += ../../../../../../bsps/shared/dev/serial/ns16550.c
+libbsp_a_SOURCES += ../../../../../../bsps/shared/dev/serial/ns16550-context.c
+libbsp_a_SOURCES += ../../../../../../bsps/shared/dev/serial/serprobe.c
+libbsp_a_SOURCES += ../../../../../../bsps/shared/dev/serial/z85c30.c
+libbsp_a_SOURCES += ../../../../../../bsps/shared/dev/serial/z85c30_reg.c
+if HAS_NETWORKING
+libbsp_a_SOURCES += ../../../../../../bsps/shared/net/cs8900.c
+libbsp_a_SOURCES += ../../../../../../bsps/shared/net/dec21140.c
+libbsp_a_SOURCES += ../../../../../../bsps/shared/net/elnk.c
+if !HAS_SMP
+libbsp_a_SOURCES += ../../../../../../bsps/shared/net/greth2.c
+endif
+libbsp_a_SOURCES += ../../../../../../bsps/shared/net/i82586.c
+libbsp_a_SOURCES += ../../../../../../bsps/shared/net/if_dc.c
+libbsp_a_SOURCES += ../../../../../../bsps/shared/net/if_fxp.c
+libbsp_a_SOURCES += ../../../../../../bsps/shared/net/open_eth.c
+libbsp_a_SOURCES += ../../../../../../bsps/shared/net/smc91111.c
+libbsp_a_SOURCES += ../../../../../../bsps/shared/net/sonic.c
+endif
libbsp_a_SOURCES += ../../../../../../bsps/shared/rtems-version.c