diff options
author | Sebastian Huber <sebastian.huber@embedded-brains.de> | 2018-04-03 07:20:11 +0200 |
---|---|---|
committer | Sebastian Huber <sebastian.huber@embedded-brains.de> | 2018-04-04 10:13:28 +0200 |
commit | 27de4e1fb8bcdbdd8cb882fc0d7a2c152b4e027a (patch) | |
tree | def0664dcddc53fd5d599b455c64f76ca2293606 /bsps | |
parent | bsps: Move config macros to RTEMS_BSP_CONFIGURE (diff) | |
download | rtems-27de4e1fb8bcdbdd8cb882fc0d7a2c152b4e027a.tar.bz2 |
bsps: Move libchip to bsps
This patch is a part of the BSP source reorganization.
Update #3285.
Diffstat (limited to 'bsps')
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, ®); + 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, ®); + 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)ðerbroadcastaddr); + 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 |