📄 s3c2410_spi.c
字号:
/*
s3c2410_spi.c - spi driver, char device interface
Copyright (C) 2004-06-07 Leequng <leequng@mail.sc.cninfo.net>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/* $Id: s3c2410_spi.c,v 1.0 2004/06/07 01:28:01 mds Exp $ */
#ifndef __KERNEL__
#define __KERNEL__
#endif
#ifndef MODULE
#define MODULE
#endif
#include <linux/config.h>
#include <linux/module.h>
#include <linux/kernel.h> // printk()
#include <linux/malloc.h> // kmalloc()
#include <linux/fs.h> // everything support file struct
#include <linux/errno.h> // error codes
#include <linux/types.h> // size_t
#include <linux/proc_fs.h>
#include <linux/ioport.h> // kernel mem access
#include <linux/fcntl.h> // O_ACCMODE
#include <asm/system.h> // cli(), *_flags
#include <asm/io.h>
#include <asm/ioctl.h>
#include <asm/uaccess.h>
#include "s3c2410_spi.h"
#include <linux/init.h>
#include <linux/devfs_fs_kernel.h>
#define SDEBUG
// define macro for debug
#ifdef SDEBUG
#define SPI_DEBUG(fmt, args...) printk(fmt, ## args)
#else
#define SPI_DEBUG(fmt, args...)
#endif
// 定义 SPI 的主设备号
int spi_drv_major=0;
devfs_handle_t dev_handle;
unsigned char conf_val8;
unsigned short int conf_val16;
unsigned int conf_val32;
struct spi_dev My_SPI_Local,*spi_local=&My_SPI_Local;
unsigned char boot[60*1024];
int modem_reset,enable_config;
int powerCTL_EN;
static int spi_baudrate_set(unsigned int spi_channel_num, unsigned int baud_rate)
{
unsigned char prescaler_val;
prescaler_val = PCLK/2/ baud_rate -1;
outb(prescaler_val,SPPRE);
return ACCESS_OK;
}
static int spi_config(unsigned char SelectSM)
{
// 设置 SPI 工作模式
conf_val8 = inb(SPCON);
conf_val8|=(SPCON_SMOD_INT); //interrupt rx/tx mode
if(SelectSM==0){
conf_val8&=~SPCON_MSTR; //slave mode,disable clock
conf_val8&=~SPCON_ENSCK;
}
else{
conf_val8|=SPCON_MSTR;
conf_val8|=SPCON_ENSCK; //master mode,enable clock
}
conf_val8&=~(SPCON_CPOL); //active low,Format B,Tx auto mode
conf_val8|=(SPCON_CPHA|SPCON_TAGD);
outb(conf_val8,SPCON);
return ACCESS_OK;
}
static int spi_pin_config(unsigned int spi_channel_num)
{
// 设置 SPI 引脚控制寄存器
conf_val8 = inb(SPPIN);
conf_val8 &= ~(SPPIN_KEEP|SPPIN_ENMUL); //disable multi master error detect,release MOSI drive
conf_val8 |=0x2; //set reserve bit=1
outb(conf_val8,SPPIN);
return 0;
}
static int spi_open(struct inode *inode, struct file *p_file)
{
MOD_INC_USE_COUNT;
printk(KERN_INFO "SPI open....!!\n");
p_file->private_data = spi_local; // 让 file 的数据字段指向已经分配的数据
return 0;
} // end of static int spi_open
static int spi_close(struct inode *inode, struct file *p_file)
{
// 得到 spi 接口指针
SPI_Status=Status_Send_Packet;
DisInt_SPI;
MOD_DEC_USE_COUNT;
printk(KERN_INFO "SPI close!!!\n");
return 0;
} // end of static int spi_close
static void BackReverse(struct spi_dev *pdev,unsigned int num)
{
pdev->recv_buf.size-=num;
if(pdev->recv_buf.tail-num>=0)
pdev->recv_buf.tail-=num;
else
pdev->recv_buf.tail=BUF_SIZE-num+pdev->recv_buf.tail;
return;
}
static void powerOff(int irq, void *private, struct pt_regs *regs)
{
if(powerCTL_EN){
write_gpio_bit(GPIO_G8,0);
while(1);
}
return;
}
volatile unsigned int counter=0,PacketFound=0,prc=0;
volatile unsigned long Tout=0;
volatile int TimeOut;
static void spi_intr(int irq, void *private, struct pt_regs *regs)
{
struct spi_dev *spi_local = private;
int head,tail;
cli();
if(!read_gpio_bit(nSS))
write_gpio_bit(nSS,1);
if(SPSTA&SPSTA_DCOL)
SPI_DEBUG("Data collision\n");
switch(SPI_Status){
case Status_Send_Packet:
if(HaveSendPacket){ //如果有包传送,发送/接收中断
// SPI_DEBUG("S");
if(spi_local->send_buf.size>0){ //有包,未发送完毕
head=spi_local->send_buf.head;
write_gpio_bit(nSS,0);
while(!(SPSTA&SPSTA_READY));
outb(spi_local->send_buf.buffer[head],SPTDAT);
spi_local->send_buf.head=(head+1)%BUF_SIZE;
spi_local->send_buf.size--;
break;
}
else{//有包,但传送完毕,应是接收中断
SPI_DEBUG("\n");
HaveSendPacket=NO;
if(ADSL_Packet.CmdType!=StopTest&&ADSL_Packet.CmdType!=MasterModeTest&&ADSL_Packet.CmdType!=CalibrateMode){
SPI_Status=Status_Receive_Packet;
inb(SPRDAT);
}
TimeOut=NO;
Tout=jiffies+1000*(HZ/100);
if(ADSL_Packet.TestType==SPI_ADSL_PSD)
Tout+=1000*(HZ/100);
}
}
break;
case Status_Receive_Packet:
if((!read_gpio_bit(IO4))||PacketFound){
// SPI_DEBUG("R");
write_gpio_bit(nSS,0);
while(!(SPSTA&SPSTA_READY));
ByteIn=inb(SPRDAT);
// SPI_DEBUG("%2X ",ByteIn);
tail=spi_local->recv_buf.tail;
switch(RxSta){
case RxSta_head55:
if(ByteIn==0xaa)
RxSta=RxSta_headAA;
break;
case RxSta_headAA:
if(ByteIn==0x55)
RxSta=RxSta_len0;
else
RxSta=RxSta_head55;
break;
case RxSta_len0:
RxCounter=ByteIn;
RxSta=RxSta_len1;
break;
case RxSta_len1:
RxCounter=RxCounter+ByteIn*256;
RxSta=RxSta_comtype;
break;
case RxSta_comtype:
if(ByteIn!=DataPacket)
RxSta=RxSta_head55;
else
RxSta=RxSta_testtype;
break;
case RxSta_testtype:
if(ByteIn==CurTestType&&spi_local->recv_buf.size<BUF_SIZE-6){
PacketFound=1;
SPI_Status=Status_Receive_Packet;
RxCounters[RxPacketTail]=RxCounter;
spi_local->recv_buf.buffer[tail++]=0xaa;
tail%=BUF_SIZE;
spi_local->recv_buf.buffer[tail++]=0x55;
tail%=BUF_SIZE;
spi_local->recv_buf.buffer[tail++]=RxCounter%256; //length lower byte
tail%=BUF_SIZE;
spi_local->recv_buf.buffer[tail++]=RxCounter/256; //length high byte
tail%=BUF_SIZE;
spi_local->recv_buf.buffer[tail++]=DataPacket;
tail%=BUF_SIZE;
spi_local->recv_buf.buffer[tail++]=CurTestType;
tail%=BUF_SIZE;
spi_local->recv_buf.tail=tail;
spi_local->recv_buf.size+=6;
RxCounter-=6;
RxSta=RxSta_data;
}
else
RxSta=RxSta_head55;
break;
case RxSta_data:
if(!(RxData%15))
SPI_DEBUG("\n");
if(spi_local->recv_buf.size<BUF_SIZE){
spi_local->recv_buf.buffer[tail++]=ByteIn;
tail%=BUF_SIZE;
spi_local->recv_buf.tail=tail;
spi_local->recv_buf.size++;
if(!(--RxCounter)){ //complete receive one packet
SPI_DEBUG("\nReceive over,not loss data\n");
if(RxCounters[RxPacketTail]!=0&&RxPacketNum!=1024){ //未丢失数据
RxPacketTail++;
RxPacketTail%=1024;
RxPacketNum++;
}
else //loss data....
BackReverse(spi_local,RxData+6);
RxSta=RxSta_head55;
SPI_Status=Status_Send_Packet;
RxData=0;
PacketFound=0;
break;
}
RxData++;
}
else{
SPI_DEBUG("Receive buffer overflow!\n");
if(!(--RxCounter)){ //complete receive one packet,but loss data
SPI_DEBUG("receive over,not loss data\n");
RxSta=RxSta_head55;
SPI_Status=Status_Send_Packet;
BackReverse(spi_local,RxData+6);
RxData=0;
PacketFound=0;
}
RxCounters[RxPacketTail]=0;
}
break;
default:
break;
}
}
else{
if(Tout<jiffies){
SPI_DEBUG("Receive packet time out!\n");
RxSta=RxSta_head55;
SPI_Status=Status_Send_Packet;
RxData=0;
HaveSendPacket=NO;
PacketFound=0;
TimeOut=YES;
}
inb(SPRDAT);
}
break;
default:
break;
}
sti();
return ;
}
// Define file operation set for s3c2410
static struct file_operations spi_fops =
{
owner : THIS_MODULE,
open : spi_open,
release : spi_close,
ioctl : spi_ioctl
};
static int __init spi_init_module(void)
{
int result;
modem_reset = 0;
enable_config = 0;
powerCTL_EN = 0;
write_gpio_bit(RESET,1);
set_gpio_ctrl(RESET|GPIO_MODE_OUT|GPIO_PULLUP_EN); //Set DSP RESET PIN to Low,output
write_gpio_bit(IO4,1);
set_gpio_ctrl(IO4|GPIO_MODE_IN|GPIO_PULLUP_EN); //Set DSP IO4 as input;
write_gpio_bit(nSS,1);
set_gpio_ctrl(nSS|GPIO_MODE_OUT|GPIO_PULLUP_EN); //Set DSP nSS to High,output;
write_gpio_bit(GPIO_G9,1); //2005-5-8
set_gpio_ctrl(GPIO_G9|GPIO_MODE_IN|GPIO_PULLUP_EN); //set MODEM ready signal monitor, input;
write_gpio_bit(GPIO_G8,1);
set_gpio_ctrl(GPIO_G8|GPIO_MODE_OUT|GPIO_PULLUP_EN);
write_gpio_bit(GPIO_A21,1);
set_gpio_ctrl(GPIO_A21|GPIO_MODE_OUT|GPIO_PULLUP_EN);
write_gpio_bit(GPIO_G4,1);
set_gpio_ctrl(GPIO_G4|GPIO_MODE_OUT|GPIO_PULLUP_EN);
/*
set_gpio_ctrl(GPIO_G5|GPIO_MODE_SPIMISO|GPIO_PULLUP_EN);
set_gpio_ctrl(GPIO_G6|GPIO_MODE_SPIMOSI|GPIO_PULLUP_EN);
set_gpio_ctrl(GPIO_G7|GPIO_MODE_SPICLK|GPIO_PULLUP_EN);
*/
set_gpio_ctrl(GPIO_H4|GPIO_MODE_OUT|GPIO_PULLUP_EN);
//write_gpio_bit(GPIO_H4,0);
// Delay10ms(200);
//write_gpio_bit(GPIO_H4,1);
// 设定 file_operations 结构的 owner 字段
SET_MODULE_OWNER(&spi_fops);
SPI_DEBUG("Module SPI is initing!!!\n");
// 将 SPI 作为字符设备注册,采用设备文件系统
result = devfs_register_chrdev(spi_drv_major, "spi_drv", &spi_fops);
if (result < 0){
SPI_DEBUG("spi_drv: Couldn't get a major number.\n");
SPI_DEBUG("spi_drv: Spi driver register failure !\n");
SPI_DEBUG("spi_drv: Error in calling devfs_register_chrdev\n");
return result;
}
dev_handle=devfs_register(NULL,"spi_drv",DEVFS_FL_DEFAULT,result,0,S_IFCHR,&spi_fops,NULL);
if ( 0 == spi_drv_major )
spi_drv_major = result;
// 初始化设备中的相关变量(发送缓冲区,接收缓冲区相关数据)
spi_local->send_buf.head = 0;
spi_local->send_buf.tail = 0;
spi_local->send_buf.size = 0;
spi_local->recv_buf.head = 0;
spi_local->recv_buf.tail = 0;
spi_local->recv_buf.size = 0;
// 初始化发送缓冲区和接收缓冲区使用的信号量
sema_init(&spi_local->send_buf.sem, 1);
sema_init(&spi_local->recv_buf.sem, 1);
// 注册成功后开始向 SPI 控制寄存器写控制字
// 首先设置通信的波特率
spi_baudrate_set(0, 2000000);
// 设置 SPI0 的工作模式
spi_config(1);
spi_pin_config(0);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -