1
1
Fork 0

Драйвер devc-serexar для ЗОСРВ Нейтрино редакции 2020 (основано на исх. коде редакции 2021+)

This commit is contained in:
parent 23ee7029b9
commit 30e8332d7c
19 changed files with 1532 additions and 2 deletions

View File

@ -19,8 +19,9 @@
## Дерево исходных кодов
```
|- devc/ser8250/
| |- *.* - Исходный код драйвера последовательных 8250-совместимых устройств
|- devc/
| |- ser8250/ - Исходный код драйвера последовательных 8250-совместимых устройств
| |- serexar/ - Исходный код драйвера последовательных устройств, совместимых с контроллером Exar XR17V354/358
| |- common.mk - Параметры сборки драйверов
| `- Makefile - Правила сборки дерева исходников
|

2
devc/serexar/Makefile Normal file
View File

@ -0,0 +1,2 @@
LIST=CPU
include recurse.mk

91
devc/serexar/exar.c Normal file
View File

@ -0,0 +1,91 @@
/*
* (c) 2022, SWD Embedded Systems Limited, http://www.kpda.ru
*/
#include <stdio.h>
#include <sys/mman.h>
#include <gulliver.h>
#include "externs.h"
int exar_dev_init( struct pci_dev_info *dev, exar_dev_t *exdev )
{
/* Disable PCIe Active-state power managment */
if ( exdev->disable_aspm )
{
uint8_t byte = 0;
pci_write_config8( dev->BusNumber, dev->DevFunc, 0x90, 1, &byte );
}
if ( (dev->Class != UART_DEV_CLASS) && (dev->Class != EXAR_DEV_CLASS) )
{
devc_err( "Warning: incorrect device type. Class code: 0x%X instead of expected 0x%X or 0x%X\n",
dev->Class, UART_DEV_CLASS, EXAR_DEV_CLASS );
/* return (-1); */
}
exdev->pci_dev = dev;
if ( (exdev->uart_mem = mmap_device_memory( NULL, exdev->pci_dev->BaseAddressSize[0],
PROT_READ | PROT_WRITE | PROT_NOCACHE, 0,
PCI_MEM_ADDR( exdev->pci_dev->CpuBaseAddress[0] ))) == MAP_FAILED )
{
devc_err( "Cannot map global uart region" );
return (-1);
}
if ( exdev->uart_cnt < 0 )
{
if ( (dev->VendorId == EXAR_VID) && (dev->DeviceId == EXAR_XR17D154_DID) )
exdev->uart_cnt = 4;
else if ( (dev->VendorId == EXAR_VID) && (dev->DeviceId == EXAR_XR17D158_DID) )
exdev->uart_cnt = 8;
else if ( (dev->VendorId == ADVANTECH_VID) && (dev->DeviceId == ADVANTECH_PCI1612_DID_B || dev->DeviceId == ADVANTECH_PCI1612_DID_1B) )
exdev->uart_cnt = 4;
else
exdev->uart_cnt = UART_DEFAULT_CH_COUNT;
}
uint32_t imi = exar_read32( exdev, EXAR_CONFIG_REG_BASE + UART_CONFIG_REG_ID_MPIO_INT_OFFSET );
devc_dbg( "Exar device attached. DVID: 0x%X , Revision: 0x%X", UART_GET_DVID( imi ), UART_GET_REVISION( imi ) );
return (0);
}
uint32_t exar_read32( exar_dev_t *dev, uintptr_t offset )
{
return *((uint32_t *)(dev->uart_mem + offset));
}
uint8_t exar_read8( exar_dev_t *dev, uintptr_t offset )
{
return *((uint8_t *)(dev->uart_mem + offset));
}
void exar_write32( exar_dev_t *dev, uintptr_t offset, uint32_t val )
{
*(uint32_t *)(dev->uart_mem + offset) = val;
}
void exar_write8( exar_dev_t *dev, uintptr_t offset, uint8_t val )
{
*(uint8_t *)(dev->uart_mem + offset) = val;
}
uint64_t exar_read64( exar_dev_t *dev, uintptr_t offset )
{
return *((uint64_t *)(dev->uart_mem + offset));
}
void exar_write64( exar_dev_t *dev, uintptr_t offset, uint64_t val )
{
*(uint64_t *)(dev->uart_mem + offset) = val;
}

98
devc/serexar/exar.h Normal file
View File

