Драйвер devc-ser8250 для ЗОСРВ Нейтрино редакции 2020
Этот коммит содержится в:
Коммит
23ee7029b9
|
@ -0,0 +1,2 @@
|
|||
LIST=hardware
|
||||
include recurse.mk
|
|
@ -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
|
||||
```
|
|
@ -0,0 +1,2 @@
|
|||
LIST=DEVC
|
||||
include recurse.mk
|
|
@ -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)
|
|
@ -0,0 +1,2 @@
|
|||
LIST=CPU
|
||||
include recurse.mk
|
|
@ -0,0 +1,2 @@
|
|||
LIST=VARIANT
|
||||
include recurse.mk
|
|
@ -0,0 +1 @@
|
|||
include ../../../common.mk
|
|
@ -0,0 +1 @@
|
|||
include ../../../common.mk
|
|
@ -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;
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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
|
|
@ -0,0 +1,2 @@
|
|||
LIST=VARIANT
|
||||
include recurse.mk
|
|
@ -0,0 +1 @@
|
|||
include ../../../common.mk
|
|
@ -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;
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
/*
|
||||
* (c) 2010-2014, SWD Embedded Systems Limited, http://www.kpda.ru
|
||||
*/
|
||||
|
||||
|
||||
#define DEFN
|
||||
|
||||
#include "externs.h"
|
|
@ -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
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -0,0 +1,2 @@
|
|||
LIST=VARIANT
|
||||
include recurse.mk
|
|
@ -0,0 +1 @@
|
|||
include ../../../common.mk
|
|
@ -0,0 +1 @@
|
|||
include ../../../common.mk
|
|
@ -0,0 +1 @@
|
|||
include ../../../common.mk
|
|
@ -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;
|
||||
}
|
|
@ -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 );
|
||||
}
|
||||
|
||||
if ( enable_device( dev_list[i] ) != -1 )
|
||||
enabled_cnt++;
|
||||
}
|
||||
|
||||
return (enabled_cnt);
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
#
|
||||
# (c) 2010, SWD Embedded Systems Limited, http://www.kpda.ru
|
||||
#
|
||||
|
||||
define PINFO
|
||||
PINFO DESCRIPTION=Character device driver for the 8250 chipset
|
||||
endef
|
|
@ -0,0 +1,2 @@
|
|||
LIST=VARIANT
|
||||
include recurse.mk
|
|
@ -0,0 +1 @@
|
|||
include ../../../common.mk
|
|
@ -0,0 +1 @@
|
|||
include ../../../common.mk
|
|
@ -0,0 +1,17 @@
|
|||
/*
|
||||
* (c) 2010, SWD Embedded Systems Limited, http://www.kpda.ru
|
||||
*/
|
||||
|
||||
|
||||
#include "externs.h"
|
||||
|
||||
|
||||
#define DEFAULT_CLK 7372800
|
||||
#define DEFAULT_DIV 16
|
||||
|
||||
|
||||
void sys_ttyinit( TTYINIT *dip )
|
||||
{
|
||||
dip->clk = DEFAULT_CLK;
|
||||
dip->div = DEFAULT_DIV;
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
/*
|
||||
* (c) 2010-2014, SWD Embedded Systems Limited, http://www.kpda.ru
|
||||
*/
|
||||
|
||||
|
||||
DEV_8250 *create_device( TTYINIT *dip, unsigned unit, int map_dev_mem, int dev_id );
|
||||
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( DEV_8250 *dev, unsigned port, unsigned mask, unsigned data );
|
||||
const struct sigevent *ser_intr( void *area, int id );
|
||||
unsigned options( int argc, char *argv[] );
|
|
@ -0,0 +1,15 @@
|
|||
/*
|
||||
* (c) 2010-2014, 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);
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
/*
|
||||
* (c) 2010-2014, SWD Embedded Systems Limited, http://www.kpda.ru
|
||||
*/
|
||||
|
||||
|
||||
#include "externs.h"
|
||||
|
||||
|
||||
int edit( TTYDEV *dev, unsigned c )
|
||||
{
|
||||
dev = dev;
|
||||
c = c;
|
||||
|
||||
return (0);
|
||||
}
|
|
@ -0,0 +1,182 @@
|
|||
/*
|
||||
* (c) 2010-2014, SWD Embedded Systems Limited, http://www.kpda.ru
|
||||
*/
|
||||
|
||||
|
||||
#include "externs.h"
|
||||
|
||||
|
||||
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( dev, port[REG_LC], LCR_BREAK, arg1 &_SERCTL_BRK ? LCR_BREAK : 0 );
|
||||
|
||||
if ( arg1 & _SERCTL_DTR_CHG )
|
||||
set_port( dev, port[REG_MC], MCR_DTR, arg1 & _SERCTL_DTR ? MCR_DTR : 0 );
|
||||
|
||||
if ( arg1 & _SERCTL_RTS_CHG )
|
||||
set_port( dev, port[REG_MC], MCR_RTS, arg1 & _SERCTL_RTS ? MCR_RTS : 0 );
|
||||
|
||||
return (0);
|
||||
|
||||
case TTO_LINESTATUS:
|
||||
return (((dev->read_8250(port[REG_MS]) << 8) | dev->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 ( dev->read_8250( port[REG_LS] ) & LSR_TXRDY )
|
||||
{
|
||||
nbytes_tx = 0;
|
||||
while ( bup->cnt > 0 && nbytes_tx < dev->tx_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 */
|
||||
dev->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( dev, 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;
|
||||
unsigned value;
|
||||
|
||||
/* Set Baud rate */
|
||||
value = (dev->tty.baud == 0) ? 0 : (dev->clk / (dev->tty.baud * dev->div));
|
||||
|
||||
dev_lock( &dev->tty );
|
||||
set_port( dev, port[REG_LC], LCR_DLAB, LCR_DLAB );
|
||||
set_port( dev, port[REG_DL0], 0xff, value & 0xff );
|
||||
set_port( dev, port[REG_DL1], 0xff, value >> 8 );
|
||||
set_port( dev, port[REG_LC], LCR_DLAB, 0 );
|
||||
dev_unlock( &dev->tty );
|
||||
|
||||
if ( dev->tty.c_cflag & CREAD && !(dev->read_8250(port[REG_IE]) & 0x1) )
|
||||
{
|
||||
/* Enable receiver */
|
||||
dev->write_8250( dev->port[REG_FC], dev->fcr | FCR_RX_FIFO_RESET ); /* Clear RX FIFO */
|
||||
set_port( dev, port[REG_IE], 0x1, 0x1 ); /* Enable receive interrupt */
|
||||
} else
|
||||
if ( !(dev->tty.c_cflag & CREAD) && (dev->read_8250(port[REG_IE]) & 0x1) )
|
||||
{
|
||||
/* Disable receiver */
|
||||
set_port( dev, 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( dev, 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) && (dev->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);
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
* (c) 2010-2014, SWD Embedded Systems Limited, http://www.kpda.ru
|
||||
*/
|
||||
|
||||
|
||||
static inline uint32_t io_read( uintptr_t port )
|
||||
{
|
||||
return in8( port );
|
||||
}
|
||||
|
||||
|
||||
static inline void io_write( uintptr_t port, uint32_t val )
|
||||
{
|
||||
out8( port, (uint8_t)val );
|
||||
}
|
||||
|
||||
|
||||
static inline uint32_t mem_read( uintptr_t port )
|
||||
{
|
||||
return (*(volatile uint8_t*)port);
|
||||
}
|
||||
|
||||
|
||||
static inline void mem_write( uintptr_t port, uint32_t val )
|
||||
{
|
||||
(*(volatile uint8_t*)port) = (uint8_t)val;
|
||||
}
|
||||
|
||||
|
||||
static inline uint32_t mem_read32( uint32_t *port )
|
||||
{
|
||||
return (*(volatile uint32_t*)port);
|
||||
}
|
||||
|
||||
|
||||
static inline void mem_write32( uint32_t *port, uint32_t val )
|
||||
{
|
||||
(*(volatile uint32_t*)port) = (uint32_t)val;
|
||||
}
|
|
@ -0,0 +1,2 @@
|
|||
LIST=VARIANT
|
||||
include recurse.mk
|
|
@ -0,0 +1,2 @@
|
|||
|
||||
include ../../../common.mk
|
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
* (c) 2010-2014, SWD Embedded Systems Limited, http://www.kpda.ru
|
||||
*/
|
||||
|
||||
|
||||
#include "externs.h"
|
||||
|
||||
|
||||
/* Should query configuration manager for port assignments */
|
||||
void * query_default_device( TTYINIT *dip, void *link )
|
||||
{
|
||||
switch ( (unsigned)link )
|
||||
{
|
||||
case 0:
|
||||
dip->port = 0x3f8;
|
||||
dip->port_shift = 0;
|
||||
dip->intr = 4;
|
||||
return (void *)1;
|
||||
case 1:
|
||||
dip->port = 0x2f8;
|
||||
dip->port_shift = 0;
|
||||
dip->intr = 3;
|
||||
return (void *)2;
|
||||
}
|
||||
|
||||
return (NULL);
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
/*
|
||||
* (c) 2010-2014, 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;
|
||||
}
|
Загрузка…
Ссылка в новой задаче