你好,麻烦你了,我看不明白内核中的spi子系统的代码,因为新手,所以只能当做一般的字符驱动来做了。
以下是我的代码:注册驱动参考的LDD3的scull驱动的方法。
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h> //module init
#include <linux/errno.h> //error number
#include <asm/uaccess.h> //copy_to_user copy_from_user
#include <linux/delay.h>
#include <asm/io.h> //iowrite8()
#include <linux/gpio.h>
#include <mach/regs-gpioj.h> //gpio-j
#include <mach/regs-gpio.h> //gpio ports
#include <plat/regs-spi.h> //spi registers
#include <mach/hardware.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/miscdevice.h>
#include <linux/semaphore.h>
#define DEVICE_NAME "spi_adc" //device name
int adc_major=231;
int adc_minor=0;
int adc_number=1;
#define CHANNAL_SELECT 1 //select channal command
static void configure_spi_pin(void);
static void configure_spi_register(void);
static void ad_convert(void);
static void spi_tx_data(unsigned char data);
static void spi_poll_done(void);
unsigned char dataTX=0; //send data ,select channal
unsigned char dataRX_0=0;//receive data 0
unsigned char dataRX_1=0;//receive data 1
unsigned int convert_data=0;//read data from AD
struct adc_dev{
struct cdev cdev;
struct semaphore sem;
};
struct adc_dev *devp;
/*remap PA to VA*/
static void __iomem *base_addr;
/*spi ralate pin(GPJ12\GPG11\GPG12\GPG13) configure*/
static void configure_spi_pin(void)
{/*disable gpio pullup for spi*/
s3c2410_gpio_pullup(S3C2440_GPJ12,1); //0=enable
s3c2410_gpio_pullup(S3C2410_GPG(13),1);
s3c2410_gpio_pullup(S3C2410_GPG(12),1);
s3c2410_gpio_pullup(S3C2410_GPG(11),1);
/*configure I/O ports as spi function*/
s3c2410_gpio_cfgpin(S3C2440_GPJ12,S3C2440_GPJ12_OUTP); //CS
s3c2410_gpio_cfgpin(S3C2410_GPE(13),S3C2410_GPE13_SPICLK0); //SPICLK0
s3c2410_gpio_cfgpin(S3C2410_GPE(12),S3C2410_GPE12_SPIMOSI0);//SPIMOSI0
s3c2410_gpio_cfgpin(S3C2410_GPE(11),S3C2410_GPE11_SPIMISO0);//SPIMISO0
printk(KERN_ALERT "K:pins function configured!\n");
s3c2410_gpio_setpin(S3C2440_GPJ12,1);
}
static void configure_spi_register(void)
{ //set prescaler value baud=PCLK/2/(Prescaler+1)=2MHZ(AD7888 fsclk=2MHZ)
int i;
char control_reg=0;
__raw_writeb(0x32,base_addr+S3C2410_SPPRE);
//poll,sck enable,major,sytle=A;regs-spi.h
control_reg=S3C2410_SPCON_SMOD_POLL|
S3C2410_SPCON_ENSCK|
S3C2410_SPCON_MSTR|
S3C2410_SPCON_CPOL_HIGH|
S3C2410_SPCON_CPHA_FMTB|
(0<<0);
__raw_writeb(control_reg,base_addr+S3C2410_SPCON);
for(i=0;i<10;i++)
__raw_writeb(0xff,base_addr+S3C2410_SPTDAT);
printk(KERN_ALERT "K:spi registers configured!\n");
}
static void spi_poll_done(void)
{
while(!(__raw_readb(base_addr+S3C2410_SPSTA)&0x01));
}
/*
static void spi_tx_data(unsigned char data)
{
__raw_writeb(data,base_addr+S3C2410_SPTDAT);
}*/
static void ad_convert(void)
{
configure_spi_register();
s3c2410_gpio_setpin(S3C2440_GPJ12,0);
spi_poll_done();
__raw_writeb(dataTX,base_addr+S3C2410_SPTDAT);;
__raw_writeb(0xff,base_addr+S3C2410_SPTDAT);
spi_poll_done();
dataRX_0=__raw_readb(base_addr+S3C2410_SPRDAT);
__raw_writeb(0xff,base_addr+S3C2410_SPTDAT);
spi_poll_done();
dataRX_1=__raw_readb(base_addr+S3C2410_SPRDAT);
s3c2410_gpio_setpin(S3C2440_GPJ12,1);
printk(KERN_ALERT "K: dataRX_0=%d\n",dataRX_0);
printk(KERN_ALERT "K: dataRX_1=%d\n",dataRX_1);
}
static int spi_adc_ioctl(struct inode *inode,struct file *filp,unsigned int cmd,unsigned long arg)
{
switch(cmd)
{
case CHANNAL_SELECT:
dataTX=arg;
printk(KERN_ALERT "K:receive data=%d from user space\n",dataTX);
break;
default:
return -ENOTTY;
}
dataTX<<=3;
dataTX|=0X04;
//DONTC ZERO ADD2 ADD1 ADD0 REF PM1 PM0
// 0 0 1 0 0
printk(KERN_ALERT "K:change dataTX=%d\n",dataTX);
return 0;
}
static int spi_adc_read(struct file *filp,char __user *buf,size_t count,loff_t *f_ops)
{
struct adc_dev *dev=filp->private_data;
int ret;
if(filp->f_flags&O_NONBLOCK) //read block
{
ret= -EAGAIN;
goto out;
}
if(down_interruptible(&dev->sem)) //own the sem
{
return -ERESTARTSYS;
}
ad_convert();
ad_convert();
convert_data=dataRX_0;
convert_data<<=8;
convert_data+=dataRX_1;
convert_data&=0x0fff;
printk(KERN_ALERT "K:send data=%d/tto user space\n",convert_data);
ret=copy_to_user(buf,&convert_data,count); //success ret=0
up(&dev->sem); //release samephore sem
out:return ret;
}
static int spi_adc_open(struct inode *inode,struct file *filp)
{
struct adc_dev *dev; //device information
dev=container_of(inode->i_cdev,struct adc_dev,cdev);
filp->private_data=dev; //for other methors
printk(KERN_ALERT "K:spi device opened\n");
configure_spi_pin();
return 0;
}
static int spi_adc_close(struct inode *inode,struct file *filp)
{
printk(KERN_ALERT "K:spi device closed\n");
s3c2410_gpio_setpin(S3C2440_GPJ12,1); //set CS high , chip select canceled
return 0;
}
static struct file_operations spi_adc_fops={
.owner =THIS_MODULE,
.open =spi_adc_open,
.ioctl =spi_adc_ioctl,
.read =spi_adc_read,
.release=spi_adc_close,
};
/*setup char device*/
static void adc_setup_cdev(struct adc_dev *dev)
{
int err;
int devno=MKDEV(adc_major,adc_minor);
cdev_init(&dev->cdev,&spi_adc_fops);
dev->cdev.owner=THIS_MODULE;
dev->cdev.ops=&spi_adc_fops;
err=cdev_add(&dev->cdev,devno,adc_number);
if(err)
printk(KERN_NOTICE "K:Error %d\n",err);
}
static int __init spi_adc_init(void)
{
int ret;
dev_t dev=0;
//remap physics address to vitrual address
base_addr=ioremap(S3C2410_PA_SPI,0x20);
if(base_addr==NULL)//deal with error
{
printk(KERN_ERR "K:Failed to remap register block\n");
return -ENOMEM;
}
if(adc_major)
{
dev=MKDEV(adc_major,adc_minor);
ret=register_chrdev_region(dev,adc_number,"spi_adc");
}
else//alloc device major
{
ret=alloc_chrdev_region(&dev,adc_minor,adc_number,"spi_adc");
adc_major=MAJOR(dev);
}
if(ret<0)
{
printk(KERN_WARNING "K:can't get major %d\n",adc_major);
return ret;
}
init_MUTEX(&devp->sem); //inital semaphore sem=1
adc_setup_cdev(devp);
printk(KERN_NOTICE "K:initial!\n");
return 0;
}
static void __exit spi_adc_exit(void)
{
int devno=0;
iounmap(base_addr);
devno=MKDEV(adc_major,adc_minor);
cdev_del(&devp->cdev);
unregister_chrdev_region(devno,adc_number);
printk(KERN_NOTICE "K:exit!\n");
}
module_init(spi_adc_init);
module_exit(spi_adc_exit);
MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("http://amwha.cublog.cn");
MODULE_DESCRIPTION("serial adc7888 convert");