@ -0,0 +1,98 @@
/*
* (c) 2022, SWD Embedded Systems Limited, http://www.kpda.ru
*/
#ifndef __EXAR_X17_D154_H__
#define __EXAR_X17_D154_H__
#include <hw/pci.h>
#define EXAR_VID 0x13a8
#define EXAR_XR17D154_DID 0x354
#define EXAR_XR17D158_DID 0x358
#define ADVANTECH_VID 0x13fe
#define ADVANTECH_PCI1612_DID_B 0xb
#define ADVANTECH_PCI1612_DID_1B 0x1b
#define EXAR_FIFO_RX_SIZE 192
#define EXAR_FIFO_TX_SIZE 128
#define EXAR_UART_CHANNEL_SIZE 0x400
#define EXAR_UART_CHANNEL_OFFSET 0
#define EXAR_UART_CHANNEL_BASE( uart_num ) ((uart_num) * 0x400)
#define EXAR_CONFIG_REG_BASE 0x80
#define UART_READ_FIFO_BASE( uart_num ) (EXAR_UART_CHANNEL_BASE( uart_num ) + 0x100)
#define UART_WRITE_FIFO_BASE( uart_num ) (UART_READ_FIFO_BASE( uart_num ))
#define UART_ERROR_FIFO_BASE( uart_num ) (EXAR_UART_CHANNEL_BASE( uart_num ) + 0x200)
#define UART_CONFIG_REG_INT_OFFSET 0x0
#define UART_CONFIG_REG_INT_GET_CH( regval, chnum ) (((regval) >> (chnum * 3)) & 0x7)
#define UART_DEFAULT_CH_COUNT 4
#define UART_MAX_CH_COUNT 8
#define UART_INT_NONE 0
#define UART_INT_RXRDY 1
#define UART_INT_RXRDY_TO 2
#define UART_INT_RXRDY_THR 3
#define UART_INT_RXRDY_MSR 4
#define UART_INT_MPIO_PINS 6
#define UART_INT_TIMER_TO 7
#define UART_CONFIG_REG_TIMERCTL_OFFSET 0x4
#define UART_TIMER_EN 0x1
#define UART_TIMER_STOP_START (0x1 << 1)
#define UART_TIMER_RETRIGGER_EN (0x1 << 2)
#define UART_TIMER_EXT_SRC (0x1 << 3)
#define UART_TIMER_INT_TO_MPIO (0x1 << 4)
#define UART_TIMER_GET_CNTR( regval ) ((val) >> 16)
#define UART_TIMER_SET_CNTR( regval, val ) (((regval) & ~(0xFFFF << 16)) | ((val) << 16))
#define UART_CONFIG_REG_OPTIONS_OFFSET 0x8
#define UART_8X_ENABLE( chnum ) ((1 << chnum) << 16)
#define UART_RESET( chnum ) ((1 << (chnum)) << 16)
#define UART_SLEEP( chnum ) ((1 << (chnum)) << 24)
#define UART_CONFIG_REG_ID_MPIO_INT_OFFSET 0xC
#define UART_GET_REVISION( regval ) (regval & 0xFF)
#define UART_GET_DVID( regval ) (((regval) >> 8) & 0xFF)
#define UART_GET_REGB( regval ) (((regval) >> 16) & 0xFFFF)
#define UART_MPIO_INT( mpionum ) ((1 << (mpionum)) << 24)
#define UART_CONFIG_REG_MPIO_OFFSET 0x10
#define UART_MPIO_LVL( mpionum ) (1 << mpionum)
#define UART_MPIO3T( mpionum ) ((1 << mpionum) << 8)
#define UART_MPIOINV( mpionum ) ((1 << mpionum) << 16)
#define UART_MPIOSEL( mpionum ) ((1 << mpionum) << 24)
#define UART_DEV_CLASS (0x70002)
#define EXAR_DEV_CLASS (0x70200)
typedef enum {
EXAR_MODE_RS232,
EXAR_MODE_RS485
} exar_mode;
typedef struct {
void *pci_dev_handle;
struct pci_dev_info *pci_dev;
uint32_t caps;
uint8_t *uart_mem;
int uart_cnt;
int ch_offset;
int disable_aspm;
int rs485_invert_polarity;
exar_mode mode[UART_MAX_CH_COUNT + 1];
} exar_dev_t;
int exar_dev_init( struct pci_dev_info *dev, exar_dev_t *exdev );
uint32_t exar_read32( exar_dev_t *exdev, uintptr_t offset );
uint8_t exar_read8( exar_dev_t *dev, uintptr_t offset );
void exar_write8( exar_dev_t *dev, uintptr_t offset, uint8_t val );
void exar_write32( exar_dev_t *exdev, uintptr_t offset, uint32_t val );
uint64_t exar_read64( exar_dev_t *exdev, uintptr_t offset );
void exar_write64( exar_dev_t *exdev, uintptr_t offset, uint64_t val );
#endif /* __EXAR_X17_D154_H__ */

54
devc/serexar/externs.c Normal file
View File

@ -0,0 +1,54 @@
/*
* (c) 2022, SWD Embedded Systems Limited, http://www.kpda.ru
*/
#define DEFN
#include "externs.h"
#include <stdarg.h>
exar_dev_t exar_dev;
int driver_verbosity_level;
int devc_slogf( int verbosity_level, const char *fmt, ... )
{
int status = 0;
va_list arg;
const char *infostr = "INFO";
int severity = _SLOG_INFO;
switch ( verbosity_level )
{
case 0:
infostr = "ERROR";
severity = _SLOG_ERROR;
break;
case 1:
infostr = "WARNING";
severity = _SLOG_WARNING;
break;
case 3:
infostr = "DEBUG";
severity = _SLOG_DEBUG1;
break;
default:
infostr = "INFO";
severity = _SLOG_INFO;
break;
}
if ( driver_verbosity_level < verbosity_level )
return (0);
va_start( arg, fmt );
status += fprintf( stderr, "\t %s : ", infostr );
status = vfprintf( stderr, fmt, arg );
vslogf( DEVC_EXAR_SLOG_OPCODE, severity, fmt, arg );
status += fprintf( stderr, "\n" );
va_end( arg );
return status;
}

132
devc/serexar/externs.h Normal file
View File

@ -0,0 +1,132 @@
/*
* (c) 2022, SWD Embedded Systems Limited, http://www.kpda.ru
*/
#ifdef DEFN
#define EXT
#define INIT1( a ) = { a }
#else
#define EXT extern
#define INIT1(a)
#endif
#include <stdio.h>
#include <errno.h>
#include <signal.h>
#include <malloc.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/neutrino.h>
#include <termios.h>
#include <devctl.h>
#include <sys/dcmd_chr.h>
#include <sys/iomsg.h>
#include <atomic.h>
#include <hw/inout.h>
#include <hw/8250.h>
#include <sys/io-char.h>
#include <sys/slog.h>
#include <sys/slogcodes.h>
#include <variant.h>
#include "exar.h"
extern exar_dev_t exar_dev;
#ifndef DEV_8250 /* Can be defined in variant.h to override default */
typedef struct dev_8250 {
TTYDEV tty;
struct dev_8250 *next;
unsigned intr;
unsigned clk;
unsigned div;
unsigned char rx_fifo; /* rx fifo size */
unsigned char tx_fifo; /* tx fifo size */
unsigned dtr_disable; /* Disable DTR control. Workaround for Qt QtSerialPort */
uintptr_t port[0x100];
int chnum;
exar_dev_t *parent_dev;
} DEV_8250;
#endif
struct dev_list {
struct dev_list *next;
DEV_8250 *device;
int iid;
};
EXT TTYCTRL ttyctrl;
EXT struct dev_list *devices;
#define MAX_DEVICES 16
#define FIFO_SIZE 256
#include "proto.h"
#ifndef IE_SET_ALL
#define IE_SET_ALL (IE_RX | IE_TX | IE_LS | IE_MS)
#endif
#ifndef IE_CLR_ALL
#define IE_CLR_ALL 0x0
#endif
extern int driver_verbosity_level;
#define DEVC_EXAR_SLOG_OPCODE _SLOG_SETCODE( _SLOGC_CHAR, 0 )
int devc_slogf(int verbosity_level, const char *fmt, ...);
#define devc_dbg( ... ) devc_slogf( 3, __VA_ARGS__ )
#define devc_info( ... ) devc_slogf( 2, __VA_ARGS__ )
#define devc_warn( ... ) devc_slogf( 1, __VA_ARGS__ )
#define devc_err( ... ) devc_slogf( 0, __VA_ARGS__ )
#define REG_DLD 2
#define REG_DLD_INVERT_RS485_POLARITY 0x80
#define REG_SPR 7
#define REG_FCTR 8
#define REG_FCTR_INVERT_IR_OFFSET 4
#define REG_FCTR_INVERT_IR_MASK 1
#define REG_FCTR_AUTO_RS485_OFFSET 5
#define REG_FCTR_AUTO_RS485_MASK 1
#define REG_FCTR_TRG_TABLE_OFFSET 6
#define REG_FCTR_TRG_TABLE_MASK 0x3
#define REG_FCTR_RTS_DTR_HYST_OFFSET 0
#define REG_FCTR_RTS_DTR_HYST_MASK 0xF
#define REG_EFR 9
#define REG_EFR_ENABLE_SHARED_BITS 0x10
#define REG_TXCNT 10
#define REG_TXTRG 10
#define REG_RXCNT 11
#define REG_RXTRG 11
#define REG_XOFF1 12
#define REG_XCHAR 12
#define REG_XOFF2 13
#define REG_XON1 14
#define REG_XON2 15
#define FIELD_VAL( var, bitname ) ((var >> bitname ## _OFFSET) & bitname ## _MASK)
#define FIELD_SET( val, fieldname ) ((val & fieldname ## _MASK) << fieldname ## _OFFSET)
#define REG_EX_INT0 0x80
#define REG_EX_INT1 0x81
#define REG_EX_INT2 0x82
#define REG_EX_INT3 0x83
#define REG_EX_TIMERCTL 0x84
#define REG_EX_REGA 0x85
#define REG_EX_TIMERLSB 0x86
#define REG_EX_TIMERMSB 0x87
#define REG_EX_8XMODE 0x88
#define REG_EX_4XMODE 0x89
#define REG_EX_RESET 0x8A
#define REG_EX_SLEEP 0x8B
#define REG_EX_DREV 0x8C
#define REG_EX_DVID 0x8D
#define REG_EX_REGB 0x8E

