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/shared/dev | |
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/shared/dev')
54 files changed, 13521 insertions, 0 deletions
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); +} |