bsps: Move libchip to bsps

This patch is a part of the BSP source reorganization.

Update #3285.
This commit is contained in:
Sebastian Huber
2018-04-03 07:20:11 +02:00
parent 8621ed3806
commit 27de4e1fb8
83 changed files with 55 additions and 91 deletions

View File

@@ -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 */

View File

@@ -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;

File diff suppressed because it is too large Load Diff

View File

@@ -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 */

View File

@@ -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
};

View File

@@ -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;

View File

@@ -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;

View File

@@ -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)
};

View File

@@ -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;

View File

@@ -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;

View File

@@ -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,
};

File diff suppressed because it is too large Load Diff

1360
bsps/shared/dev/ide/ata.c Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -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;
}

View File

@@ -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);
}

View File

@@ -0,0 +1,3 @@
The Mostek M48T08 is compatible with the Dallas Semiconductor DS1643. Please
use that driver.

View File

@@ -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.

View File

@@ -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.

View File

@@ -0,0 +1 @@
This is supported by the m48t08 driver.

View File

@@ -0,0 +1 @@
This is supported by the mc146818a driver.

View File

@@ -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.

View File

@@ -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,
};

View File

@@ -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
};

View File

@@ -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;
}

View File

@@ -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"

View File

@@ -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"

View File

@@ -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"

View File

@@ -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
};

View File

@@ -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;
}

View File

@@ -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"

View File

@@ -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"

View File

@@ -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"

View File

@@ -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
};

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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.

View File

@@ -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.

View File

@@ -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.

View File

@@ -0,0 +1,2 @@
The Exar XR88681 is an enhanced version of the Motorola MC68681 and is
supported by the mc68681 driver.

View File

@@ -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.

View File

@@ -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.

View File

@@ -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)
);
}

View File

@@ -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 */
},
};

View File

@@ -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_ */

View File

@@ -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;
}

View File

@@ -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"

View File

@@ -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"

View File

@@ -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"

View File

@@ -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
};

View File

@@ -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 );
}

View File

@@ -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;
}

View File

@@ -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 );
}

View File

@@ -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

View File

@@ -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);
}