418
devc/serexar/init.c Normal file
View File

@ -0,0 +1,418 @@
/*
* (c) 2022, SWD Embedded Systems Limited, http://www.kpda.ru
*/
#include "externs.h"
#include <sys/mman.h>
void set_port( unsigned port, unsigned mask, unsigned data )
{
unsigned char c;
c = read_8250( port );
write_8250( port, (c & ~mask) | (data & mask) );
}
static void clear_device( const uintptr_t *port )
{
unsigned char tmp __attribute__(( unused ));
write_8250( port[REG_IE], IE_CLR_ALL ); /* Disable all interrupts */
tmp = read_8250( port[REG_LS] ); /* Clear Line Status Interrupt */
tmp = read_8250( port[REG_RX] ); /* Clear RX Interrupt */
tmp = read_8250( port[REG_TX] ); /* Clear TX Interrupt */
tmp = read_8250( port[REG_MS] ); /* Clear Modem Interrupt */
write_8250( port[REG_FC], 0x00 ); /* Disable and reset FIFOs */
}
/* Clean up the device then add it to the interrupt list and enable it. */
int ser_attach_intr( DEV_8250 *dev )
{
uintptr_t *port = dev->port;
struct dev_list **owner;
struct dev_list *curr;
/* According to the National bug sheet you must wait for the transmit holding register to be empty. */
devc_dbg( "Wait TX empty" );
do {
} while ((read_8250( port[REG_LS] ) & LSR_TXRDY) == 0);
/* Clean the device so we get a level change on the intr line to the bus. Enable out2 (gate intr to bus) */
set_port( port[REG_MC], MCR_OUT2, MCR_OUT2 );
clear_device( port );
/* Add it to the interrupt list */
owner = &devices;
for ( ;; )
{
curr = *owner;
if ( curr == NULL )
{
curr = malloc( sizeof( *curr ) );
if ( curr == NULL )
{
devc_err( "io-char: Allocation of device interrupt entry failed (%d)", errno );
return (-1);
}
*owner = curr;
curr->next = NULL;
curr->device = NULL;
break;
}
if ( curr->device->intr == dev->intr )
break;
owner = &curr->next;
}
/* Delay interrupts while we're fiddling around with the list */
InterruptMask( dev->intr, -1 );
dev->next = curr->device;
curr->device = dev;
InterruptUnmask( dev->intr, -1 );
/* If first handler, attach the interrupt */
if ( curr->device->next == NULL )
curr->iid = InterruptAttach( dev->intr, ser_intr, curr, 0, 0 );
/* Enable ALL interrupt sources */
if ( dev->tty.c_cflag & CREAD )
write_8250( port[REG_IE], IE_SET_ALL );
else
write_8250( port[REG_IE], (IE_SET_ALL & ~IE_RX) );
devc_dbg( "Interrupt handler attached successfully" );
return (EOK);
}
void ser_detach_intr( DEV_8250 *dev )
{
struct dev_list *curr_list,
*prev_list;
DEV_8250 *curr_dev,
*prev_dev;
uintptr_t *port = dev->port;
/* Disable ALL interrupt sources */
write_8250( port[REG_IE], IE_CLR_ALL ); /* Disable interrupts */
set_port( port[REG_MC], MCR_OUT2, 0x00 ); /* Disable out2 */
/* Remove from list of devices to scan on an interrupt */
/* Delay interrupts while we're fiddling around with the list */
InterruptMask( dev->intr, -1 );
/* Find the right interrupt list */
curr_list = prev_list = devices;
while ( curr_list )
{
if ( curr_list->device->intr == dev->intr )
{
/* Now that we found the right interrupt list find and remove the device from the
* device list (but don't free it) */
prev_dev = curr_dev = curr_list->device;
while ( curr_dev )
{
if ( curr_dev == dev )
{
if ( curr_dev == curr_list->device ) /* If first entry in device list */
{
curr_list->device = curr_list->device->next;
if ( curr_list->device == NULL ) /* No more devices in list */
{
if ( curr_list == devices ) /* If first list in list of lists :-) */
devices = devices->next;
else
prev_list->next = curr_list->next;
InterruptDetach( curr_list->iid );
free( curr_list ); /* Free empty list */
curr_list = NULL;
}
} else
prev_dev->next = curr_dev->next;
curr_dev = NULL;
curr_list = NULL; /* We found what we were looking for, now bail out of both loops */
} else {
if ( prev_dev != curr_dev )
prev_dev = prev_dev->next;
curr_dev = curr_dev->next;
}
}
} else {
if ( prev_list != curr_list )
prev_list = prev_list->next;
curr_list = curr_list->next;
}
}
clear_device( dev->port );
InterruptUnmask( dev->intr, -1 );
dev->intr = _NTO_INTR_SPARE;
return;
}
DEV_8250 * create_device( TTYINIT *dip, unsigned unit, exar_dev_t *exdev )
{
DEV_8250 *dev;
unsigned i;
uintptr_t port;
/* Get a device entry and the input/output buffers for it */
if ( (dev = (void *)malloc( sizeof( *dev ) )) == NULL )
{
devc_err( "io-char: Allocation of device entry failed (%d)", errno );
return (dev);
}
memset( dev, 0, sizeof( *dev ) );
/* Get buffers */
dev->tty.ibuf.size = dip->isize;
dev->tty.ibuf.head = dev->tty.ibuf.tail = dev->tty.ibuf.buff = malloc( dev->tty.ibuf.size );
if ( dev->tty.ibuf.head == NULL )
{
devc_err( "io-char: Allocation of input buffer failed (%d)", errno );
free( dev );
return (NULL);
}
dev->tty.obuf.size = dip->osize;
dev->tty.obuf.head = dev->tty.obuf.tail = dev->tty.obuf.buff = malloc( dev->tty.obuf.size );
if ( dev->tty.obuf.head == NULL )
{
devc_err( "io-char: Allocation of output buffer failed (%d)", errno );
free( dev->tty.ibuf.head );
free( dev );
return (NULL);
}
dev->tty.cbuf.size = dip->csize;
dev->tty.cbuf.head = dev->tty.cbuf.tail = dev->tty.cbuf.buff = malloc( dev->tty.cbuf.size );
if ( dev->tty.cbuf.head == NULL )
{
devc_err( "io-char: Allocation of canonical buffer failed (%d)", errno );
free( dev->tty.ibuf.head );
free( dev->tty.obuf.head );
free( dev );
return (NULL);
}
if ( dip->highwater )
dev->tty.highwater = dip->highwater;
else
dev->tty.highwater = dev->tty.ibuf.size - FIFO_SIZE * 2;
if ( dev->tty.highwater <= 0 )
return (NULL);
strcpy( dev->tty.name, dip->name );
dev->chnum = unit;
if ( exar_dev.ch_offset < 0 )
exar_dev.ch_offset = unit - 1;
dev->tty.baud = dip->baud;
dev->tty.fifo = dip->fifo;
#if defined( __X86__ )
port = (uintptr_t)mmap_device_memory( NULL, (sizeof( dev->port ) / sizeof( dev->port[0] )) << dip->port_shift,
PROT_READ | PROT_WRITE | PROT_NOCACHE, 0, dip->port );
#else
port = mmap_device_io( (sizeof( dev->port ) / sizeof( dev->port[0] )) << dip->port_shift, dip->port );
#endif
for ( i = 0; i < sizeof( dev->port ) / sizeof( dev->port[0] ); ++i )
{
dev->port[i] = port;
port += 1 << dip->port_shift;
}
write_8250( dev->port[REG_EX_RESET], 0xFF );
dev->intr = dip->intr;
dev->clk = dip->clk;
dev->div = dip->div;
dev->tty.flags = EDIT_INSERT | LOSES_TX_INTR;
dev->tty.c_cflag = dip->c_cflag;
dev->tty.c_iflag = dip->c_iflag;
dev->tty.c_lflag = dip->c_lflag;
dev->tty.c_oflag = dip->c_oflag;
dev->tty.verbose = dip->verbose;
/* Initialize termios cc codes to an ANSI terminal */
ttc( TTC_INIT_CC, &dev->tty, 0 );
/* Initialize the device's name. Assume that the basename is set in device name. This will
* attach to the path assigned by the unit number/minor number combination */
unit = SET_NAME_NUMBER( unit ) | NUMBER_DEV_FROM_USER;
ttc( TTC_INIT_TTYNAME, &dev->tty, unit );
/* Initialize power management structures before attaching ISR */
ttc( TTC_INIT_POWER, &dev->tty, 0 );
/* Disable Interrupts on port. Interrupts must be disbled on chip for all ports on a multiport card
* before attaching an ISR to any of the ports. enable_device() must be called for each port before
* returning from options() to complete port initialization */
clear_device( dev->port );
dev->parent_dev = exdev;
return (dev);
}
void free_device( DEV_8250 *dev )
{
free( dev->tty.ibuf.head );
free( dev->tty.obuf.head );
free( dev->tty.cbuf.head );
ser_detach_intr( dev );
free( dev );
}
void enable_exar_ch( DEV_8250 *dev )
{
uint8_t fctr = read_8250( dev->port[REG_FCTR] );
devc_dbg( "[CH%d] RTS/DTS : 0x%lX , TRG Table: 0x%X , SPR : 0x%X", dev->chnum,
(fctr & 0xF), ((fctr >> 6) & 0x3), read_8250( dev->port[REG_SPR] ) );
devc_dbg( "[CH%d] Invert IR input %s , Auto RS485 %s", dev->chnum,
((FIELD_VAL( fctr, REG_FCTR_INVERT_IR )) ? "enable" : "disable"),
((FIELD_VAL( fctr , REG_FCTR_AUTO_RS485 )) ? "enable" : "disable") );
uint8_t efr = read_8250( dev->port[REG_EFR] );
devc_dbg( "[CH%d] Software Flow cntl : 0x%X, Spec char select %s ", dev->chnum,
(efr & 0xf), (((efr >> 5) & 1) ? "enable" : "disable") );
devc_dbg( "[CH%d] Auto RTS/DTR %s , Auto CTS/DSR %s", dev->chnum,
(((efr >> 6) & 1) ? "enable" : "disable"),
(((efr >> 7) & 1) ? "enable" : "disable") );
devc_dbg( "[CH%d] RX cntr : %d , TX cntr : %d", dev->chnum, read_8250( dev->port[REG_RXCNT] ),
read_8250( dev->port[REG_TXCNT] ) );
/* Restore default state */
write_8250( dev->port[REG_SPR], 0xFF );
write_8250( dev->port[REG_FCTR], 0 );
write_8250( dev->port[REG_EFR], 0 );
devc_dbg( "[CH%d] INT regs : 0x%X 0x%X 0x%X 0x%X", dev->chnum, read_8250( dev->port[REG_EX_INT0] ),
read_8250( dev->port[REG_EX_INT1] ), read_8250( dev->port[REG_EX_INT2] ),
read_8250( dev->port[REG_EX_INT3] ) );
devc_dbg( "[CH%d] 8xMode 0x%X 4xMode 0x%X", dev->chnum, read_8250( dev->port[REG_EX_8XMODE] ),
read_8250( dev->port[REG_EX_4XMODE] ) );
devc_dbg( "[CH%d] SLEEP 0x%X", dev->chnum, read_8250( dev->port[REG_EX_SLEEP] ) );
devc_dbg( "[CH%d] DREV 0x%X DVID 0x%X", dev->chnum, read_8250( dev->port[REG_EX_DREV] ),
read_8250( dev->port[REG_EX_DVID] ) );
/* write_8250( dev->port[REG_EX_SLEEP], 0 ); */
/* write_8250( dev->port[REG_EX_4XMODE], 0 ); */
/* write_8250( dev->port[REG_EX_8XMODE], 0 ); */
/* set_port( dev->port[REG_EFR], 1 << 4, 1 << 4 ); */
/* set_port( dev->port[REG_IE], 1 << 6, 1 << 7 ); */
/* set_port( dev->port[REG_MC], MCR_OUT1, MCR_OUT1 ); */
devc_info( "[CH%d] Set device mode %s", dev->chnum,
(exar_dev.mode[dev->chnum - exar_dev.ch_offset] == EXAR_MODE_RS232) ? "RS232" : "RS485" );
fctr = FIELD_SET( 3, REG_FCTR_RTS_DTR_HYST ) | FIELD_SET( 3, REG_FCTR_TRG_TABLE );
if ( exar_dev.mode[dev->chnum - exar_dev.ch_offset] == EXAR_MODE_RS485 )
fctr |= 1 << REG_FCTR_AUTO_RS485_OFFSET;
write_8250( dev->port[REG_FCTR], fctr );
if ( exar_dev.mode[dev->chnum - exar_dev.ch_offset] == EXAR_MODE_RS485 && exar_dev.rs485_invert_polarity )
{
devc_info( "[CH%d] Invert RS485 Polarity", dev->chnum );
set_port( dev->port[REG_LC], LCR_DLAB, LCR_DLAB );
set_port( dev->port[REG_DLD], REG_DLD_INVERT_RS485_POLARITY, REG_DLD_INVERT_RS485_POLARITY );
set_port( dev->port[REG_EFR], REG_EFR_ENABLE_SHARED_BITS, REG_EFR_ENABLE_SHARED_BITS );
set_port( dev->port[REG_LC], LCR_DLAB, 0 );
}
}
int enable_device( DEV_8250 *dev )
{
unsigned char reg = 0;
/* Only setup IRQ handler for non-pcmcia devices. Pcmcia devices will have this done later when card is inserted. */
if(dev->port != 0 && dev->intr != _NTO_INTR_SPARE)
{
/* If CREAD is set (default) then we must clear it before we call ser_stty(), else ser_stty() will
* enable the receive interrupt before we attach our ISR. ser_attach_intr() will enable all the
* interrupts after the ISR is attached. */
if ( dev->tty.c_cflag & CREAD )
{
dev->tty.c_cflag &= ~(CREAD);
ser_stty( dev );
dev->tty.c_cflag |= CREAD;
} else
ser_stty( dev );
if ( ser_attach_intr( dev ) == -1 )
{
free_device( dev );
return (-1);
}
}
/* rx and tx fifo sizes */
dev->rx_fifo = EXAR_FIFO_RX_SIZE;
dev->tx_fifo = EXAR_FIFO_TX_SIZE;
devc_info( "[CH%d] RX FIFO: %d, TX FIFO: %d", dev->chnum, dev->rx_fifo, dev->tx_fifo );
/* Set RX fifo trigger level */
switch ( dev->tty.fifo & 0xf )
{
case 1:
default:
reg = FCR_RX_TRIG_1;
break;
case 4:
reg = FCR_RX_TRIG_4;
break;
case 8:
reg = FCR_RX_TRIG_8;
break;
case 14:
reg = FCR_RX_TRIG_14;
break;
}
write_8250( dev->port[REG_RXTRG], dev->rx_fifo );
write_8250( dev->port[REG_TXTRG], dev->tx_fifo );
reg |= FCR_FIFO_ENABLE | FCR_RX_FIFO_RESET | FCR_TX_FIFO_RESET;
write_8250( dev->port[REG_FC], reg );
/* Turn on DTR and RTS */
set_port( dev->port[REG_MC], MCR_DTR | MCR_RTS, MCR_DTR | MCR_RTS );
/* Get current MSR stat */
reg = read_8250( dev->port[REG_MS] );
if ( reg & MSR_DDCD )
tti( &dev->tty, (reg & MSR_DCD) ? TTI_CARRIER : TTI_HANGUP );
if ( (reg & MSR_DCTS) && (dev->tty.c_cflag & OHFLOW) )
tti( &dev->tty, (reg & MSR_CTS) ? TTI_OHW_CONT : TTI_OHW_STOP );
enable_exar_ch( dev );
/* Attach the resource manager */
ttc( TTC_INIT_ATTACH, &dev->tty, 0 );
return (EOK);
}

