该驱动程序经过了长期的负载运行测试,所有串口同时全双工高速率(baudrate115200)下工作稳定。移植时需要根据平台速度,设置合适的读写速度,读写端口的速度宁慢勿快,这是ch438q能长期稳定工作的关键。linux tty设备在文件打开时,读写端口的瞬间,如果读写速度过快,会导致ch438q的访问时序建立失败,而无法正常通讯。该驱动没有使用8250的驱动框架,它们之间存在明显区别。也是担心修改了8250的代码导致其他类8250芯片无法正常工作。
制作硬件电路时,建议使用外置有源晶振。芯片内置的振荡电路配合无源晶振极不稳定,随时都可能停振。这个问题比较难查,希望使用该芯片的朋友引起重视。
如要看更新的代码请看这里,帖子就不同步了。
https://gitee.com/iamyhw/ch438q
硬件接口电路图:
头文件ch438.h:
#ifndef __CH438_H__
#define __CH438_H__
/*
** hardware interface
** E17:CS, E16:RD, E15:WR
** E14~E08: Address
** E07~E00: Data
*/
#define CH438_BASE (SUNXI_PIO_PBASE+0x90) /* PE base */
/* ch438_membase = ioremap(CH438_BASE, GPIO_RANGE); */
#define CH438_CFG0 (ch438_membase+0x00) /* PE Configure Register 0, PE7~PE0 */
#define CH438_CFG1 (ch438_membase+0x04) /* PE Configure Register 1, PE15~PE8 */
#define CH438_CFG2 (ch438_membase+0x08) /* PE Configure Register 2, PE17~PE16 */
#define CH438_CFG3 (ch438_membase+0x0C)
#define CH438_DAT (ch438_membase+0x10) /* PE Data Register, PE17~PE0 */
#define CH438_DRV0 (ch438_membase+0x14) /* PE Multi-Driving 0, PE15~PE0 */
#define CH438_DRV1 (ch438_membase+0x18) /* PE Multi-Driving 1, PE17~PE16 */
#define CH438_PULL0 (ch438_membase+0x1C) /* PE Pull 0, PE15~PE0 */
#define CH438_PULL1 (ch438_membase+0x20) /* PE Pull 1, PE17~PE16 */
#define GPIO_RANGE 0x24
#define CH438_CS 17
#define CH438_RD 16
#define CH438_WR 15
struct ch438_pin {
int rst,pint;
int irqnum;
};
struct ch438_port {
struct uart_port port;
char name[16];
unsigned char id;
unsigned char acr;
unsigned char ier;
unsigned char lcr;
unsigned char mcr;
unsigned char fcr;
unsigned char dll;
unsigned char dlh;
unsigned char msr_saved_flags;
unsigned int lsr_break_flag;
unsigned int io_num;
struct ch438_pin *pin;
};
#define CH438_MEM_BASE SUNXI_GPIOE_BASE
#define RBR_RO 0x00 /* Received Buffer Register */
#define THR_WO 0x00 /* Transmit Holding Register */
#define IER_RW 0x01 /* Interrupt Enable Register */
#define IIR_RO 0x02 /* Interrupt Identification Register */
#define FCR_WO 0x02 /* FIFO Control Register */
#define LCR_RW 0x03 /* Line Control Register */
#define MCR_RW 0x04 /* Modem Control Register */
#define LSR_RO 0x05 /* Line Status Register */
#define MSR_RO 0x06 /* Modem Status Register */
#define SCR_RW 0x07 /* Scratch Register */
#define DLL_RW 0x00 /* Divisor latch (LSB) */
#define DLH_RW 0x01 /* Divisor latch (MSB) */
/* CH438 inside uart0~7 Status Register */
#define SSR_RO 0x4F /* all interrupt status */
/* IER register bits */
#define BIT_IER_RESET BIT(7) /* 1: reset */
#define BIT_IER_LOWPWR BIT(6) /* 1: close inside base clk */
#define BIT_IER_SLP BIT(5) /* 1: SLP[uart0] */
#define BIT_IER_CK2X BIT(5) /* 1: CK2X[uart1~7]*/
#define BIT_IER_MSI BIT(3) /* 1: enable MODEM status change interrupt */
#define BIT_IER_RLSI BIT(2) /* 1: enable receive line state interrupt */
#define BIT_IER_THRI BIT(1) /* 1: enable transmit is null interrupt */
#define BIT_IER_RDI BIT(0) /* 1: enable receive data interrupt */
/* IIR Interrupt ID Register */
#define BIT_IIR_FIFOENS1 BIT(7) /* FIFOs is used */
#define BIT_IIR_FIFOENS0 BIT(6) /* FIFOs is used */
/* interrupt type:*/
#define BIT_IIR_IID3 BIT(3)
#define BIT_IIR_IID2 BIT(2)
#define BIT_IIR_IID1 BIT(1)
#define BIT_IIR_NOINT BIT(0)
#define BIT_IIR_IID_MASK (BIT(3)|BIT(2)|BIT(1)|BIT(0))
#define BIT_IIR_FEFLAG_MASK (BIT(7)|BIT(6))
/* Interrupt ID */
#define INT_MODEM_CHANGE 0x00 /* MODEM input change */
#define INT_NOINT 0x01 /* none interrupt */
#define INT_THR_EMPTY 0x02 /* THR is empty */
#define INT_RCV_SUCCESS 0x04 /* receive success */
#define INT_RCV_LINES 0x06 /* receive line status */
#define INT_RCV_OVERTIME 0x0C /* receive overtime */
/* FIFO Control Register */
/* Trigger Point: 00:1Byte, 01:16Byte, 10:64Byte, 11:112Byte */
#define BIT_FCR_RECVTG1 BIT(7) /* Receiver TriGger 1 */
#define BIT_FCR_RECVTG0 BIT(6) /* Receiver TriGger 0 */
#define BIT_FCR_FT_112 (BIT(7)|BIT(6))
#define BIT_FCR_FT_64 BIT(7)
#define BIT_FCR_FT_16 BIT(6)
#define BIT_FCR_FT_01 0x00
#define BIT_FCR_TFIFORST BIT(2) /* Transmit FIFO Reset */
#define BIT_FCR_RFIFORST BIT(1) /* Receive FIFO Reset */
#define BIT_FCR_FIFOEN BIT(0) /* FIFO Enable */
/* Line Control Register */
/* DLAB: 1:read/write DLL/DLH, 0:read/write RBR/THR/IER */
#define BIT_LCR_DLAB BIT(7)
#define BIT_LCR_SBC BIT(6) /* 1:create BREAK */
/* Parity Format: when PAREN=1, 00:ODD,01:EVEN,10:MARK,11:SPACE */
#define BIT_LCR_PARMODE1 BIT(5) /* Set Parity Style */
#define BIT_LCR_PARMODE0 BIT(4) /* Set parity Style */
#define BIT_PARITY_MASK (BIT(5)|BIT(4)) /* Parity Mask */
#define BIT_LCR_EPAR (1<<4) /* Parity EVEN */
#define BIT_LCR_OPAR (0<<4) /* Parity ODD */
#define BIT_LCR_PARITY BIT(3) /* 1:enable Parity */
#define BIT_LCR_STOP BIT(2) /* 1:2StopBits, 0:1StopBits */
/* Set WrodLen: 00:5, 01:6, 10:7, 11:8 */
#define BIT_LCR_WORDSZ1 BIT(1) /* Word Length Select Bit1 */
#define BIT_LCR_WORDSZ0 BIT(0) /* Word Length Select Bit0 */
#define BIT_LCR_WLEN_MASK (BIT(1)|BIT(0))
#define BIT_LCR_WLEN_8 0x03
#define BIT_LCR_WLEN_7 0x02
#define BIT_LCR_WLEN_6 0x01
#define BIT_LCR_WLEN_5 0x00
/* Modem Control Register */
#define BIT_MCR_AFE BIT(5) /* 1:enable CTS/RTS */
#define BIT_MCR_LOOP BIT(4) /* 1:enable Loop-back mode */
#define BIT_MCR_OUT2 BIT(3) /* 1:enable irq hw output !!! */
#define BIT_MCR_OUT1 BIT(2) /* user defined output bit */
#define BIT_MCR_RTS BIT(1) /* Reauest to Send */
#define BIT_MCR_DTR BIT(0) /* Data Terminal Ready */
/* Line Status Register */
#define BIT_LSR_RXFIFOE BIT(7) /* Receive FIFO Error */
#define BIT_LSR_TEMT BIT(6) /* Transmit Empty */
#define BIT_LSR_THRE BIT(5) /* Transmit Holding Register Empty */
#define BIT_LSR_BI BIT(4) /* Break Interrupt */
#define BIT_LSR_FE BIT(3) /* Framing Error */
#define BIT_LSR_PE BIT(2) /* Parity Error */
#define BIT_LSR_OE BIT(1) /* Overrun Error */
#define BIT_LSR_DR BIT(0) /* Data Ready */
#define LSR_BRK_ERROR_BITS 0x1E /* BI|FE|PE|OE bits */
/* Modem Status Register */
#define BIT_MSR_DCD BIT(7) /* Data Carrier Detect */
#define BIT_MSR_RI BIT(6) /* Ring Indicator */
#define BIT_MSR_DSR BIT(5) /* Data Set Ready */
#define BIT_MSR_CTS BIT(4) /* Clear to Send */
#define BIT_MSR_DDCD BIT(3) /* Delta Data Carrier Detect */
#define BIT_MSR_TERI BIT(2) /* Trailing Edge Ring Indicator */
#define BIT_MSR_DDSR BIT(1) /* Delta Data Set Ready */
#define BIT_MSR_DCTS BIT(0) /* Delta Clear to Send */
#define BIT_MSR_ANY_DELTA 0x0F
#define MSR_SAVE_FLAGS BIT_MSR_ANY_DELTA
#define CH438_IIR_FIFOS_ENABLED 0xC0 /* enable FIFO */
#endif
C文件ch438.c:
#include <linux/io.h>
#include <linux/irq.h>
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/gpio.h>
#include <linux/ctype.h>
#include <linux/slab.h>
#include <linux/proc_fs.h>
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_device.h>
#include <linux/console.h>
#include <linux/serial_core.h>
#include <linux/platform_device.h>
#include <mach/hardware.h>
#include <mach/platform.h>
#include <linux/pinctrl/consumer.h>
#include <linux/pinctrl/pinconf-sunxi.h>
#include <mach/sys_config.h>
#include <mach/gpio.h>
#include <mach/irqs.h>
#include <asm/io.h>
#include "ch438.h"
#define CH438_UART_NAME "usart"
#define CH438_DEV_NAME "ttySC"
#define CH438_DEV_NUM 8
#define CH438_CLOCK_RATE (22118400/12)
/* debug control */
enum {
DBG_ERR = 1U << 0,
DBG_DBG = 1U << 1,
};
static u32 ch438_debug_mask = 0;
#define dprintk(level, fmt, arg...) \
do { \
if (unlikely(ch438_debug_mask & level)) { \
printk("%s()%d - ", __func__, __LINE__); \
printk(fmt, ##arg); \
} \
} while (0)
#define SERIAL_ERR(fmt, arg...) dprintk(DBG_ERR, fmt, ##arg)
#define SERIAL_DBG(fmt, arg...) dprintk(DBG_DBG, fmt, ##arg)
#define UART_TO_SPORT(port) ((struct ch438_port*)port)
static struct ch438_pin ch438_pin;
static spinlock_t ch438_rwlock;
static unsigned long ch438_mapbase=0;
static unsigned char __iomem *ch438_membase=NULL; /*read/write[bwl]*/
static const u8 REG[] = {
0x00,0x10,0x20,0x30,0x08,0x18,0x28,0x38};
/* NO CONFIG_SERIAL_SUNXI_CONSOLE, this device don't as console! */
static struct platform_device ch438_device[CH438_DEV_NUM];
static struct ch438_port ch438_ports[CH438_DEV_NUM];
/* Access macros for the CH438 UART */
#define UART_GET_CHR(p) RdReg(REG[p->line]|RBR_RO)
#define UART_PUT_CHR(p, c) WrReg(REG[p->line]|THR_WO, c)
#define UART_GET_IER(p) RdReg(REG[p->line]|IER_RW)
#define UART_PUT_IER(p, c) WrReg(REG[p->line]|IER_RW, c)
#define UART_GET_IIR(p) RdReg(REG[p->line]|IIR_RO)
#define UART_GET_FCR(p) RdReg(REG[p->line]|FCR_WO)
#define UART_PUT_FCR(p, c) WrReg(REG[p->line]|FCR_WO, c)
#define UART_GET_MSR(p) RdReg(REG[p->line]|MSR_RO)
#define UART_GET_LSR(p) RdReg(REG[p->line]|LSR_RO)
#define UART_GET_LCR(p) RdReg(REG[p->line]|LCR_RW)
#define UART_PUT_LCR(p, c) WrReg(REG[p->line]|LCR_RW, c)
#define UART_GET_MCR(p) RdReg(REG[p->line]|MCR_RW)
#define UART_PUT_MCR(p, c) WrReg(REG[p->line]|MCR_RW, c)
#define UART_GET_SCR(p) RdReg(REG[p->line]|SCR_RW)
#define UART_PUT_SCR(p, c) WrReg(REG[p->line]|SCR_RW, c)
#define UART_PUT_DLH(p, c) WrReg(REG[p->line]|DLH_RW, c)
#define UART_PUT_DLL(p, c) WrReg(REG[p->line]|DLL_RW, c)
#define UART_GET_SSR() RdReg(SSR_RO)
static void ch438_gpio_init(void)
{
struct ch438_pin *pin = &ch438_pin;
script_item_u val;
char *para = "ch438"; /* see as sys_config.fex */
int ret=0;
int reg = 0;
SERIAL_DBG("ch438 gpio init.\n");
__raw_writel(0x55555555, CH438_PULL0); /* all pull up */
__raw_writel(0x00000005, CH438_PULL1); /* all pull up */
__raw_writel(0x11111111, CH438_CFG0); /* PE7~PE0: output */
__raw_writel(0x11111111, CH438_CFG1); /* WR[15] ADDR[14~8] output */
__raw_writel(0x00000011, CH438_CFG2); /* CS[17], RD[16] */
/* ch438 RST: PF01 */
script_get_item(para, "ch438_rst", &val);
pin->rst = val.gpio.gpio;
ret=gpio_request(pin->rst, NULL);
gpio_direction_output(pin->rst, 1);
/* ch438 INT: PB06 */
script_get_item(para, "ch438_int", &val);
pin->pint = val.gpio.gpio;
ret=gpio_request(pin->pint, NULL);
gpio_direction_input(pin->pint);
/* Allocate the IRQ */
pin->irqnum = gpio_to_irq(pin->pint);
/* hw reset ch438 */
SERIAL_DBG("ch438 chip hw reset!\n");
gpio_set_value(pin->rst, 1);
udelay(10);
gpio_set_value(pin->rst, 0);
udelay(100);
gpio_set_value(pin->rst, 1);
reg = __raw_readl(CH438_DAT);
/* Reset default: CS=1, WR=1, RD=1 */
__raw_writel(reg|(1<<CH438_CS)|(1<<CH438_WR)|(1<<CH438_RD), CH438_DAT);
}
static void ch438_gpio_free(void)
{
struct ch438_pin *pin = &ch438_pin;
gpio_free(pin->rst);
gpio_free(pin->pint);
}
static void WrReg(unsigned char addr,unsigned char dat)
{
unsigned long flags;
int reg;
spin_lock_irqsave(&ch438_rwlock, flags);
/* addr output */
__raw_writel(0x11111111, CH438_CFG0);
ndelay(20);
/* CS=0, RD=1, WR=0, 00... */
reg =