linux的CH438Q驱动程序

本文介绍了如何在全志A33平台上移植和配置CH438Q驱动程序,以实现1并转8串的串口扩展。内容包括驱动的稳定性、移植注意事项、硬件电路建议以及Linux内核配置和应用测试。代码和更新可在提供的链接中查看。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

ch438q驱动 for linux

该驱动程序经过了长期的负载运行测试,所有串口同时全双工高速率(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 = 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值