123
devc/serexar/intr.c Normal file
View File

@ -0,0 +1,123 @@
/*
* (c) 2022, SWD Embedded Systems Limited, http://www.kpda.ru
*/
#include "externs.h"
/* Process data in a line status register */
unsigned process_lsr( DEV_8250 *dev, unsigned char lsr )
{
unsigned key = 0; /*, eventflag = 0; */
/* Return immediately if no errors. */
if ( (lsr & (LSR_BI | LSR_OE | LSR_FE | LSR_PE)) == 0 )
return (0);
/* Save the error as out-of-band data which can be retrieved via devctl(). */
dev->tty.oband_data |= (lsr >> 1) & 0x0f;
atomic_set( &dev->tty.flags, OBAND_DATA );
if ( lsr & LSR_BI )
key |= TTI_BREAK;
else {
if ( lsr & LSR_OE )
key |= TTI_OVERRUN;
else if ( lsr & LSR_FE )
key |= TTI_FRAME;
else if ( lsr & LSR_PE )
key |= TTI_PARITY;
}
return (key);
}
/* Serial interrupt handler */
const struct sigevent * ser_intr( void *area, int id )
{
struct dev_list *list = area;
int status = 0;
int something_happened;
unsigned char msr,
lsr;
DEV_8250 *dev;
struct sigevent *event = NULL;
unsigned c;
int channel;
do {
something_happened = 0;
for ( dev = list->device; dev != NULL; dev = dev->next )
{
unsigned iir,
rxcnt = 0;
uintptr_t *port = dev->port;
status = 0;
iir = (read_8250( port[REG_II] ) & 0x07);
switch ( iir )
{
case II_RX: /* Receive data */
case II_LS:
/* Some UARTs will generate a LS interrupt when there is an error anywhere in the RX
* FIFO, and will clear this interrupt only when there are no more errors remaining
* in the FIFO. The error bits in REG_LS (BI/PR/FE/OE) always represent the error
* status for the received character at the top of the Rx FIFO. Reading the Rx FIFO
* updates these bits to the appropriate status of the new character. This means that
* it is possible to get an LS interrupt with none of the error status bits set, in
* order to clear the LS interrupt we must read out all of the characters in the FIFO
* until we find and handle the erronous character. */
if ( (lsr = read_8250( port[REG_LS] )) & LSR_RXRDY )
{
c = process_lsr( dev, lsr );
rxcnt = read_8250( port[REG_RXCNT] );
channel = dev->chnum - exar_dev.ch_offset - 1;
status |= tti2( &dev->tty, dev->parent_dev->uart_mem + UART_READ_FIFO_BASE( channel ), rxcnt, c );
};
break;
case II_TX: /* Transmit buffer empty */
dev->tty.un.s.tx_tmr = 0;
/* Send evnet to io-char, tto() will be processed at thread time */
atomic_set( &dev->tty.flags, EVENT_TTO );
status |= 1;
break;
case II_MS: /* Modem change */
msr = read_8250( port[REG_MS] );
if ( msr & MSR_DDCD )
status |= tti( &dev->tty, (msr & MSR_DCD) ? TTI_CARRIER : TTI_HANGUP );
if ( (msr & MSR_DCTS) && (dev->tty.c_cflag & OHFLOW) )
status |= tti( &dev->tty, (msr & MSR_CTS) ? TTI_OHW_CONT : TTI_OHW_STOP );
/* OBAND notification of Modem status change */
dev->tty.oband_data |= _OBAND_SER_MS;
atomic_set( &dev->tty.flags, OBAND_DATA );
atomic_set( &dev->tty.flags, EVENT_NOTIFY_OBAND );
status |= 1;
break;
default:
continue;
}
something_happened = 1;
if ( status )
if ((dev->tty.flags & EVENT_QUEUED) == 0 )
{
event = &ttyctrl.event;
dev_lock( &ttyctrl );
ttyctrl.event_queue[ttyctrl.num_events++] = &dev->tty;
atomic_set( &dev->tty.flags, EVENT_QUEUED );
dev_unlock( &ttyctrl );
}
}
} while ( something_happened );
return (event);
}

