Драйвер devc-serexar для ЗОСРВ Нейтрино редакции 2020 (основано на исх. коде редакции 2021+)
Этот коммит содержится в:
родитель
23ee7029b9
Коммит
30e8332d7c
@ -19,8 +19,9 @@
|
||||
## Дерево исходных кодов
|
||||
|
||||
```
|
||||
|- devc/ser8250/
|
||||
| |- *.* - Исходный код драйвера последовательных 8250-совместимых устройств
|
||||
|- devc/
|
||||
| |- ser8250/ - Исходный код драйвера последовательных 8250-совместимых устройств
|
||||
| |- serexar/ - Исходный код драйвера последовательных устройств, совместимых с контроллером Exar XR17V354/358
|
||||
| |- common.mk - Параметры сборки драйверов
|
||||
| `- Makefile - Правила сборки дерева исходников
|
||||
|
|
||||
|
2
devc/serexar/Makefile
Обычный файл
2
devc/serexar/Makefile
Обычный файл
@ -0,0 +1,2 @@
|
||||
LIST=CPU
|
||||
include recurse.mk
|
91
devc/serexar/exar.c
Обычный файл
91
devc/serexar/exar.c
Обычный файл
@ -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
Обычный файл
98
devc/serexar/exar.h
Обычный файл
@ -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
Обычный файл
54
devc/serexar/externs.c
Обычный файл
@ -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
Обычный файл
132
devc/serexar/externs.h
Обычный файл
@ -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
Обычный файл
418
devc/serexar/init.c
Обычный файл
@ -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
Обычный файл
123
devc/serexar/intr.c
Обычный файл
@ -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
Обычный файл
24
devc/serexar/main.c
Обычный файл
@ -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
Обычный файл
300
devc/serexar/options.c
Обычный файл
@ -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
Обычный файл
7
devc/serexar/pinfo.mk
Обычный файл
@ -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
Обычный файл
17
devc/serexar/proto.h
Обычный файл
@ -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[] );
|
13
devc/serexar/query_defdev.c
Обычный файл
13
devc/serexar/query_defdev.c
Обычный файл
@ -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
Обычный файл
15
devc/serexar/tedit.c
Обычный файл
@ -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
Обычный файл
193
devc/serexar/tto.c
Обычный файл
@ -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
|
||||
if ( !(dev->tty.c_cflag & CREAD) && (read_8250( port[REG_IE] ) & 0x1) )
|
||||
{
|
||||
/* Disable receiver */
|
||||
set_port( port[REG_IE], 0x1, 0x0 ); /* Disable receive interrupt */
|
||||
}
|
||||
|
||||
/* Set data bits */
|
||||
switch ( dev->tty.c_cflag & CSIZE )
|
||||
{
|
||||
case CS8: ++lcr;
|
||||
case CS7: ++lcr;
|
||||
case CS6: ++lcr;
|
||||
}
|
||||
|
||||
/* Set stop bits */
|
||||
if ( dev->tty.c_cflag & CSTOPB )
|
||||
lcr |= LCR_STB2;
|
||||
|
||||
/* Set parity bits */
|
||||
if ( dev->tty.c_cflag & PARENB )
|
||||
{
|
||||
lcr |= LCR_PEN;
|
||||
if ( dev->tty.c_cflag & PARODD )
|
||||
lcr &= ~( LCR_EPS ); /* Clear even bit if odd parity */
|
||||
else
|
||||
lcr |= LCR_EPS;
|
||||
if ( dev->tty.c_cflag & PARSTK ) /* Check if Mark/Space parity enabled */
|
||||
lcr |= LCR_SPS;
|
||||
else
|
||||
lcr &= ~( LCR_SPS );
|
||||
} else
|
||||
lcr &= ~( LCR_PEN );
|
||||
|
||||
set_port( port[REG_LC], 0xFF, lcr );
|
||||
}
|
||||
|
||||
|
||||
int drain_check( TTYDEV *ttydev, uintptr_t *count )
|
||||
{
|
||||
TTYBUF *bup = &ttydev->obuf;
|
||||
DEV_8250 *dev = (DEV_8250 *)ttydev;
|
||||
const uintptr_t *port = dev->port;
|
||||
|
||||
/* if the device has DRAINED, return 1 */
|
||||
if ( (bup->cnt == 0) && (read_8250( port[REG_LS] ) & LSR_TSRE) )
|
||||
return 1;
|
||||
|
||||
/* if the device has not DRAINED, set a timer based on 50ms counts wait for the time it takes
|
||||
* for one character to be transmitted out the shift register. We do this dynamically since the
|
||||
* baud rate can change. */
|
||||
if ( count != NULL )
|
||||
*count = (ttydev->baud == 0) ? 0 : ((IO_CHAR_DEFAULT_BITSIZE * 20) / ttydev->baud) + 1;
|
||||
return (0);
|
||||
}
|
21
devc/serexar/variant.h
Обычный файл
21
devc/serexar/variant.h
Обычный файл
@ -0,0 +1,21 @@
|
||||
/*
|
||||
* (c) 2022, SWD Embedded Systems Limited, http://www.kpda.ru
|
||||
*/
|
||||
|
||||
|
||||
#ifndef write_8250
|
||||
#if defined( __X86__ )
|
||||
#define write_8250( __port, __val ) *((uint8_t *)__port) = __val //out8( __port,__val )
|
||||
#else
|
||||
#define write_8250( __port, __val ) out8( __port, __val )
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
#ifndef read_8250
|
||||
#if defined( __X86__ )
|
||||
#define read_8250( __port ) (*((uint8_t *)__port)) //in8( __port )
|
||||
#else
|
||||
#define read_8250( __port ) in8( __port )
|
||||
#endif
|
||||
#endif
|
2
devc/serexar/x86/Makefile
Обычный файл
2
devc/serexar/x86/Makefile
Обычный файл
@ -0,0 +1,2 @@
|
||||
LIST=VARIANT
|
||||
include recurse.mk
|
2
devc/serexar/x86/o/Makefile
Обычный файл
2
devc/serexar/x86/o/Makefile
Обычный файл
@ -0,0 +1,2 @@
|
||||
include ../../../common.mk
|
||||
LIBS+=m
|
17
devc/serexar/x86/sys_ttyinit.c
Обычный файл
17
devc/serexar/x86/sys_ttyinit.c
Обычный файл
@ -0,0 +1,17 @@
|
||||
/*
|
||||
* (c) 2022, SWD Embedded Systems Limited, http://www.kpda.ru
|
||||
*/
|
||||
|
||||
|
||||
#include "externs.h"
|
||||
|
||||
|
||||
#define DEFAULT_CLK 125000000
|
||||
#define DEFAULT_DIV 16
|
||||
|
||||
|
||||
void sys_ttyinit( TTYINIT *dip )
|
||||
{
|
||||
dip->clk = DEFAULT_CLK;
|
||||
dip->div = DEFAULT_DIV;
|
||||
}
|
Загрузка…
x
Ссылка в новой задаче
Block a user