1 . 工作原理
CQ8401 spi总线(master)通过两个SC16IS752芯片(slave),外扩232,GPS,485,422;一块SC16IS752芯片最多只能扩两个设备。
涉及到的知识:SPI协议(参考网上资料有很多), UART工作原理(参考设备驱动)。
1.1 原理图:
原理图说明:
SCLK: The divider of the SSICLK, I2SCLK and USB clock can be changed by programming CFCR1.SSIFR, CFCR2.I2SFR and CFCR1.UFR, respectively.
/* arch/mips/cq8401/common/setup.c */
clx_clocks.ssiclk = __cpm_get_ssiclk();
static __inline__ unsigned int __cpm_get_ssiclk(void)
{
unsigned int ssiclk;
unsigned long cfcr = REG_CPM_CFCR;
unsigned long plcr = REG_CPM_PLCR1;
if ((plcr & CPM_PLCR1_PLL1EN) && (cfcr & CPM_CFCR_SSI))
ssiclk = __cpm_get_pllout() /
(((cfcr&CPM_CFCR_SSIFR_MASK)>>CPM_CFCR_SSIFR_BIT)+1);
else
ssiclk = CLX_EXTAL;
return ssiclk;
}
CS:芯片片选信号,低有效;在安联板上有2块SC16IS752芯片,驱动中应该注意CS信号线
SO: 芯片输出 , CQ8401接收
SI: CQ8401 输出,芯片 接收
IRQ:中断线
GPIO 寄存器:该方案中没有用
16C45: 外接 232,GPS 或者 485,422
1.2数据流程
数据发送: CQ8401--> SPI ---> SC16IS752 -->232/485/GPS/422
数据接收: 232/485/GPS/422 有数据来产生中断,数据存储在SC16IS752 FIFO中--> SPI --->CQ8401处理中断,接收数据
2 驱动分析
本驱动最重要的是要注意在发送和接收的时候,对CS片选信号线要主动的去拉低。比如在发送中,你是知道要向那个芯片发数据的,所以在发送前要将对应的芯片CS信号线拉低。在接收过程中,由于2块芯片的接收中断是一直开启的,所以来了中断,你在接收中断函数中能通过struct uart_port *port = dev_id,port->line 来知道是那块芯片, port->line 对应如下:
port->line=0 芯片一 cs1 接232
port->line=1 芯片一 cs1 接GPS
port->line=2 芯片二 cs2 接422
port->line=3 芯片二 cs2 接422
根据这个关系来确定去拉低那跟CS片选信号线。
关于初始化部分,这里不在阐述,重点讲UART的ops 操作
static struct uart_ops s752_pops = {
tx_empty: s752_tx_empty,
set_mctrl: s752_set_mctrl,
get_mctrl: s752_get_mctrl,
stop_tx: s752_stop_tx,
start_tx: s752_start_tx,
stop_rx: s752_stop_rx,
enable_ms: s752_enable_ms,
break_ctl: s752_break_ctl,
startup: s752_startup,
shutdown: s752_shutdown,
set_termios: s752_set_termios,
type: s752_type,
release_port: s752_release_port,
request_port: s752_request_port,
config_port: s752_config_port,
verify_port: s752_verify_port,
};
这里主要讲以下几个部分:
2.1 start_tx:
s752_start_tx , UART open操作
static int s752_startup(struct uart_port *port)
{
uint16_t ret;
int retval=0;
int i=0;
/* 根据port->line 来选择ce1 或者 ce2 */
//spin_lock(&port->lock);
while((!(__ssi_transfer_end())) || __ssi_is_busy());
if(port->line < 2){
__ssi_select_ce(); //spi_ce1 被片选中
sc752_ce1_init(port); // 初始化,分析如下
}
else{
__ssi_select_ce2(); //spi_ce2 被片选中
sc752_ce2_init(port); // 初始化,分析如下
}
//spin_unlock(&port->lock);
#ifdef _DEBUG_S752
printk("=====>AFT REG_SSI_CR0=0x%08x\n",REG_SSI_CR0);
printk("s752_startup exit\n");
#endif
return 0;
}
static void sc752_ce1_init(struct uart_port *port)
{
int retval=0;
/* clears the contents of the transmit and receive FIFO and resets the FIFO
level logic and enable the transmit and receive FIFO */
s752_write_reg(FCR,port->line,0x07);
delay(5);
#if 0
/* 232 uart surport HardwareFlow*/
printk("set hartdware flow control!!\n");
if(!port->line)
HardwareFlow(); //channel A 232 hardwareflow
/*Force RTS low level for sofeware flow control*/
s752_write_reg(MCR,ChannelA,0x02);
#endif
/* 中断注册,这里用的是共享中断,中断号IRQ_GPIO0+GPIO */
retval = request_irq(port->irq, s752_int, IRQF_SHARED, "ttySC_CE1",port);
if (retval){
printk("request irq SPI_CE1 error:%d\n",retval);
return retval;
}
/* only enable the RHR interrupt in s752_startup*/
s752_write_reg(IER, port->line, 0x01);
}
static void sc752_ce2_init(struct uart_port *port)
{
unsigned int line=0;
int retval=0;
line=port->line-2;
/* clears the contents of the transmit and receive FIFO and resets the FIFO
* level logic and enable the transmit and receive FIFO */
s752_write_reg(FCR,line,0x07);
delay(5);
/* set RS-485*/
rs_485_Multidrop(line);
/* 中断注册,这里用的是共享中断,中断号IRQ_GPIO0+GPIO */
retval = request_irq(port->irq, s752_int, IRQF_SHARED, "ttySC_CE2",port);
if (retval){
printk("request irq SPI_CE2 error:%d\n",retval);
return retval;
}
/* only enable the RHR interrupt */
s752_write_reg(IER,line, 0x01);
}
2.2 start_tx:
s752_start_tx, 开始发送数据
static void s752_start_tx(struct uart_port *port)
{
unsigned int line=0;
/* 根据port->line 来选择ce1 或者 ce2 */
while((!(__ssi_transfer_end())) || __ssi_is_busy());
if(port->line < 2){
__ssi_select_ce();
line=port->line;
}
else {
__ssi_select_ce2();
line=port->line-2;
}
/* enable trasmit interrupt */
s752_write_reg(IER,line, 0x03);
//s752_tx_chars(port);
}
2.3 set_termios:
s752_set_termios, set baud_rate,even-odd parity, stop bit,data bit
static void s752_set_termios(struct uart_port *port, struct ktermios *termios,
struct ktermios *old)
{
unsigned long flags;
unsigned int baud, quot;
unsigned short cval=0, ier, lsr,line=0;
if(port->line < 2)
line=port->line;
else
line=port->line-2;
/*
* Ask the core to calculate the divisor for us.
*/
baud = uart_get_baud_rate(port, termios, old, 0, 56000*8);
quot = uart_get_divisor(port, baud);
spin_lock_irqsave(port->lock,flags);
/* LCR[7]=1,enable divisor latch*/
s752_write_reg(LCR,line,0x80);
s752_write_reg(DLL,line,quot & 0xFF); //baud rate
s752_write_reg(DLH,line,(quot >> 8) & 0xFF);
spin_unlock_irqrestore(port->lock, flags);
/* set data bit*/
switch (termios->c_cflag & CSIZE) {
case CS5:
cval = UART_LCR_WLEN5;
break;
case CS6:
cval = UART_LCR_WLEN6;
break;
case CS7:
cval = UART_LCR_WLEN7;
break;
default:
case CS8:
cval = UART_LCR_WLEN8;
break;
}
/* set stop bit*/
if (termios->c_cflag & CSTOPB){
cval |= UART_LCR_STOP;
}
/* set parity bit*/
if (termios->c_cflag & PARENB){ //如果设置了parity bit,则LCR第3位置1;
cval |= UART_LCR_PARITY;
if (!(termios->c_cflag & PARODD))
cval |= UART_LCR_EPAR;
}
s752_write_reg(LCR, line, cval);
}
2.4中断处理函数
static irqreturn_t s752_int(int irq, void *dev_id)
{
struct uart_port *port = dev_id;
uint8_t ret=0,chip;
int i=0;
unsigned int status,line;
spin_lock(&port->lock);
/* 根据port->line 来选择ce1 或者 ce2 */
while((!(__ssi_transfer_end())) || __ssi_is_busy());
if(port->line < 2){
__ssi_select_ce();
line=port->line;
chip=0;
}
else{
__ssi_select_ce2();
line=port->line-2;
chip=1;
}
ret=s752_read_reg(IIR,line);
if(!(ret & 0x01)){
status = s752_read_reg(LSR,line);
if(ret&0x04 && status&0x01) //rx intterrupt
s752_rx_chars(port,status);
if(ret&0x02) //tx interrupt
s752_tx_chars(port);
}
spin_unlock(&port->lock);
return IRQ_HANDLED;
}
2.5 具体发送,接收函数分析
s752_rx_chars(port,status)---接收 ,s752_tx_chars(port)--发送,跟普通的串口一样,
不具体分析。
2.6 485,422,232 介绍
RS-422的最大传输距离为4000英尺(约1219米),最大传输速率为10Mb/s;RS-485是从RS-422基础上发展而来的,所以RS-485许多电气规定与RS-422相仿。RS-485与RS-422一样,其最大传输距离约为1219米,最大传输速率为10Mb/s而RS-232传输距离很短只有50m。
485是半双工的,422是全双工的,232全双工。
4根线:RX,TX,RTS,CTS;一般RTS,CTS没怎么用
RX: 数据接收(pc-->8401)
TX: 数据发送 (8401-->pc)
RTS: 请求发送,输出信号(请求数据从外设pc-->8401,低有效,驱动需要控制)
CTS:清除发送,输入信号(表明外设PC已准备好,8401可以发数据给外设PC了,驱动中需要去读取寄存器的这位)
232一般只需要2根线(RX,TX),有时在需要流控的时候才需要(RTS,CTS)
485:RX,TX,RTS(控制是发送还是接收)
422:RX,TX,RTS(控制是发送还是接收)
3 建议
建议在后续开发中,在2.6的内核中把SSI总线驱动移植了,可参考2.4内核SSI总线驱动;还有
一些SSI驱动的参考如下