24
devc/serexar/main.c Normal file
View File

@ -0,0 +1,24 @@
/*
* (c) 2022, SWD Embedded Systems Limited, http://www.kpda.ru
*/
#include "externs.h"
#include <stdio.h>
#include <pthread.h>
int main( int argc, char *argv[] )
{
ttyctrl.max_devs = MAX_DEVICES;
ttc( TTC_INIT_PROC, &ttyctrl, 24 );
if ( options( argc, argv ) == 0 )
{
fprintf( stderr, "%s: No serial ports found\n", argv[0] );
exit( 0 );
}
ttc( TTC_INIT_START, &ttyctrl, 0 );
return (0);
}

300
devc/serexar/options.c Normal file
View File

@ -0,0 +1,300 @@
/*
* (c) 2022, SWD Embedded Systems Limited, http://www.kpda.ru
*/
/*
#ifdef __USAGE
%C - Serial driver for PCIe cards based on Exar XR17V354/358 and compatible controllers
%C [options]
Options:
-b number Define initial baud rate (default 115200)
-c clk[/div] Set the input clock rate and divisor
-C number Size of canonical input buffer (default 1024)
-e Set options to "edit" mode
-E Set options to "raw" mode (default)
-I number Size of raw input buffer (default 2048)
-f Enable hardware flow control (default)
-F Disable hardware flow control
-O number Size of output buffer (default 2048)
-s Enable software flow control
-S Disable software flow control (default)
-t number Set receive FIFO trigger level (default 14)
-u unit Set serial unit number (default 1)
-o opt[,opt] String options:
pmm_parent=path (pmm_parent power pathname, default NULL)
pm_noflw (Disable flow control on power down, default enabled)
highwater=value (RX watermark for input flow control (bytes))
disable=rx (Disable receiver on startup)
-v[vvv] Increase verbosity level: errors, warnings, info, debug
-V PCI Vendor ID (0x13a8 by default)
-D PCI Device ID (0x358 by default)
-i PCI index
-R Interrupt number
-P Number of channels (default 8)
-m RS485|RS232 Set selected mode for all ports (RS232 by default)
-M port=1[,port=2] Set RS485/422 mode for specific ports
-p Invert RS485 Polarity
-r Disable DTR control
-a Enable ASPM (default: disable)
Examples:
Default RS232 mode
%C
RS485 mode with disabled hardware flow control
%C -mRS485 -F
RS485 mode with inverted polarity for Advantech PCI-1612 0x13fe:0x1b PCI card
%C -mRS485 -v -p -V 0x13fe -D 0x1b -i 0
#endif
*/
#include "externs.h"
unsigned options( int argc, char *argv[] )
{
int opt,
i,
enabled_cnt = 0,
numports = 0,
port = -1;
char *cp;
char *options,
*value;
char *port_options[] = { "port", NULL };
unsigned unit,
dtr_opt = 0;
unsigned fifo_rx;
uint32_t chsize = EXAR_UART_CHANNEL_SIZE;
uint32_t choffset = EXAR_UART_CHANNEL_OFFSET;
DEV_8250 *dev_list[MAX_DEVICES];
int32_t pci_id,
pci_idx = 0;
void *pci_handle;
static TTYINIT devinit = { 0, 0, 0, 115200, /* port port_shift intr baud */
4096, 4096, 1024, /* isize osize csize */
0, 0, 0, 0, 0xee, 125000000, 16, /* c_cflag c_iflag c_lflag c_oflag fifo clk div */
"/dev/ser",
NULL, 0, 0, 0 };
struct pci_dev_info inf;
if ( (pci_id = pci_attach( 0 )) == -1 )
{
devc_err( "io-char: Unable to attach to PCI server (%d)", errno );
return (-1);
}
/* Exar PCI device */
inf.VendorId = EXAR_VID;
inf.DeviceId = EXAR_XR17D158_DID;
exar_dev.uart_cnt = -1;
exar_dev.ch_offset = -1;
for ( i = 1; i <= UART_MAX_CH_COUNT; i++ )
exar_dev.mode[i] = EXAR_MODE_RS232;
exar_dev.disable_aspm = 1;
exar_dev.rs485_invert_polarity = 0;
/* Initialize the devinit to raw mode */
ttc( TTC_INIT_RAW, &devinit, 0 );
sys_ttyinit( &devinit );
unit = 1;
while ( optind < argc )
{
/* Process options */
while ( (opt = getopt( argc, argv, IO_CHAR_SERIAL_OPTIONS "c:t:T:u:i:V:D:R:P:m:M:arp" )) != -1 )
{
switch ( ttc( TTC_SET_OPTION, &devinit, opt ) )
{
case 'c':
devinit.clk = strtoul( optarg, &optarg, 0 );
if ( (cp = strchr( optarg, '/' )) )
devinit.div = strtoul( cp + 1, NULL, 0 );
break;
case 't':
fifo_rx = strtoul( optarg, NULL, 0 );
if ( !((fifo_rx == 1) || (fifo_rx == 4) || (fifo_rx == 8) || (fifo_rx == 14)) )
{
devc_err( "Illegal rx fifo trigger. \n" );
devc_err( "Trigger number must be 1, 4, 8 or 14. \n" );
devc_err( "Rx trigger will not be enabled.\n\n" );
fifo_rx = 0;
} else {
devinit.fifo &= 0xf0;
devinit.fifo |= fifo_rx;
}
break;
case 'u':
unit = strtoul( optarg, NULL, 0 );
break;
/* PCI index */
case 'i':
pci_idx = strtoul( optarg, NULL, 0 );
break;
/* PCI Vendor */
case 'V':
inf.VendorId = strtoul( optarg, NULL, 0 );
break;
/* PCI device */
case 'D':
inf.DeviceId = strtoul( optarg, NULL, 0 );
break;
/* Number of ports */
case 'P':
exar_dev.uart_cnt = strtoul( optarg, NULL, 0 );
break;
/* IRQ */
case 'R':
devinit.intr = strtoul( optarg, NULL, 0 );
break;
/* RS232/RS485 mode */
case 'm':
if ( strcasecmp( "RS485", optarg ) == 0 )
for ( i = 1; i <= UART_MAX_CH_COUNT; i++ )
exar_dev.mode[i] = EXAR_MODE_RS485;
else if ( strcasecmp( "RS232", optarg ) == 0 )
for ( i = 1; i <= UART_MAX_CH_COUNT; i++ )
exar_dev.mode[i] = EXAR_MODE_RS232;
else
devc_err( "Error in device mode string parsing('%s'). Use default", optarg );
break;
/* RS485 per port options */
case 'M':
options = optarg;
while ( *options != '\0' )
{
switch ( getsubopt( &options, port_options, &value ) )
{
case 0:
if ( value != NULL )
{
port = atoi( value ); /* 1-8 */
if ( port > 0 && port <= UART_MAX_CH_COUNT )
{
exar_dev.mode[port] = EXAR_MODE_RS485;
devc_info( "Port[%d] mode: RS485/422", port );
} else
devc_err( "Incorrect port mode option. Valid port numbers: 1-%d", UART_MAX_CH_COUNT );
}
break;
default :
break;
}
}
break;
/* Enable Active-state power managment */
case 'a':
exar_dev.disable_aspm = 0;
break;
/* Disable DTR control. Workaround for Qt QtSerialPort class */
case 'r':
dtr_opt = 1;
break;
/* RS485 inverted polarity */
case 'p':
exar_dev.rs485_invert_polarity = 1;
break;
}
}
/* Process ports and interrupts */
while ( optind < argc && *(optarg = argv[optind]) != '-' )
{
devinit.port = strtoull( optarg, &optarg, 16 );
choffset = devinit.port;
if ( *optarg == ':' )
chsize = strtoull( optarg + 1, &optarg, 0 );
if ( *optarg == '^' )
devinit.port_shift = strtoul( optarg + 1, &optarg, 0 );
if ( *optarg == ',' )
devinit.intr = strtoul( optarg + 1, NULL, 0 );
++optind;
}
}
driver_verbosity_level = devinit.verbose;
if ( pci_idx != -1 )
{
devc_dbg( "Attach to PCI device vid:0x%X did:0x%X", inf.VendorId, inf.DeviceId );
if ( (pci_handle = pci_attach_device( NULL, PCI_SEARCH_VENDEV | PCI_MASTER_ENABLE | PCI_INIT_ALL,
pci_idx, &inf )) == NULL )
{
pci_detach( pci_id );
devc_err( "io-char: Can't attach to PCI device 0x%X,0x%X.", inf.VendorId, inf.DeviceId );
return (-1);
}
}
if ( exar_dev_init( &inf, &exar_dev ) < 0 )
{
pci_detach( pci_id );
devc_err( "Failed to init Exar device." );
fprintf( stderr, "Failed to init Exar device\n" );
return (-1);
}
/* Process ports and interrupts. */
for ( i = 0; i < exar_dev.uart_cnt; i++ )
{
/* Offset for Exar chip */
devinit.port = PCI_MEM_ADDR( inf.CpuBaseAddress[0] ) + (choffset + (chsize * i));
devc_dbg( "Run driver on channel %d. Mem: %llX (base offset : 0x%X , channel offset : 0x%X)", i, devinit.port, choffset, chsize );
if ( devinit.intr == 0 )
devinit.intr = inf.Irq;
if ( (dev_list[numports] = create_device( &devinit, unit++, &exar_dev )) == NULL )
devc_err( "io-char: Initialization of /dev/ser%d (port 0x%llx) failed", unit - 1, devinit.port );
else
++numports;
}
if ( numports == 0 )
{
devc_err( "Failed to init Exar device." );
fprintf( stderr, "Failed to init Exar device\n" );
return (-1);
}
/* Enable all ports (Attach ISR and pathname entry) */
for ( i = 0; i < numports; i++ )
{
if ( dev_list[i]->tty.verbose )
{
slogf( _SLOG_SETCODE( _SLOGC_CHAR, 0 ), _SLOG_INFO, "Port .......................... %s (0x%x)", dev_list[i]->tty.name, dev_list[i]->port[0] );
slogf( _SLOG_SETCODE( _SLOGC_CHAR, 0 ), _SLOG_INFO, "IRQ ........................... 0x%x", dev_list[i]->intr );
slogf( _SLOG_SETCODE( _SLOGC_CHAR, 0 ), _SLOG_INFO, "Input Clock ................... %d", dev_list[i]->clk );
slogf( _SLOG_SETCODE( _SLOGC_CHAR, 0 ), _SLOG_INFO, "Clock Divisor ................. %d", dev_list[i]->div );
slogf( _SLOG_SETCODE( _SLOGC_CHAR, 0 ), _SLOG_INFO, "Input buffer size ............. %d", dev_list[i]->tty.ibuf.size );
slogf( _SLOG_SETCODE( _SLOGC_CHAR, 0 ), _SLOG_INFO, "Input flow control highwater .. %d", dev_list[i]->tty.highwater );
slogf( _SLOG_SETCODE( _SLOGC_CHAR, 0 ), _SLOG_INFO, "Output buffer size ............ %d", dev_list[i]->tty.obuf.size );
slogf( _SLOG_SETCODE( _SLOGC_CHAR, 0 ), _SLOG_INFO, "Canonical buffer size ......... %d\n", dev_list[i]->tty.cbuf.size );
}
if ( enable_device( dev_list[i] ) != -1 )
{
enabled_cnt++;
dev_list[i]->dtr_disable = dtr_opt;
}
}
return (enabled_cnt);
}

