Драйвер devc-ser8250 для ЗОСРВ Нейтрино редакции 2020

This commit is contained in:
commit 23ee7029b9
39 changed files with 1733 additions and 0 deletions

2
Makefile Normal file
View File

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

49
README.md Normal file
View File

@ -0,0 +1,49 @@
## Общая структура драйвера
```
┌───────────────────────────┐
│ │
│ Устройство │
│ │
└─────────────▴─────────────┘
┌─────────────┴─────────────┐ ┌───────────────────────────┐
│ │ │ │
│ Драйвер (devc-*) ◂───▸ /dev/ser* ◂── * ──┤ Клиентское приложение │
│ │ │ │
└───────────────────────────┘ └───────────────────────────┘
```
## Дерево исходных кодов
```
|- devc/ser8250/
| |- *.* - Исходный код драйвера последовательных 8250-совместимых устройств
| |- common.mk - Параметры сборки драйверов
| `- Makefile - Правила сборки дерева исходников
|
`- Makefile - Правила сборки дерева исходников
```
## Сборка драйвера
- Установить и настроить [комплект разработчика](https://help.kpda.ru/help/topic/ru.kpda.doc.dev_tools_ru/html/devkit/devkit.html) для [ЗОСРВ "Нейтрино" редакции 2020](https://help.kpda.ru/help/index.jsp).
- Выполнить команду:
```
make
```
## Запуск драйвера и обращение к нему
Общая схема запуска драйвера:
```
devc-ser8250 -b115200 3f8,4 2f8,3
```

2
devc/Makefile Normal file
View File

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

73
devc/common.mk Normal file
View File

@ -0,0 +1,73 @@
#
# (c) 2010-2020, SWD Embedded Systems Limited, http://www.kpda.ru
#
#
# Copyright 2007, 2008, QNX Software Systems.
#
# Licensed under the Apache License, Version 2.0 (the "License"). You
# may not reproduce, modify or distribute this software except in
# compliance with the License. You may obtain a copy of the License
# at: http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" basis,
# WITHOUT WARRANTIES OF ANY KIND, either express or implied.
#
# This file may contain contributions from others, either as
# contributors under the License or as licensors under other terms.
# Please review this entire file for other proprietary rights or license
# notices, as well as the QNX Development Suite License Guide at
# http://licensing.qnx.com/license-guide/ for other information.
#
ifndef QCONFIG
QCONFIG=qconfig.mk
endif
include $(QCONFIG)
NAME=devc-$(SECTION)
EXTRA_SILENT_VARIANTS+=$(subst -, ,$(SECTION))
CCFLAGS_e2k += -fkernel
CCFLAGS += $(CCFLAGS_$(CPU))
LIBS+=io-char pm ps drvr
EXCLUDE_OBJS+=tedit.o
USEFILE=$(SECTION_ROOT)/options.c
INSTALLDIR=sbin
define PINFO
PINFO DESCRIPTION=
endef
include $(MKFILES_ROOT)/qmacros.mk
include $(SECTION_ROOT)/pinfo.mk
TINY_NAME=$(subst devc-,devc-t,$(BUILDNAME))
ifneq (,$(filter tedit.c, $(notdir $(SRCS))))
POST_TARGET=$(TINY_NAME)
EXTRA_ICLEAN=$(TINY_NAME)*
define POST_INSTALL
-$(CP_HOST) $(TINY_NAME) $(INSTALL_DIRECTORY)/
endef
endif
include $(MKFILES_ROOT)/qtargets.mk
-include $(PROJECT_ROOT)/roots.mk
ifndef LIBIOCHAR_ROOT
LIBIOCHAR_ROOT=$(PRODUCT_ROOT)
endif
#
# Some makefile mopery-popery to get devc-t*.pinfo generated properly
#
$(TINY_NAME): INSTALLNAME=$(INSTALL_DIRECTORY)/$(TINY_NAME)
$(TINY_NAME): tedit.o $(OBJS) $(LIBNAMES)
$(TARGET_BUILD)

2
devc/ser8250/Makefile Normal file
View File

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

View File

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

View File

@ -0,0 +1 @@
include ../../../common.mk

View File

@ -0,0 +1 @@
include ../../../common.mk

View File

@ -0,0 +1,17 @@
/*
* (c) 2010, SWD Embedded Systems Limited, http://www.kpda.ru
*/
#include "externs.h"
#define DEFAULT_CLK 1843200
#define DEFAULT_DIV 16
void sys_ttyinit( TTYINIT *dip )
{
dip->clk = DEFAULT_CLK;
dip->div = DEFAULT_DIV;
}

48
devc/ser8250/baytrail.c Normal file
View File

@ -0,0 +1,48 @@
/*
* (c) 2019, SWD Embedded Systems Limited, http://www.kpda.ru
*/
#include "externs.h"
/* Description: return current base clock(not dh/dl)
* In:
* dev - device handler
* Out:
* current clock in Hz */
uint64_t get_baytrail_clock( DEV_8250* dev )
{
uint32_t clock = mem_read32( dev->ext_regs + BTUART_PRIV_CLOCK_PARAMS );
uint64_t m = BTUART_PRIV_CLOCK_PARAMS_GET_M( clock );
uint64_t n = BTUART_PRIV_CLOCK_PARAMS_GET_N( clock );
if ( clock == 0 )
return (0);
/* Fbase = 100000000 (Hz)
* Formula to calculate base clock : ( FBase * M ) / N */
return (FBASE * m) / n;
}
/* Description: set base clock(not dh/dl)
* In:
* dev - device handler
* m - multiplier ( see formula )
* n - divider ( see formula )
* Out:
* 0 if setting success, other for error */
int set_baytrail_clock( DEV_8250* dev, int m , int n )
{
uint32_t clock = mem_read32( dev->ext_regs + BTUART_PRIV_CLOCK_PARAMS );
if ( clock == 0 )
return (-1);
clock = BTUART_PRIV_CLOCK_PARAMS_SET_M( m, clock );
clock = BTUART_PRIV_CLOCK_PARAMS_SET_N( n, clock );
mem_write32( dev->ext_regs + BTUART_PRIV_CLOCK_PARAMS, clock );
return (0);
}

94
devc/ser8250/baytrail.h Normal file
View File

@ -0,0 +1,94 @@
/*
* (c) 2019, SWD Embedded Systems Limited, http://www.kpda.ru
*/
#ifndef __BAYTRIL_H__
#define __BAYTRIL_H__
#define BTUART_PCI_VENDOR 0x8086
#define BTUART_PCI_DEVICE_0 0xF0A
#define BTUART_PCI_DEVICE_1 0xF0C
/* Count of extend registers. Full memory size = 0x824*/
#define BTUART_REG_CNT 2084
/* FBase clock default - 100MHz */
#define FBASE 100000000UL
/* Regno range - {0 - 15} */
#define BTUART_SHADOW_REG( regnum ) ( regnum + 0x30 )
/* FIFO access register */
#define BTUART_FAR 0x1C
/* Transmit FIFO register */
#define BTUART_TFR 0x1D
/* Receive FIFO write register */
#define BTUART_RFW 0x1E
/* UART status register */
#define BTUART_USR 0x1F
/* Transmit FIFO level */
#define BTUART_TFL 0x20
/* Receive FIFO level */
#define BTUART_RFL 0x21
/* Software reset register */
#define BTUART_SRR 0x22
/* Shadow request to send */
#define BTUART_SRTS 0x23
/* Shadow break control */
#define BTUART_SBCR 0x24
/* Shadow DMA mode */
#define BTUART_SDMAM 0x25
/* Shadow FIFO enable */
#define BTUART_SFE 0x26
/* Shadow request to send */
#define BTUART_SRTS2 0x27
/* Shadow TX empty trigger */
#define BTUART_STET 0x28
/* Halt tx */
#define BTUART_HTX 0x29
/* DMA software acknowledge */
#define BTUART_DMASA 0x2A
/* Component parameter register */
#define BTUART_CPR 0x3D
#define BTUART_CPR_FIFO_mode_mask (0xFF << 16)
#define BTUART_CPR_GET_FIFO_mode( val ) (val & BTUART_CPR_FIFO_mode_mask >> 16)
#define BTUART_FIFO_SIZE( val ) (val * 32)
#define BTUART_CPR_dma_extra (1 << 13)
#define BTUART_CPR_add_encoded_params (1 << 12)
#define BTUART_CPR_shadow (1 << 11)
#define BTUART_CPR_FIFO_stat (1 << 10)
#define BTUART_CPR_FIFO_access (1 << 9)
#define BTUART_CPR_additional_feat (1 << 8)
#define BTUART_CPR_sir_lp_mode (1 << 7)
#define BTUART_CPR_sir_mode (1 << 6)
#define BTUART_CPR_thre_mode (1 << 5)
#define BTUART_CPR_afce_mode (1 << 4)
#define BTUART_CPR_apb_data_width (0x3)
/* Uart component version */
#define BTUART_UCV 0x3E
/* Component type register */
#define BTUART_CTR 0X3F
/* Private clock params */
#define BTUART_PRIV_CLOCK_PARAMS 0x200
#define BTUART_PRIV_CLOCK_PARAMS_clk_en 0x1
#define BTUART_PRIV_CLOCK_PARAMS_clk_update (0x1 << 31)
#define BTUART_PRIV_CLOCK_PARAMS_GET_M( val ) ((val >> 1) & 0x7FFF)
#define BTUART_PRIV_CLOCK_PARAMS_GET_N( val ) ((val >> 16) & 0x7FFF)
#define BTUART_PRIV_CLOCK_PARAMS_SET_M( val,regvar ) ( (regvar & ~0xFFFE) | ((val & 0x7FFF) << 1 ))
#define BTUART_PRIV_CLOCK_PARAMS_SET_N( val,regvar ) ( (regvar & ~0x7FFF0000) | ((val & 0x7FFF) << 16 ))
/* Software resets */
#define BTUART_RESETS 0x201
/* General purpose register */
#define BTUART_GENERAL 0x202
/* UART byte count */
#define BTUART_BYTE_COUNT 0x206
/* overflow intr stat */
#define BTUART_OVERFLOW_INTR 0x208
#define BTUART_OVERFLOW_INTR_STAT (0x1 << 0)
#define BTUART_OVERFLOW_INTR_ENABLE (0x1 << 1)
uint64_t get_baytrail_clock( DEV_8250* dev );
int set_baytrail_clock( DEV_8250* dev, int m , int n );
#endif

View File

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

View File

@ -0,0 +1 @@
include ../../../common.mk

View File

@ -0,0 +1,17 @@
/*
* (c) 2021, SWD Embedded Systems Limited, http://www.kpda.ru
*/
#include "externs.h"
#define DEFAULT_CLK 1843200
#define DEFAULT_DIV 16
void sys_ttyinit( TTYINIT *dip )
{
dip->clk = DEFAULT_CLK;
dip->div = DEFAULT_DIV;
}

8
devc/ser8250/externs.c Normal file
View File

@ -0,0 +1,8 @@
/*
* (c) 2010-2014, SWD Embedded Systems Limited, http://www.kpda.ru
*/
#define DEFN
#include "externs.h"

108
devc/ser8250/externs.h Normal file
View File

@ -0,0 +1,108 @@
/*
* (c) 2010-2018, 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/mman.h>
#include <sys/io-char.h>
#include <sys/slog.h>
#include <sys/slogcodes.h>
#include <variant.h>
#if defined( VARIANT_pa6t ) || defined( VARIANT_tegra2 )
/* enable workarounds for bugs in the UARTS of early PA6T-1682's */
/* Note: The implementation fo the 8250 on the nVIDIA Tegra2
behave the same way as the workaround for the PA6T.
We will reuse the same code unless there is some
difference between both implementation. */
#define PA6T_WORKAROUND
#endif
#if defined( VARIANT_mpc8540 )
#define MPC850_WORKAROUND
#endif
#if defined(VARIANT_jace5)
#define TL16C752B_WORKAROUND
#endif
#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 char fcr; /* FCR is write-only register, so keep local copy for read-modify-write */
unsigned dev_id;
#if defined( PA6T_WORKAROUND )
unsigned char irr_fiddle; /* for PA6T-1682 workaround */
unsigned char tx_empty_disable; /* for PA6T-1682 workaround */
#endif
uintptr_t port[REG_TOTAL];
uint32_t *ext_regs; /* Extended UART registers */
uint32_t (*read_8250)( uintptr_t port ); /* HW access function pointers */
void (*write_8250)( uintptr_t port, uint32_t val );
} DEV_8250;
#endif
struct dev_list {
struct dev_list *next;
DEV_8250 *device;
int iid;
};
EXT TTYCTRL ttyctrl;
EXT struct dev_list *devices;
struct pci_list_t {
int vendorID;
int deviceID;
};
#define MAX_DEVICES 16
#define FIFO_SIZE 16
#include "proto.h"
#include "baytrail.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

