cq8401-spi-sc16IS751 驱动分析
时间:2010-03-11 来源:cao5170
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驱动的参考如下
本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/yinkaizhong/archive/2009/05/19/4202229.aspx
原理图说明: 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驱动的参考如下
本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/yinkaizhong/archive/2009/05/19/4202229.aspx
相关阅读 更多 +