7
devc/serexar/pinfo.mk Normal file
View File

@ -0,0 +1,7 @@
#
# (c) 2022, SWD Embedded Systems Limited, http://www.kpda.ru
#
define PINFO
PINFO DESCRIPTION=Character device driver for the Exar serial controllers
endef

17
devc/serexar/proto.h Normal file
View File

@ -0,0 +1,17 @@
/*
* (c) 2022, SWD Embedded Systems Limited, http://www.kpda.ru
*/
DEV_8250 * create_device( TTYINIT *dip, unsigned unit, exar_dev_t *exdev );
void ser_stty( DEV_8250 *dev );
void ser_ctrl( DEV_8250 *dev, unsigned flags );
void sys_ttyinit( TTYINIT *dip );
void * query_default_device( TTYINIT *dip, void *link );
int enable_device( DEV_8250 *dev );
void set_port( unsigned port, unsigned mask, unsigned data );
const struct sigevent * ser_intr( void *area, int id );
unsigned options( int argc, char *argv[] );

View File

@ -0,0 +1,13 @@
/*
* (c) 2022, SWD Embedded Systems Limited, http://www.kpda.ru
*/
#include "externs.h"
/* By default, no default devices. */
void * query_default_device( TTYINIT *dip, void *link )
{
return (NULL);
}