384
devc/ser8250/init.c Normal file
View File

@ -0,0 +1,384 @@
/*
* (c) 2010-2018, SWD Embedded Systems Limited, http://www.kpda.ru
*/
#include "externs.h"
#include <sys/mman.h>
void set_port( DEV_8250 *dev, unsigned port, unsigned mask, unsigned data )
{
unsigned char c;
c = dev->read_8250( port );
dev->write_8250( port, (c & ~mask) | (data & mask) );
}
static void clear_device( DEV_8250 *dev )
{
unsigned char tmp __attribute__((unused));
dev->write_8250( dev->port[REG_IE], IE_CLR_ALL ); /* Disable all interrupts */
tmp = dev->read_8250( dev->port[REG_LS] ); /* Clear Line Status Interrupt */
tmp = dev->read_8250( dev->port[REG_RX] ); /* Clear RX Interrupt */
tmp = dev->read_8250( dev->port[REG_TX] ); /* Clear TX Interrupt */
tmp = dev->read_8250( dev->port[REG_MS] ); /* Clear Modem Interrupt */
dev->write_8250( dev->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. */
do {
} while( (dev->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( dev, port[REG_MC], MCR_OUT2, MCR_OUT2 );
clear_device( dev );
/* Add it to the interrupt list */
owner = &devices;
for ( ;; )
{
curr = *owner;
if ( curr == NULL )
{
curr = malloc( sizeof( *curr ) );
if ( curr == NULL )
{
slogf( _SLOG_SETCODE( _SLOGC_CHAR, 0 ), _SLOG_ERROR, "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 )
dev->write_8250( port[REG_IE], IE_SET_ALL );
else
dev->write_8250( port[REG_IE], (IE_SET_ALL & ~IE_RX) );
/* Mask Baytrail HSUART Overflow Interrupt */
if ( dev->dev_id == BTUART_PCI_DEVICE_0 || dev->dev_id == BTUART_PCI_DEVICE_1 )
mem_write32( dev->ext_regs + BTUART_OVERFLOW_INTR, BTUART_OVERFLOW_INTR_ENABLE );
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 */
dev->write_8250( port[REG_IE], IE_CLR_ALL ); /* Disable interrupts */
set_port( dev, 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 );
InterruptUnmask( dev->intr, -1 );
dev->intr = _NTO_INTR_SPARE;
return;
}
DEV_8250 * create_device( TTYINIT *dip, unsigned unit, int map_dev_mem, int device_id )
{
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 )
{
slogf( _SLOG_SETCODE( _SLOGC_CHAR, 0 ), _SLOG_ERROR, "io-char: Allocation of device entry failed (%d)", errno );
return (dev);
}
memset( dev, 0, sizeof( *dev ) );
/* Get buffers. */
dev->tty.ibuf.head = dev->tty.ibuf.tail = dev->tty.ibuf.buff = malloc( dev->tty.ibuf.size = dip->isize );
if ( dev->tty.ibuf.head == NULL )
{
slogf( _SLOG_SETCODE( _SLOGC_CHAR, 0 ), _SLOG_ERROR, "io-char: Allocation of input buffer failed (%d)", errno );
free( dev );
return (NULL);
}
dev->tty.obuf.head = dev->tty.obuf.tail = dev->tty.obuf.buff = malloc( dev->tty.obuf.size = dip->osize );
if ( dev->tty.obuf.head == NULL )
{
slogf( _SLOG_SETCODE( _SLOGC_CHAR, 0 ), _SLOG_ERROR, "io-char: Allocation of output buffer failed (%d)", errno );
free( dev->tty.ibuf.head );
free( dev );
return (NULL);
}
dev->tty.cbuf.head = dev->tty.cbuf.tail = dev->tty.cbuf.buff = malloc( dev->tty.cbuf.size = dip->csize );
if ( dev->tty.cbuf.head == NULL )
{
slogf( _SLOG_SETCODE( _SLOGC_CHAR, 0 ), _SLOG_ERROR, "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->clk = 0;
dev->tty.baud = dip->baud;
dev->tty.fifo = dip->fifo;
if ( device_id )
dev->dev_id = device_id;
#if defined( __X86__ )
/* If specified, map device physical memory instead of I/O space and set the h/w access function pointers */
if ( map_dev_mem )
{
port = (uintptr_t)mmap_device_memory( NULL,
(sizeof( dev->port ) / sizeof( dev->port[0] )) << dip->port_shift,
PROT_READ | PROT_WRITE | PROT_NOCACHE,
MAP_SHARED | MAP_PHYS,
dip->port );
dev->read_8250 = &mem_read;
dev->write_8250 = &mem_write;
} else
#endif
{
port = mmap_device_io( (sizeof( dev->port ) / sizeof( dev->port[0] )) << dip->port_shift, dip->port );
dev->read_8250 = &io_read;
dev->write_8250 = &io_write;
}
for ( i = 0; i < sizeof( dev->port ) / sizeof( dev->port[0] ); ++i )
{
dev->port[i] = port;
port += 1 << dip->port_shift;
}
dev->intr = dip->intr;
/* Get Intel E38xx Baytrail HSUART clock and mmap extended registers */
if ( dev->dev_id == BTUART_PCI_DEVICE_0 || dev->dev_id == BTUART_PCI_DEVICE_1 )
{
dev->ext_regs = mmap_device_memory( NULL,
BTUART_REG_CNT,
PROT_READ | PROT_WRITE | PROT_NOCACHE,
MAP_SHARED | MAP_PHYS,
dip->port );
if ( (dev->clk = get_baytrail_clock( dev )) == 0 )
return (NULL);
}
if ( dev->clk == 0 )
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 );
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 );
}
int enable_device( DEV_8250 *dev )
{
unsigned char reg;
/* 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);
}
}
/* Extract the rx and tx fifo sizes */
dev->rx_fifo = dev->tty.fifo & 0xf;
dev->tx_fifo = (dev->tty.fifo >> 4) & 0xf;
/* Set RX fifo trigger level */
switch ( dev->rx_fifo )
{
case 1:
default:
dev->fcr = FCR_RX_TRIG_1;
break;
case 4:
dev->fcr = FCR_RX_TRIG_4;
break;
case 8:
dev->fcr = FCR_RX_TRIG_8;
break;
case 14:
dev->fcr = FCR_RX_TRIG_14;
break;
}
dev->fcr |= FCR_FIFO_ENABLE;
dev->write_8250( dev->port[REG_FC], dev->fcr );
#if defined( VARIANT_jacinto )
dev->write_8250( dev->port[REG_PWREMU], (PWREMU_FREE | PWREMU_URRST | PWREMU_UTRST) ); /* Enable Tx/Rx in Free running mode */
dev->write_8250( dev->port[REG_MDR], 0x0 ); /* 16x over-sampling */
#endif
/* Turn on DTR and RTS */
set_port( dev, dev->port[REG_MC], MCR_DTR | MCR_RTS, MCR_DTR | MCR_RTS );
/* Get current MSR stat */
reg = dev->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 );
/* Attach the resource manager */
ttc( TTC_INIT_ATTACH, &dev->tty, 0 );
return (EOK);
}

191
devc/ser8250/intr.c Normal file
View File

@ -0,0 +1,191 @@
/*
* (c) 2010-2014, 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 )
{
#if defined( MPC8540_WORKAROUND )
/* No break, can re-enable the LSR interrupt. */
dev->write_8250( dev->port[REG_IE], IE_SET_ALL );
#endif
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;
#if defined( MPC8540_WORKAROUND )
/* On the MPC8540 chip, when a break occurs, the lsr interrupt stays
* asserted until a character is input. We therefore need to disable the
* lsr interrupt to prevent an interrupt overflow condition. */
dev->write_8250( dev->port[REG_IE], (IE_SET_ALL & ~IE_LS) );
#endif
} else {
#if defined( MPC8540_WORKAROUND )
/* No break, can re-enable the LSR interrupt. */
dev->write_8250( dev->port[REG_IE], IE_SET_ALL );
#endif
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;
#if defined( PA6T_WORKAROUND )
int first = 1;
#endif
do {
something_happened = 0;
for ( dev = list->device; dev != NULL; dev = dev->next )
{
unsigned iir;
uintptr_t *port = dev->port;
#if defined( PA6T_WORKAROUND )
if ( first && (dev->tx_empty_disable <= 50) )
dev->tx_empty_disable = 0;
#endif
status = 0;
iir = (dev->read_8250( port[REG_II] ) & 0x07)
#if defined( PA6T_WORKAROUND )
^ dev->irr_fiddle
#endif
;
switch ( iir )
{
#if defined( PA6T_WORKAROUND )
case II_RX ^ 0x1:
/* Work around the fact that the early PA6T-1682's have
* the bottom bit of the IRR register flipped. */
dev->irr_fiddle = 0x1;
/* fall through */
#endif
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. */
while ( (lsr = dev->read_8250( port[REG_LS] )) & LSR_RXRDY )
{
c = dev->read_8250( port[REG_RX] );
c |= process_lsr( dev, lsr );
status |= tti( &dev->tty, c );
};
break;
#if defined( PA6T_WORKAROUND )
case II_TX ^ 0x1:
/* Work around the fact that the early PA6T-1682's have
* the bottom bit of the IRR register flipped. */
dev->irr_fiddle = 0x1;
/* fall through */
#endif
case II_TX: /* Transmit buffer empty */
#if defined( TL16C752B_WORKAROUND )
/* ugly, bug workaround false interrupt
* from TL16C752B dual uart */
while ( !(dev->read_8250( port[REG_LS] ) & LSR_TXRDY) )
;
#endif
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;
#if defined( PA6T_WORKAROUND )
if ( dev->tx_empty_disable > 50 )
{
/* Turn off the TX holding empty interrupt - early
* PA6T-1682's keep reporting it even though the IIR
* read is supposed to turn it off. */
set_port( dev, port[REG_IE], 0x2, 0x0 );
} else
++dev->tx_empty_disable;
#endif
break;
case II_MS: /* Modem change */
msr = dev->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 );
}
}
}
#if defined( PA6T_WORKAROUND )
first = 0;
#endif
} while( something_happened );
return (event);
}

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

@ -0,0 +1,24 @@
/*
* (c) 2010-2014, SWD Embedded Systems Limited, http://www.kpda.ru
*/
#include "externs.h"
#include <stdio.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);
}

View File

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

View File

@ -0,0 +1 @@
include ../../../common.mk

View File

@ -0,0 +1 @@
include ../../../common.mk

View File

@ -0,0 +1 @@
include ../../../common.mk

View File

@ -0,0 +1,17 @@
/*
* (c) 2010, SWD Embedded Systems Limited, http://www.kpda.ru
*/
#include "externs.h"
#define DEFAULT_CLK 1843200
#define DEFAULT_DIV 16
void sys_ttyinit( TTYINIT *dip )
{
dip->clk = DEFAULT_CLK;
dip->div = DEFAULT_DIV;
}

344
devc/ser8250/options.c Normal file
View File

@ -0,0 +1,344 @@
/*
* (c) 2010-2018, SWD Embedded Systems Limited, http://www.kpda.ru
*/
/*
#ifdef __USAGE
%C - Serial driver for 8250's
%C [options] [port[^shift][,irq]] &
Options:
-b number Define initial baud rate (default 57600)
-c clk[/div] Set the input clock rate and divisor
-C number Size of canonical input buffer (default 256)
-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 8)
-T number Set transmit FIFO size (default 14)
-u unit Set serial unit number (default 1)
-m Map device physical memory instead of I/O memory (x86)
-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)
timer_period=value (Internal timer period, default 50ms)
-V 0xXXXX PCI UART vendor
-D 0xXXXX PCI device
-i idx PCI index (default 0)
-v Increase verbosity
Examples:
Autoscan for serial devices from startup system page,
PCI bus and BIOS default ports (x86)
%C -v -b115200
Start serial driver for default x86 ports
%C -b115200 3f8,4 2f8,3
Start serial driver for Intel PCI HSUART
%C -v -b115200 -V 0x8086 -D 0x0f0a
#endif
*/
#include "externs.h"
#include "hw/sysinfo.h"
#include "drvr/hwinfo.h"
/* List of supported PCI devices */
struct pci_list_t pci_list[] = { {BTUART_PCI_VENDOR, BTUART_PCI_DEVICE_0}, /* Intel Atom E38xx Baytrail HSUART*/
{BTUART_PCI_VENDOR, BTUART_PCI_DEVICE_1},
{0, 0} };
int query_hwi_device( TTYINIT *dip, unsigned unit )
{
unsigned hwi_off = hwi_find_device( "uart", unit );
if ( hwi_off != HWI_NULL_OFF )
{
hwi_tag *tag_location = hwi_tag_find( hwi_off, HWI_TAG_NAME_location, 0 );
if ( tag_location )
{
dip->port = tag_location->location.base;
dip->port_shift = tag_location->location.regshift;
}
hwi_tag *tag_irq = hwi_tag_find( hwi_off, HWI_TAG_NAME_irq, 0 );
if ( tag_irq )
dip->intr = tag_irq->irq.vector;
return (1);
}
/* No default device, the base address and irq have been specified */
return (0);
}
unsigned options( int argc, char *argv[] )
{
int opt,
i,
enabled_cnt = 0,
numports = 0,
map_dev_mem = 0;
char *cp;
void *link;
unsigned unit;
unsigned fifo_tx, fifo_rx;
int found_hwi_device = -1;
DEV_8250 *dev_list[MAX_DEVICES];
static TTYINIT devinit = { 0, 0, 0, 57600,
2048, 2048, 256,
0, 0, 0, 0, 0xe8, 0, 0, /* Default Tx fifo 14, Rx fifo 8 */
"/dev/ser",
NULL, 0, 0, 0 };
int32_t pci_id,
pci_idx = 0;
struct pci_dev_info pci_inf;
void *pci_handle = NULL;
off64_t offset;
pci_inf.DeviceId = pci_inf.VendorId = 0;
/* Initialize the devinit to raw mode */
ttc( TTC_INIT_RAW, &devinit, 0 );
sys_ttyinit( &devinit );
unit = 1;
while ( optind < argc )
{
/* Process dash options. */
while ( (opt = getopt( argc, argv, IO_CHAR_SERIAL_OPTIONS "c:t:T:u:mi:V:D:" )) != -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)) )
{
fprintf( stderr, "Illegal rx fifo trigger. \n" );
fprintf( stderr, "Trigger number must be 1, 4, 8 or 14. \n" );
fprintf( stderr, "Rx trigger will not be enabled.\n\n" );
fifo_rx = 0;
} else {
devinit.fifo &= 0xf0;
devinit.fifo |= fifo_rx;
}
break;
case 'T':
fifo_tx = strtoul( optarg, NULL, 0 );
if ( !((fifo_tx == 1) || (fifo_tx == 4) || (fifo_tx == 8) || (fifo_tx == 14)) )
{
fprintf( stderr, "Illegal tx fifo size. \n" );
fprintf( stderr, "Tx fifo size must be 1, 4, 8 or 14. \n" );
fprintf( stderr, "Tx fifo will not be enabled.\n\n" );
fifo_tx = 0;
} else {
devinit.fifo &= 0x0f;
devinit.fifo |= (fifo_tx << 4);
}
break;
case 'u':
unit = strtoul( optarg, NULL, 0 );
break;
case 'm':
map_dev_mem = 1;
break;
/* PCI index */
case 'i':
pci_idx = strtoul( optarg, NULL, 0 );
break;
/* PCI Vendor */
case 'V':
pci_inf.VendorId = strtoul( optarg, NULL, 0 );
break;
/* PCI device */
case 'D':
pci_inf.DeviceId = strtoul( optarg, NULL, 0 );
break;
}
}
/* Process ports and interrupts. */
while ( optind < argc && *(optarg = argv[optind]) != '-' )
{
devinit.port = strtoull( optarg, &optarg, 16 );
if ( *optarg == '^' )
devinit.port_shift = strtoul( optarg + 1, &optarg, 0 );
if ( *optarg == ',' )
devinit.intr = strtoul( optarg + 1, NULL, 0 );
if ( (dev_list[numports] = create_device( &devinit, unit++, map_dev_mem, 0 )) == NULL )
{
slogf( _SLOG_SETCODE( _SLOGC_CHAR, 0 ), _SLOG_ERROR, "io-char: Initialization of /dev/ser%d (port 0x%llx) failed", unit - 1, devinit.port );
fprintf( stderr, "io-char: Initialization of port 0x%llx failed\n", devinit.port );
} else
++numports;
++optind;
}
}
/* PCI UART device if specified */
if ( pci_inf.VendorId && pci_inf.DeviceId )
{
if ( (pci_id = pci_attach( 0 )) == -1 )
{
slogf( _SLOG_SETCODE( _SLOGC_CHAR, 0 ), _SLOG_ERROR, "io-char: Unable to attach to PCI server (%d)", errno );
fprintf( stderr, "io-char: Unable to attach to PCI server (%d)\n", errno );
return (0);
}
if ( (pci_handle = pci_attach_device( NULL, PCI_SEARCH_VENDEV | PCI_MASTER_ENABLE | PCI_INIT_ALL, pci_idx, &(pci_inf) )) == NULL )
{
slogf( _SLOG_SETCODE( _SLOGC_CHAR, 0 ), _SLOG_ERROR, "io-char: Can't attach to PCI device 0x%X:0x%X", pci_inf.VendorId, pci_inf.DeviceId );
fprintf( stderr, "io-char: Can't attach to PCI device 0x%X:0x%X\n", pci_inf.VendorId, pci_inf.DeviceId );
return (0);
}
devinit.port = PCI_MEM_ADDR( pci_inf.CpuBaseAddress[0] );
devinit.intr = pci_inf.Irq;
devinit.port_shift = 2;
map_dev_mem = 1;
if ( (dev_list[numports] = create_device( &devinit, unit++, map_dev_mem, pci_inf.DeviceId )) == NULL )
{
slogf( _SLOG_SETCODE( _SLOGC_CHAR, 0 ), _SLOG_ERROR, "io-char: Initialization of /dev/ser%d (port 0x%llx) failed", unit - 1, devinit.port );
fprintf( stderr, "io-char: Initialization of port 0x%llx failed\n", devinit.port );
return (0);
} else
++numports;
}
if ( numports == 0 )
{
unit = 0;
link = NULL;
/* Scan syspage for UART devices */
for ( ;; )
{
found_hwi_device = query_hwi_device( &devinit, unit );
if ( !found_hwi_device )
break;
/* Getting the UART Clock from the Hwinfo Section if available */
{
unsigned hwi_off = hwi_find_device( "uart", 0 );
if ( hwi_off != HWI_NULL_OFF )
{
hwi_tag *tag = hwi_tag_find( hwi_off, HWI_TAG_NAME_inputclk, 0 );
if ( tag )
devinit.clk = ((tag->inputclk.clk) / (tag->inputclk.div));
}
}
if ( (dev_list[numports] = create_device( &devinit, unit++, map_dev_mem, 0 )) == NULL )
{
slogf( _SLOG_SETCODE( _SLOGC_CHAR, 0 ), _SLOG_ERROR, "io-char: Initialization of /dev/ser%d (port 0x%llx) failed", unit - 1, devinit.port );
fprintf( stderr, "io-char: Initialization of port 0x%llx failed\n", devinit.port );
} else
++numports;
}
/* Default UART ports if present */
for ( ;; )
{
link = query_default_device( &devinit, link );
if ( link == NULL )
break;
if ( (dev_list[numports] = create_device( &devinit, unit++, map_dev_mem, 0 )) == NULL )
{
slogf( _SLOG_SETCODE( _SLOGC_CHAR, 0 ), _SLOG_ERROR, "io-char: Initialization of /dev/ser%d (port 0x%llx) failed", unit - 1, devinit.port );
fprintf( stderr, "io-char: Initialization of port 0x%llx failed\n", devinit.port );
} else
++numports;
}
/* Scan for supported PCI UART devices */
if ( (pci_id = pci_attach( 0 )) != -1 )
{
for ( i = 0; (pci_list[i].vendorID != 0 && pci_list[i].deviceID != 0); i++ )
{
pci_inf.VendorId = pci_list[i].vendorID;
pci_inf.DeviceId = pci_list[i].deviceID;
if ( (pci_handle = pci_attach_device( NULL, PCI_SEARCH_VENDEV | PCI_MASTER_ENABLE | PCI_INIT_ALL, pci_idx, &(pci_inf) )) == NULL )
{
slogf( _SLOG_SETCODE( _SLOGC_CHAR, 0 ), _SLOG_ERROR, "io-char: Can't attach to PCI device 0x%X:0x%X", pci_inf.VendorId, pci_inf.DeviceId );
fprintf( stderr, "io-char: Can't attach to PCI device 0x%X:0x%X\n", pci_inf.VendorId, pci_inf.DeviceId );
return (0);
}
devinit.port = PCI_MEM_ADDR( pci_inf.CpuBaseAddress[0] );
devinit.intr = pci_inf.Irq;
devinit.port_shift = 2;
map_dev_mem = 1;
if ( (dev_list[numports] = create_device( &devinit, unit++, map_dev_mem, pci_inf.DeviceId )) == NULL )
{
slogf( _SLOG_SETCODE( _SLOGC_CHAR, 0 ), _SLOG_ERROR, "io-char: Initialization of /dev/ser%d (port 0x%llx) failed", unit - 1, devinit.port );
fprintf( stderr, "io-char: Initialization of port 0x%llx failed\n", devinit.port );
return (0);
} else
++numports;
}
}
}
/* Enable all ports (Attach ISR and pathname entry) */
for ( i = 0; i < numports; i++ )
{
if ( dev_list[i]->tty.verbose )
{
if ( dev_list[i]->dev_id )
mem_offset64( (void *)dev_list[i]->port[0], NOFD, 1, &offset, 0 );
else
offset = dev_list[i]->port[0];
slogf( _SLOG_SETCODE( _SLOGC_CHAR, 0 ), _SLOG_INFO, "Port .......................... %s (0x%llx)", dev_list[i]->tty.name, offset );
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, "Tx fifo size .................. %d", (dev_list[i]->tty.fifo >> 4) );
slogf( _SLOG_SETCODE( _SLOGC_CHAR, 0 ), _SLOG_INFO, "Rx fifo trigger ............... %d", (dev_list[i]->tty.fifo & 0x0f) );
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 );
}