15
devc/serexar/tedit.c Normal file
View File

@ -0,0 +1,15 @@
/*
* (c) 2022, SWD Embedded Systems Limited, http://www.kpda.ru
*/
#include "externs.h"
int edit( TTYDEV *dev, unsigned c )
{
dev = dev;
c = c;
return (0);
}

193
devc/serexar/tto.c Normal file
View File

@ -0,0 +1,193 @@
/*
* (c) 2022, SWD Embedded Systems Limited, http://www.kpda.ru
*/
#include <math.h>
#include "externs.h"
double trunc( double );
double round( double );
int tto( TTYDEV *ttydev, int action, int arg1 )
{
TTYBUF *bup = &ttydev->obuf;
DEV_8250 *dev = (DEV_8250 *)ttydev;
const uintptr_t *port = dev->port;
unsigned char c;
int nbytes_tx = 0;
switch ( action )
{
case TTO_STTY:
ser_stty( dev );
return (0);
case TTO_CTRL:
if ( arg1 & _SERCTL_BRK_CHG )
set_port( port[REG_LC], LCR_BREAK, arg1 &_SERCTL_BRK ? LCR_BREAK : 0 );
if ( (arg1 & _SERCTL_DTR_CHG) && !dev->dtr_disable )
set_port( port[REG_MC], MCR_DTR, arg1 & _SERCTL_DTR ? MCR_DTR : 0 );
if ( arg1 & _SERCTL_RTS_CHG )
set_port( port[REG_MC], MCR_RTS, arg1 & _SERCTL_RTS ? MCR_RTS : 0 );
return (0);
case TTO_LINESTATUS:
return (((read_8250( port[REG_MS] ) << 8) | read_8250( port[REG_MC] )) & 0xf003);
case TTO_DATA:
case TTO_EVENT:
break;
default:
return (0);
}
/* With FIFOs enable LSR_TXRDY is set when the last byte is moved from the TX fifo to the TX shift
* register (TX FIFO empty). LSR_TXRDY is cleared when a byte is added to the TX FIFO. Since there
* is no TX trigger level for 16550 style UARTs, we only ever write when the FIFO is empty to ensure
* we do not overrun the TX fifo.
* NOTE: Do not confuse the LSR_TXDRY with the II_TX which is cleared on read of the II register
* and/or when a byte is written to the TX Fifo */
if ( read_8250( port[REG_LS] ) & LSR_TXRDY )
{
nbytes_tx = 0;
while ( bup->cnt > 0 && nbytes_tx < FIFO_SIZE - dev->rx_fifo )
{
/* If the OSW_PAGED_OVERRIDE flag is set then allow transmit of character even if output is
* suspended via the OSW_PAGED flag. This flag implies that the next character in the obuf
* is a software flow control charater (STOP/START).
* Note: tx_inject sets it up so that the contol character is at the start (tail) of the buffer. */
if ( dev->tty.flags & (OHW_PAGED | OSW_PAGED) && !(dev->tty.xflags & OSW_PAGED_OVERRIDE) )
break;
/* Get character from obuf and do any output processing */
dev_lock( &dev->tty );
c = tto_getchar( &dev->tty );
dev_unlock( &dev->tty );
/* Print the character */
dev->tty.un.s.tx_tmr = 3; /* Timeout */
write_8250( port[REG_TX], c );
nbytes_tx++;
/* Clear the OSW_PAGED_OVERRIDE flag as we only want one character to be transmitted in this case. */
if ( dev->tty.xflags & OSW_PAGED_OVERRIDE )
{
atomic_clr( &dev->tty.xflags, OSW_PAGED_OVERRIDE );
break;
}
}
#if defined( PA6T_WORKAROUND )
if ( nbytes_tx && (dev->tx_empty_disable > 50) )
{
/* Enable the TX holding register empty interrupt, as we might have disabled it in the interrupt
* handler due to a problem with early PA6T-1682's */
set_port( port[REG_IE], 0x2, 0x2 );
}
#endif
}
/* Check the client lists for notify conditions */
return (tto_checkclients( &dev->tty ));
}
void ser_stty( DEV_8250 *dev )
{
unsigned lcr = 0;
const uintptr_t *port = dev->port;
double calc_div;
unsigned dld, actual_div;
calc_div = (double)dev->clk / (dev->tty.baud * dev->div);
actual_div = round( (calc_div - trunc( calc_div )) * 16 ) / 16 + trunc( calc_div );
dld = round( (calc_div - trunc( calc_div )) * 16 );
dev_lock( &dev->tty );
devc_dbg( "Set DLL : 0x%X , DLM : 0x%X, DLD : 0x%X", actual_div & 0xff, (actual_div >> 8) & 0xff, dld );
set_port( port[REG_LC], LCR_DLAB, LCR_DLAB );
set_port( port[REG_DL0], 0xff, actual_div & 0xff );
set_port( port[REG_DL1], 0xff, actual_div >> 8 );
set_port( port[REG_FC], 0x0f, dld );
set_port( port[REG_LC], LCR_DLAB, 0 );
dev_unlock( &dev->tty );
if ( dev->tty.c_cflag & (OHFLOW) )
{
devc_dbg( "Enable Auto hardware flow" );
set_port( port[REG_EFR], (1 << 7) | (1 << 6), (1 << 7) | (1 << 6) ); /* Enable Auto hardware flow */
set_port( port[REG_MC], MCR_OUT1, MCR_OUT1 );
set_port( port[REG_MC], MCR_DTR | MCR_RTS, MCR_DTR | MCR_RTS );
} else {
devc_dbg( "Disable Auto hardware flow" );
set_port( port[REG_EFR], (1 << 7) | (1 << 6), 0x00 ); /* Disable Auto hardware flow */
set_port( port[REG_MC], MCR_OUT1, 0x00 );
}
if ( dev->tty.c_cflag & CREAD && !(read_8250( port[REG_IE] ) & 0x1) )
{
/* Enable receiver */
set_port( port[REG_FC], FCR_RX_FIFO_RESET, FCR_RX_FIFO_RESET ); /* Clear RX Fifo */
set_port( port[REG_IE], 0x1, 0x1 ); /* Enable receive interrupt */
} else