SPI驱动流程(S3C2440)
来源:互联网 发布:java入门基础书籍 编辑:程序博客网 时间:2024/05/17 22:01
SPI驱动流程(S3C2440)
SPI驱动流程(S3C2440)
2007-12-27 22:50:25
简单的说就是写几个寄存器,其实非常简单的,呵呵
一、配置IO脚为SPI接口
我用的是(s3c2440)SPI1通道,所有的IO都在GPG脚上,故配置的是GPGCON,
将对应的位置为SPI
然后是GPGUP寄存器
二、开始SPI寄存器的配置
1、SPPRE,设置传送频率,即SPICLK的频率
SPI_CLK=PCLK/2/(SPPRE+1)
根据S3C2440的资料,PCLK=48M,我要配置SPI_CLK为 1M,
1=48/2/(SPPRE+1)=> SPPRE=23=> SPPRE=0x18
(个人理解,有错请指出,呵呵)
2、SPCON 寄存器
该寄存器有 7 位,对应的控制包括:SPIMOD(6:5),SCK enable (4),master/slave (3),CPOL(2),CPHA(1),TAGD(0).具体怎么设置根据自己的需要设置就OK。
3、SPPIN 寄存器
就三个寄存器,都设置好后就可以写和接收数据 啦
写之前记得要检查SPSTA的 REDY位是否为 1 .
NOTE: 我的配置好IO之后,发现写SPCON寄存器无效,郁闷了两天终于找到原因,就CLKCON寄存器的SPI位没有
激活,所以写寄存器之前记得要检查CLKCON的状态
一点点经验,留个记号,呵呵
代码写的有点乱,测试的时候弄的,不好意思^_^
code :
/************************************************/
#ifndef MODULE
#define MODULE
#endif
#include <asm/io.h>
#include <linux/module.h>
#include <linux/config.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/miscdevice.h>
/*---------- spi ---------------*/
/*----------------- addr ---------- note --------*/
#define GPG_CON 0x56000060 /* port G control */
#define GPG_DAT 0x56000064 /* port G data */
#define GPG_UP 0x56000068 /* pull up port G control */
#define SPCON_1 0x59000020 /*SPI1 control */
#define SPSTA_1 0x59000024 /* SPI1 status */
#define SPPIN_1 0x59000028 /* SPI1 pin control */
#define SPPRE_1 0X5900002C /* spi1 baud rate prescaler */
#define SPTDAT_1 0x59000030 /* spi1 Tx data */
#define SPRDAT_1 0x59000034 /* spi1 Rx data */
/* GPGCON */
#define nSS1 (1<<6 | 1<<7) /* GPG3 */
#define SPICLK1 (1<<14 | 1<<15) /* GPG7 */
#define SPIMOSI1 (1<<12 | 1<<13) /* GPG6 */
#define SPIMISO1 (1<<10 | 1<<11) /* GPG5 */
#define SPI_USE (nSS1 | SPICLK1 | SPIMOSI1 |SPIMISO1)
//static int spi_major;
#define spi_name "S3C2440_SPI"
#define spi_minor 1
#ifdef CONFIG_DEVFS_FS
static devfs_handle_t devfs_spi_dir,devfs_spi_raw;
#endif
MODULE_LICENSE("GPL");
#if 1
#define SPI_SPPRE1 (*(volatile unsigned long *)SPPRE1) /* spi1 baud rate prescaler */
#define SPI_GPGCON (*(volatile unsigned long *)GPGCON) /* port-G control */
#define SPI_SPCON1 (*(volatile unsigned long *)SPCON1) /* spi1 control */
#define SPI_SPSTA1 (*(volatile unsigned long *)SPSTA1) /* spi1 status */
#define SPI_SPPIN1 (*(volatile unsigned long *)SPPIN1) /* spi1 pin control */
#define SPI_SPTDAT1 (*(volatile unsigned long *)SPTDAT1) /* spi1 Tx data */
#define SPI_SPRDAT1 (*(volatile unsigned long *)SPRDAT1) /* spi1 Rx data */
#define SPTDAT1_READ (*(volatile unsigned long *)SPTDAT1) /* spi1 Tx data */
#define SPI_GPGDAT (*(volatile unsigned long *)GPGDAT) /* port-G data */
#define CLKCON (*(volatile unsigned long *)ADDR_CLKCON)
#endif
unsigned long GPGCON;
unsigned long GPGDAT;
unsigned long GPGUP;
unsigned long SPCON1;
unsigned long SPSTA1;
unsigned long SPPIN1;
unsigned long SPPRE1;
unsigned long SPTDAT1;
unsigned long SPRDAT1;
unsigned long ADDR_CLKCON;
static void get_sys_addr(void)
{
GPGCON =(unsigned long)ioremap(GPG_CON,4);
GPGDAT =(unsigned long)ioremap(GPG_DAT,4);
GPGUP =(unsigned long)ioremap(GPG_UP,4);
SPCON1 =(unsigned long)ioremap(SPCON_1,4);
SPSTA1 =(unsigned long)ioremap(SPSTA_1,4);
SPPIN1 =(unsigned long)ioremap(SPPIN_1,4);
SPPRE1 =(unsigned long)ioremap(SPPRE_1,4);
SPTDAT1=(unsigned long)ioremap(SPTDAT_1,4);
SPRDAT1=(unsigned long)ioremap(SPRDAT_1,4);
ADDR_CLKCON=(unsigned long )ioremap(0x4c00000c,4);
}
#if 1
static int spi_open(struct inode *inode,struct file *filp)
{
unsigned int port_state;
int i=0;
#if 1
/*=============== start init spi regiser ============*/
/* 时钟使能 */
printk("CLKCON: %x /n",(unsigned int )CLKCON);
if (!(CLKCON&(1<<18)))
CLKCON|=(1<<18);
printk("CLKCON: %x /n",(unsigned int )CLKCON);
/* 配置IO 脚为 SPI */
port_state = readl(GPGCON);
printk("<1>only read :port_state= %x /n",port_state);
port_state &= ~SPI_USE;
port_state |= SPI_USE;
/* printk("<1>port_state= %x /n",port_state); */
SPI_GPGCON = port_state;
// writel(SPI_USE,GPGCON);
/* printk("<1> after write :readl(GPGCON)= %x /n",SPI_GPGCON); */
/* IO 脚使能 */
port_state=0x0;
port_state = readl(GPGUP);
/* printk("<1> GPGUP before write: %x /n",port_state); */
port_state &=~(1<<3 | 1<<5 | 1<<6 | 1<<7);
writel(port_state,GPGUP);
/* printk("<1>after write :GPGUP= %x /n",readl(GPGUP)); */
#endif
#if 1
port_state = readl(SPPRE1);
// writel(value,SPPRE1); /* if PCLK=50MHz PCLK/2/(value+1) =SPICLK */
SPI_SPPRE1=0x18; /* set Baud rate 1M .get it in net.note: PCLE=48MHz ,SPICLK=48/2/(0x18+1)=1M */
/* set spi module */
/* printk("<1>before write :readl(SPCON1)=%x /n",readl(SPCON1)); */
port_state = readl(SPCON1);
port_state &= ~0x03f;
port_state |= (0<<0 | 0<<1 | 1<<2 | 1<<3 | 1<<4 | 0<<5 );
/* 0<<0 normal mode , 0<<1 format A,1<<2 active low ,1<<3 master
* 1<<4 SCK enable (master only), 0<<5 polling mode
*/
// writel(port_state,SPCON1);
SPI_SPCON1=(0<<0 | 0<<1 | 1<<2 | 1<<3 | 1<<4 | 0<<5 );
port_state = readl(SPPIN1);
port_state &= ~(1<<0 | 1<<1 | 1<<2);
port_state |= (0<<2 | 1<<1 | 0<<0);
/* 0<<0:release 1<<1:SBO 0<<2:dis-ENMUL*/
// writel(port_state,SPPIN1);
SPI_SPPIN1=(0<<2 | 1<<1 | 0<<0);
printk("<1>SPI_SPPIN1 = %x /n",(unsigned int )SPI_SPPIN1);
// SPI_GPGDAT &= ~1<<3;
printk("SPSTA1=%x /n",readl(SPSTA1));
/*================== end init spiregister =========================*/
#if 0 // use for test spi wave ...
while (1)
{
// printk("<1> readl(SPSTA1)&0X01: %x /n",readl(SPSTA1)&0x01);
if (readl(SPSTA1)&0x01)
{
SPTDAT1_READ=0x1234;
// writel(a,SPTDAT1);
// SPI_SPTDAT1='a';
}
printk("<1>writel /n");
// printk("<1>readl(SPRDAT1)=%x /n",readl(SPRDAT1));
printk("<1> readl(SPTDAT1)=%s /n",readl(SPTDAT1));
i++;
// printk("<1>readl(SPRDAT1)=%c /n",readl(SPRDAT1));
// printk("<1> readl(SPTDAT1)=%c /n",readl(SPTDAT1));
}
#endif // test spi wave
#endif
// writel(~(1<<4),SPCON1);// dis-SCK
// printk("<1>is the OPEN readl(GPGUP)=%x /n",readl(GPGUP));
return 0;
}
static int spi_release(struct inode *inode,struct file *filp)
{
// MOD_DEC_USE_COUNT;
printk("<1> release /n");
return 0;
}
#endif
#if 1
static ssize_t spi_write(struct file *filp,const char *buf,size_t count,loff_t *f_ops)
{
int i=0;
// int config;
char string;
char str[20];
char *txStr,*rxStr;
volatile char *spiTxStr,*spiRxStr;
volatile int endSpiTx;
unsigned int port_state=0;
endSpiTx=0;
spiTxStr="spi123456test";
spiRxStr=str;
txStr=(char *)spiTxStr;
rxStr=(char *)spiRxStr;
/* Determine SPI clock rate.
* Baud rate = PCLK / 2 / (Prescaler value + 1)
* Baud rate < 25 M HZ
* */
#if 0 // init spi in open ()
port_state = readl(SPPRE1);
writel(0x18,SPPRE1); /* SPPRE1= 0x18 ,Baud rate = 1MHz */
printk("<1> spi_write/n");
/* set spi module */
port_state = readl(SPCON1);
port_state &= ~0x07f;
port_state |= (0<<0 | 0<<1 | 1<<2 | 1<<3 | 1<<4 | 0<<5);
/* 0<<0 normal mode , 0<<1 format A,1<<2 active low ,1<<3 master
* 1<<4 SCK enable (master only), 0<<5 | 0<<6 polling mode
*/
writel(port_state,SPCON1);
printk("<1>set SPCON1/n");
/* SPI : master,nSS pin :multi-master error */
port_state = readl(SPPIN1);
port_state &= ~(1<<0 | 1<<1 | 1<<2);
port_state |= (1<<2 | 0<<1 | 1<<0);
/* 1<<0: Drive the previous level; 1<<2:Multi master error detect enable*/ writel(port_state,SPPIN1);
printk("<1>set SPCON1 /n");
#endif // init spi in open()
while (endSpiTx == 0)
{
printk("SPSTA1=%x /n",readl(SPSTA1));
if((readl(SPSTA1) & 0X01) == 0)
{
if (*spiTxStr != '/0')
{
writel(*spiTxStr,SPTDAT1);
string = readl(SPTDAT1);
spiTxStr++;
}
else
{
endSpiTx=1;
}
str[i]=readl(SPRDAT1);
printk("receive char = %c /n",str[i]);
i++;
}
}
port_state = readl(SPCON1);
port_state &= ~0x07f;
port_state |= (0<<1 | 0<<1 | 1<<2 | 0<<4 | 0<<5 );
writel(port_state,SPCON1);
*(spiRxStr-1)='/0';
printk("<1> Tx string : %s /n",txStr);
printk("<1> Rx string : %s /n",rxStr);
return 0;
}
#endif
static struct file_operations spi_fops ={
.owner = THIS_MODULE,
.open = spi_open,
.release = spi_release,
.write = spi_write,
};
static struct miscdevice spi_dev=
{
123,
"spi",
&spi_fops,
};
static int __init spi_init(void)
{
// unsigned int value;
// int ret;
get_sys_addr();
misc_register(&spi_dev);
#if 0
ret = register_chrdev(0,spi_name,&spi_fops);
if(ret<0)
{
printk("<1> cannot get major number /n");
return ret;
}
spi_major=ret;
#ifdef CONFIG_DEVFS_FS
devfs_spi_dir = devfs_mk_dir(NULL,"spi",NULL);
devfs_spi_raw = devfs_register(devfs_spi_dir,"0",DEVFS_FL_DEFAULT,spi_major,spi_minor,S_IFCHR | S_IRUSR | S_IWUSR,&spi_fops,NULL);
#endif
#endif
printk(KERN_ALERT "hello,world /n");
printk("<1> readl(SPTDAT1)=%x , SPTDAT1_READ = %x /n",readl(SPTDAT1),(unsigned int)SPTDAT1_READ);
return 0;
}
static void __exit spi_exit(void)
{
#if 0
#ifdef CONFIG_DEVFS_FS
devfs_unregister(devfs_spi_raw);
devfs_unregister(devfs_spi_dir);
#endif
unregister_chrdev(spi_major,spi_name);
#endif
misc_deregister(&spi_dev);
printk(KERN_ALERT "goodbye ,cruel world /n");
}
module_init(spi_init);
module_exit(spi_exit);
2410_SPI接口与linux驱动
2.6.18内核下已经添加了完整的spi子系统了,参考mtd的分析,将从下到上层,再从上到下层的对其进行分析。
以下先从下到上的进行分析:
driver/spi下有两个底层相关的spi驱动程序:
spi_s3c24xx.c和spi_s3c24xx_gpio.c
其中spi_s3c24xx.c是基于s3c24xx下相应的spi接口的驱动程序,spi_s3c24xx_gpio.c允许用户指定3个gpio口,分别充当spi_clk、spi_mosi和spi_miso接口,模拟标准的spi总线。
s3c2410自带了两个spi接口(spi0和spi1),在此我只研究基于s3c2410下spi接口的驱动程序spi_s3c24xx.c。
首先从spi驱动的检测函数进行分析:
static int s3c24xx_spi_probe(struct platform_device *pdev)
{
struct s3c24xx_spi *hw;
struct spi_master *master;
struct spi_board_info *bi;
struct resource *res;
int err = 0;
int i;
/* pi_alloc_master函数申请了struct spi_master+struct s3c24xx_spi大小的数据,
* spi_master_get_devdata和pi_master_get分别取出struct s3c24xx_spi和struct spi_master结构指针
*/
master = spi_alloc_master(&pdev->dev, sizeof(struct s3c24xx_spi));
if (master == NULL) {
dev_err(&pdev->dev, "No memory for spi_master/n");
err = -ENOMEM;
goto err_nomem;
}
/* 填充struct spi_master结构 */
hw = spi_master_get_devdata(master);
memset(hw, 0, sizeof(struct s3c24xx_spi));
hw->master = spi_master_get(master);
hw->pdata = pdev->dev.platform_data;
hw->dev = &pdev->dev;
if (hw->pdata == NULL) {
dev_err(&pdev->dev, "No platform data supplied/n");
err = -ENOENT;
goto err_no_pdata;
}
platform_set_drvdata(pdev, hw);//dev_set_drvdata(&pdev->dev, hw)
init_completion(&hw->done);
/* setup the state for the bitbang driver */
/* 填充hw->bitbang结构(hw->bitbang结构充当一个中间层,相当与input system的input_handle struct) */
hw->bitbang.master = hw->master;
hw->bitbang.setup_transfer = s3c24xx_spi_setupxfer;
hw->bitbang.chipselect = s3c24xx_spi_chipsel;
hw->bitbang.txrx_bufs = s3c24xx_spi_txrx;
hw->bitbang.master->setup = s3c24xx_spi_setup;
dev_dbg(hw->dev, "bitbang at %p/n", &hw->bitbang);
/* find and map our resources */
/* 申请spi所用到的资源:io、irq、时钟等 */
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (res == NULL) {
dev_err(&pdev->dev, "Cannot get IORESOURCE_MEM/n");
err = -ENOENT;
goto err_no_iores;
}
hw->ioarea = request_mem_region(res->start, (res->end - res->start)+1,
pdev->name);
if (hw->ioarea == NULL) {
dev_err(&pdev->dev, "Cannot reserve region/n");
err = -ENXIO;
goto err_no_iores;
}
hw->regs = ioremap(res->start, (res->end - res->start)+1);
if (hw->regs == NULL) {
dev_err(&pdev->dev, "Cannot map IO/n");
err = -ENXIO;
goto err_no_iomap;
}
hw->irq = platform_get_irq(pdev, 0);
if (hw->irq < 0) {
dev_err(&pdev->dev, "No IRQ specified/n");
err = -ENOENT;
goto err_no_irq;
}
err = request_irq(hw->irq, s3c24xx_spi_irq, 0, pdev->name, hw);
if (err) {
dev_err(&pdev->dev, "Cannot claim IRQ/n");
goto err_no_irq;
}
hw->clk = clk_get(&pdev->dev, "spi");
if (IS_ERR(hw->clk)) {
dev_err(&pdev->dev, "No clock for device/n");
err = PTR_ERR(hw->clk);
goto err_no_clk;
}
/* for the moment, permanently enable the clock */
clk_enable(hw->clk);
/* program defaults into the registers */
/* 初始化spi相关的寄存器 */
writeb(0xff, hw->regs + S3C2410_SPPRE);
writeb(SPPIN_DEFAULT, hw->regs + S3C2410_SPPIN);
writeb(SPCON_DEFAULT, hw->regs + S3C2410_SPCON);
/* add by lfc */
s3c2410_gpio_setpin(S3C2410_GPE13, 0);
s3c2410_gpio_setpin(S3C2410_GPE12, 0);
s3c2410_gpio_cfgpin(S3C2410_GPE13, S3C2410_GPE13_SPICLK0);
s3c2410_gpio_cfgpin(S3C2410_GPE12, S3C2410_GPE12_SPIMOSI0);
s3c2410_gpio_cfgpin(S3C2410_GPE11, S3C2410_GPE11_SPIMISO0);
/* end add */
/* setup any gpio we can */
/* 片选 */
if (!hw->pdata->set_cs) {
s3c2410_gpio_setpin(hw->pdata->pin_cs, 1);
s3c2410_gpio_cfgpin(hw->pdata->pin_cs, S3C2410_GPIO_OUTPUT);
}
/* register our spi controller */
/* 最终通过调用spi_register_master来注册spi控制器(驱动) */
err = spi_bitbang_start(&hw->bitbang);
if (err) {
dev_err(&pdev->dev, "Failed to register SPI master/n");
goto err_register;
}
dev_dbg(hw->dev, "shutdown=%d/n", hw->bitbang.shutdown);
/* register all the devices associated */
/* 注册所用使用本spi驱动的设备 */
bi = &hw->pdata->board_info[0];
for (i = 0; i < hw->pdata->board_size; i++, bi++) {
dev_info(hw->dev, "registering %s/n", bi->modalias);
bi->controller_data = hw;
spi_new_device(master, bi);
}
return 0;
err_register:
clk_disable(hw->clk);
clk_put(hw->clk);
err_no_clk:
free_irq(hw->irq, hw);
err_no_irq:
iounmap(hw->regs);
err_no_iomap:
release_resource(hw->ioarea);
kfree(hw->ioarea);
err_no_iores:
err_no_pdata:
spi_master_put(hw->master);;
err_nomem:
return err;
}
/*
* spi_alloc_master - allocate SPI master controller
* @dev: the controller, possibly using the platform_bus
* @size: how much driver-private data to preallocate; the pointer to this
* memory is in the class_data field of the returned class_device,
* accessible with spi_master_get_devdata().
*
* This call is used only by SPI master controller drivers, which are the
* only ones directly touching chip registers. It's how they allocate
* an spi_master structure, prior to calling spi_register_master().
*
* This must be called from context that can sleep. It returns the SPI
* master structure on success, else NULL.
*
* The caller is responsible for assigning the bus number and initializing
* the master's methods before calling spi_register_master(); and (after errors
* adding the device) calling spi_master_put() to prevent a memory leak.
*/
/*注释已经写得很清楚了,本函数旨在分配spi_master struct
*其中,device为主控制设备,size为需要预分配的设备私有数据大小
*该函数被spi主控制器驱动所调用,用于在调用spi_register_master注册主控制器前
*分配spi_master struct,分配bus number和初始化主控制器的操作方法
*注意在分配spi_master struct的时候多分配了大小为size的设备私有数据
*并通过spi_master_set_devdata函数把其放到class_data field里,以后可以通过spi_master_get_devdata来访问
*/
struct spi_master * __init_or_module
spi_alloc_master(struct device *dev, unsigned size)
{
struct spi_master *master;
if (!dev)
return NULL;
master = kzalloc(size + sizeof *master, SLAB_KERNEL);
if (!master)
return NULL;
class_device_initialize(&master->cdev);
master->cdev.class = &spi_master_class;
master->cdev.dev = get_device(dev);
spi_master_set_devdata(master, &master[1]);
return master;
}
/*
* spi_bitbang_start - start up a polled/bitbanging SPI master driver
* @bitbang: driver handle
*
* Caller should have zero-initialized all parts of the structure, and then
* provided callbacks for chip selection and I/O loops. If the master has
* a transfer method, its final step should call spi_bitbang_transfer; or,
* that's the default if the transfer routine is not initialized. It should
* also set up the bus number and number of chipselects.
*
* For i/o loops, provide callbacks either per-word (for bitbanging, or for
* hardware that basically exposes a shift register) or per-spi_transfer
* (which takes better advantage of hardware like fifos or DMA engines).
*
* Drivers using per-word I/O loops should use (or call) spi_bitbang_setup and
* spi_bitbang_cleanup to handle those spi master methods. Those methods are
* the defaults if the bitbang->txrx_bufs routine isn't initialized.
*
* This routine registers the spi_master, which will process requests in a
* dedicated task, keeping IRQs unblocked most of the time. To stop
* processing those requests, call spi_bitbang_stop().
*/
int spi_bitbang_start(struct spi_bitbang *bitbang)
{
int status;
if (!bitbang->master || !bitbang->chipselect)
return -EINVAL;
/*bitbang_work
* 初始化a work,后面再create_singlethread_workqueue,
* 等到有数据要传输的时候,在spi_bitbang_transfer函数中通过调用queue_work(bitbang->workqueue, &bitbang->work)
* 把work扔进workqueue中调度运行
* 这是内核的一贯做法,在mmc/sd驱动中也是这样处理的^_^
*/
INIT_WORK(&bitbang->work, bitbang_work, bitbang);
/* 初始化自旋锁和链表头,以后用到 */
spin_lock_init(&bitbang->lock);
spi_new_device INIT_LIST_HEAD(&bitbang->queue);
if (!bitbang->master->transfer)
bitbang->master->transfer = spi_bitbang_transfer;//spi数据的传输就是通过调用这个方法来实现的
/* spi_s3c24xx.c驱动中有相应的txrx_bufs处理方法,在bitbang_work中被调用 */
if (!bitbang->txrx_bufs) {
bitbang->use_dma = 0;
bitbang->txrx_bufs = spi_bitbang_bufs;
if (!bitbang->master->setup) {
if (!bitbang->setup_transfer)
bitbang->setup_transfer =
spi_bitbang_setup_transfer;
bitbang->master->setup = spi_bitbang_setup;
bitbang->master->cleanup = spi_bitbang_cleanup;
}
/* spi_s3c24xx.c驱动中有相应的setup处理方法,在稍后的spi_new_device中被调用 */
} else if (!bitbang->master->setup)
return -EINVAL;
/* this task is the only thing to touch the SPI bits */
bitbang->busy = 0;
/* 创建工作者进程 */
bitbang->workqueue = create_singlethread_workqueue(
bitbang->master->cdev.dev->bus_id);
if (bitbang->workqueue == NULL) {
status = -EBUSY;
goto err1;
}
/* driver may get busy before register() returns, especially
* if someone registered boardinfo for devices
*/
status = spi_register_master(bitbang->master);
if (status < 0)
goto err2;
return status;
err2:
destroy_workqueue(bitbang->workqueue);
err1:
return status;
}
/**
* spi_register_master - register SPI master controller
* @master: initialized master, originally from spi_alloc_master()
*
* SPI master controllers connect to their drivers using some non-SPI bus,
* such as the platform bus. The final stage of probe() in that code
* includes calling spi_register_master() to hook up to this SPI bus glue.
*
* SPI controllers use board specific (often SOC specific) bus numbers,
* and board-specific addressing for SPI devices combines those numbers
* with chip select numbers. Since SPI does not directly support dynamic
* device identification, boards need configuration tables telling which
* chip is at which address.
*
* This must be called from context that can sleep. It returns zero on
* success, else a negative error code (dropping the master's refcount).
* After a successful return, the caller is responsible for calling
* spi_unregister_master().
*/
int __init_or_module
spi_register_master(struct spi_master *master)
{
static atomic_t dyn_bus_id = ATOMIC_INIT((1<<16) - 1);
struct device *dev = master->cdev.dev;
int status = -ENODEV;
int dynamic = 0;
if (!dev)
return -ENODEV;
/* convention: dynamically assigned bus IDs count down from the max */
if (master->bus_num < 0) {
master->bus_num = atomic_dec_return(&dyn_bus_id);
dynamic = 1;
}
/* register the device, then userspace will see it.
* registration fails if the bus ID is in use.
*/
snprintf(master->cdev.class_id, sizeof master->cdev.class_id,
"spi%u", master->bus_num);
status = class_device_add(&master->cdev);//注册设备
if (status < 0)
goto done;
dev_dbg(dev, "registered master %s%s/n", master->cdev.class_id,
dynamic ? " (dynamic)" : "");
/* populate children from any spi device tables */
scan_boardinfo(master);
status = 0;
done:
return status;
}
/* FIXME someone should add support for a __setup("spi", ...) that
* creates board info from kernel command lines
*/
/*
* scan board_list for spi_board_info which is registered by spi_register_board_info
* 很可惜,s3c24xx的spi驱动中不支持spi_register_board_info这种标准方式注册方式,而是直接调用spi_new_device内部函数
*/
static void __init_or_module
scan_boardinfo(struct spi_master *master)
{
struct boardinfo *bi;
struct device *dev = master->cdev.dev;
down(&board_lock);
list_for_each_entry(bi, &board_list, list) {
struct spi_board_info *chip = bi->board_info;
unsigned n;
for (n = bi->n_board_info; n > 0; n--, chip++) {
if (chip->bus_num != master->bus_num)
continue;
/* some controllers only have one chip, so they
* might not use chipselects. otherwise, the
* chipselects are numbered 0..max.
*/
if (chip->chip_select >= master->num_chipselect
&& master->num_chipselect) {
dev_dbg(dev, "cs%d > max %d/n",
chip->chip_select,
master->num_chipselect);
continue;
}
(void) spi_new_device(master, chip);
}
}
up(&board_lock);
}
/*
* Board-specific early init code calls this (probably during arch_initcall)
* with segments of the SPI device table. Any device nodes are created later,
* after the relevant parent SPI controller (bus_num) is defined. We keep
* this table of devices forever, so that reloading a controller driver will
* not make Linux forget about these hard-wired devices.
*
* Other code can also call this, e.g. a particular add-on board might provide
* SPI devices through its expansion connector, so code initializing that board
* would naturally declare its SPI devices.
*
* The board info passed can safely be __initdata ... but be careful of
* any embedded pointers (platform_data, etc), they're copied as-is.
*/
int __init
spi_register_board_info(struct spi_board_info const *info, unsigned n)
{
struct boardinfo *bi;
bi = kmalloc(sizeof(*bi) + n * sizeof *info, GFP_KERNEL);
if (!bi)
return -ENOMEM;
bi->n_board_info = n;
memcpy(bi->board_info, info, n * sizeof *info);
down(&board_lock);
list_add_tail(&bi->list, &board_list);
up(&board_lock);
return 0;
}
/* On typical mainboards, this is purely internal; and it's not needed
* after board init creates the hard-wired devices. Some development
* platforms may not be able to use spi_register_board_info though, and
* this is exported so that for example a USB or parport based adapter
* driver could add devices (which it would learn about out-of-band).
*/
struct spi_device *__init_or_module
spi_new_device(struct spi_master *master, struct spi_board_info *chip)
{
struct spi_device *proxy;//这个结构很重要,以后就是通过这个结构来操作实际的spi设备的
struct device *dev = master->cdev.dev;
int status;
/* NOTE: caller did any chip->bus_num checks necessary */
if (!spi_master_get(master))
return NULL;
proxy = kzalloc(sizeof *proxy, GFP_KERNEL);
if (!proxy) {
dev_err(dev, "can't alloc dev for cs%d/n",
chip->chip_select);
goto fail;
}
/* 初始化spi_device 结构各成员 */
proxy->master = master;
proxy->chip_select = chip->chip_select;
proxy->max_speed_hz = chip->max_speed_hz;
proxy->mode = chip->mode;
proxy->irq = chip->irq;
proxy->modalias = chip->modalias;
snprintf(proxy->dev.bus_id, sizeof proxy->dev.bus_id,
"%s.%u", master->cdev.class_id,
chip->chip_select);
proxy->dev.parent = dev;
proxy->dev.bus = &spi_bus_type;
proxy->dev.platform_data = (void *) chip->platform_data;
proxy->controller_data = chip->controller_data;
proxy->controller_state = NULL;
proxy->dev.release = spidev_release;
/* drivers may modify this default i/o setup */
/* 调用master->setup(即s3c24xx_spi_setup)函数初始化spi设备 */
status = master->setup(proxy);
if (status < 0) {
dev_dbg(dev, "can't %s %s, status %d/n",
"setup", proxy->dev.bus_id, status);
goto fail;
}
/* driver core catches callers that misbehave by defining
* devices that already exist.
*/
status = device_register(&proxy->dev);//真正注册原始设备
if (status < 0) {
dev_dbg(dev, "can't %s %s, status %d/n",
"add", proxy->dev.bus_id, status);
goto fail;
}
dev_dbg(dev, "registered child %s/n", proxy->dev.bus_id);
return proxy;
fail:
spi_master_put(master);
kfree(proxy);
return NULL;
}
static int s3c24xx_spi_setup(struct spi_device *spi)
{
int ret;
/* 进行一些检查性操作 */
if (!spi->bits_per_word)
spi->bits_per_word = 8;
if ((spi->mode & SPI_LSB_FIRST) != 0)
return -EINVAL;
ret = s3c24xx_spi_setupxfer(spi, NULL);
if (ret < 0) {
dev_err(&spi->dev, "setupxfer returned %d/n", ret);
return ret;
}
dev_dbg(&spi->dev, "%s: mode %d, %u bpw, %d hz/n",
__FUNCTION__, spi->mode, spi->bits_per_word,
spi->max_speed_hz);
return 0;
}
static int s3c24xx_spi_setupxfer(struct spi_device *spi,
struct spi_transfer *t)
{
struct s3c24xx_spi *hw = to_hw(spi);
unsigned int bpw;
unsigned int hz;
unsigned int div;
bpw = t ? t->bits_per_word : spi->bits_per_word;
hz = t ? t->speed_hz : spi->max_speed_hz;
if (bpw != 8) {
dev_err(&spi->dev, "invalid bits-per-word (%d)/n", bpw);
return -EINVAL;
}
div = clk_get_rate(hw->clk) / hz;
/* is clk = pclk / (2 * (pre+1)), or is it
* clk = (pclk * 2) / ( pre + 1) */
div = (div / 2) - 1;//求出预分频值
if (div < 0)
div = 1;
if (div > 255)
div = 255;
dev_dbg(&spi->dev, "setting pre-scaler to %d (hz %d)/n", div, hz);
writeb(div, hw->regs + S3C2410_SPPRE);//设置预分频值
spin_lock(&hw->bitbang.lock);
if (!hw->bitbang.busy) {
hw->bitbang.chipselect(spi, BITBANG_CS_INACTIVE);//修改时钟,先禁用spi
/* need to ndelay for 0.5 clocktick ? */
}
spin_unlock(&hw->bitbang.lock);
return 0;
}
static void s3c24xx_spi_chipsel(struct spi_device *spi, int value)
{
struct s3c24xx_spi *hw = to_hw(spi);
unsigned int cspol = spi->mode & SPI_CS_HIGH ? 1 : 0;
unsigned int spcon;
switch (value) {
case BITBANG_CS_INACTIVE:
/* 禁用spi(禁用片选) */
if (hw->pdata->set_cs)
hw->pdata->set_cs(hw->pdata, value, cspol);
else
s3c2410_gpio_setpin(hw->pdata->pin_cs, cspol ^ 1);
break;
case BITBANG_CS_ACTIVE:
/*
* 启用spi:根据需要设置寄存器并启用使能片选
* (如果spi_board_info中没有设置相应的mode选项的话,那就只能使用默认值SPPIN_DEFAULT和SPCON_DEFAULT了)
*/
spcon = readb(hw->regs + S3C2410_SPCON);
if (spi->mode & SPI_CPHA)
spcon |= S3C2410_SPCON_CPHA_FMTB;
else
spcon &= ~S3C2410_SPCON_CPHA_FMTB;
if (spi->mode & SPI_CPOL)
spcon |= S3C2410_SPCON_CPOL_HIGH;
else
spcon &= ~S3C2410_SPCON_CPOL_HIGH;
spcon |= S3C2410_SPCON_ENSCK;
/* write new configration */
writeb(spcon, hw->regs + S3C2410_SPCON);
if (hw->pdata->set_cs)
hw->pdata->set_cs(hw->pdata, value, cspol);
else
s3c2410_gpio_setpin(hw->pdata->pin_cs, cspol);
break;
}
}
好了,至此spi主控制器(驱动)和板上spi设备注册完毕,以后要使用spi来传输数据的话,只要先获得spi设备结构,然后就可以利用它来和spi驱动打交道了(就好像你要操作一个文件,先要获取文件句柄一样,明白吧^_^)
原文地址 http://www.cnitblog.com/luofuchong/archive/2007/09/24/33942.html
~~~~~~~~~~~~~~~~~~``
任何SPI都有4种模式
2008-11-06 22:17
void McBSPObj::McBSP0Init(void)//McBSP从设备SPI硬件配置
{
/*-------------------------------------------------------------
DSP5402之McBSP从设备SPI硬件四种模式配置实例(经过硬件测试)
--------------------------------------------------------------*/
//PCR设置过程(FSM=0,CLKM=0)
McBSP0->SPSA = PCR;
McBSP1->SPSD = (0 << PCR_XIOEN) //发送非通用I/O模式位
| (0 << PCR_RIOEN) //接收非通用I/O模式位
| (0 << PCR_FSXM) //外部发送帧同步脉冲(外部片选)
| (0 << PCR_FSRM) //外部接收帧同步脉冲(外部片选)
| (0 << PCR_CLKXM) //外部发送时钟(外部时钟源)
| (0 << PCR_CLKRM) //外部接收时钟(外部时钟源)
#if SPIMODE == 0
//SPI设置过程00(0--FS高电平有效,0--CLK上升沿收发数据)
| (0 << PCR_FSXP) //发送帧同步脉冲极性(高电平有效)
| (0 << PCR_FSRP) //接收帧同步脉冲极性(高电平有效)
| (0 << PCR_CLKXP) //发送时钟极性(上升沿发送数据)
| (0 << PCR_CLKRP);//接收时钟极性(上升沿接收数据)
#endif
#if SPIMODE == 1
//SPI设置过程01(0--FS高电平有效,1--CLK下降沿收发数据)
| (0 << PCR_FSXP) //发送帧同步脉冲极性(高电平有效)
| (0 << PCR_FSRP) //接收帧同步脉冲极性(高电平有效)
| (1 << PCR_CLKXP) //发送时钟极性(下降沿发送数据)
| (1 << PCR_CLKRP);//接收时钟极性(下降沿接收数据)
#endif
#if SPIMODE == 2
//SPI设置过程10(1--FS低电平有效,0--CLK上升沿收发数据)
| (1 << PCR_FSXP) //发送帧同步脉冲极性(低电平有效)
| (1 << PCR_FSRP) //接收帧同步脉冲极性(低电平有效)
| (0 << PCR_CLKXP) //发送时钟极性(上升沿发送数据)
| (0 << PCR_CLKRP);//接收时钟极性(上升沿接收数据)
#endif
#if SPIMODE == 3
//SPI设置过程11(1--FS低电平有效,1--CLK下降沿收发数据)
| (1 << PCR_FSXP) //发送帧同步脉冲极性(低电平有效)
| (1 << PCR_FSRP) //接收帧同步脉冲极性(低电平有效)
| (1 << PCR_CLKXP) //发送时钟极性(下降沿发送数据)
| (1 << PCR_CLKRP);//接收时钟极性(下降沿接收数据)
#endif
//MCR1设置过程(RMCM=0)
McBSP0->SPSA = MCR1;//
McBSP0->SPSD = (0 << MCR1_RMCM) //允许接收多通道选择(0)
| (0x00 << MCR1_RPBBLK)
| (0x00 << MCR1_RPABLK)
| (0x00 << MCR1_RCBLK);
//MCR2设置过程(XMCM=0)
McBSP0->SPSA = MCR2;//
McBSP0->SPSD = (0x00 << MCR2_XMCM) //允许发送多通道选择(00b)
| (0x00 << MCR2_XPBBLK)
| (0x00 << MCR2_XPABLK)
| (0x00 << MCR2_XCBLK);
//RCERA设置过程
McBSP0->SPSA = RCERA;//
McBSP0->SPSD = 0;
//RCERB设置过程
McBSP0->SPSA = RCERB;//
McBSP0->SPSD = 0;
//XCERA设置过程
McBSP0->SPSA = XCERA;//
McBSP0->SPSD = 0;
//XCERB设置过程
McBSP0->SPSA = XCERB;//
McBSP0->SPSD = 0;
//XCR1设置过程
McBSP0->SPSA = XCR1;//
McBSP0->SPSD = (0x00 << XCR1_XFRLEN1) //每帧1个字(每帧中断的次数1!!!)
// | (0x02 << XCR1_XWDLEN1);//每字16位长(每次中断的字节数2!!!)
| (0x00 << XCR1_XWDLEN1);//每字8位长(每次中断的字节数2!!!)
//XCR2设置过程
McBSP0->SPSA = XCR2;
McBSP0->SPSD = (0 << XCR2_XPHASE) //单相帧(其他设置都为0)
| (0x00 << XCR2_XCOMPAND)//发送数据从最高位(MSB)开始
| (0x00 << XCR2_XDATDLY);//同步后延迟0位数据
//RCR1设置过程
McBSP0->SPSA = RCR1;
McBSP0->SPSD = (0x00 << RCR1_RFRLEN1) //每帧1个字(每帧中断的次数1!!!)
// | (0x02 << RCR1_RWDLEN1);//每字16位长(每次中断的字节数2!!!)
| (0x00 << RCR1_RWDLEN1);//每字8位长(每次中断的字节数2!!!)
//RCR2设置过程
McBSP0->SPSA = RCR2;
McBSP0->SPSD = (0 << RCR2_RPHASE) //单相帧(其他设置都为0)
| (0x00 << RCR2_RCOMPAND)//接收数据从最高位(MSB)开始
| (0x00 << RCR2_RDATDLY);//同步后延迟0位数据
//SRGR1设置过程
McBSP0->SPSA = SRGR1;
McBSP0->SPSD = (0x00 << SRGR1_CLKGDV);//1
//SRGR2设置过程
McBSP0->SPSA = SRGR2;
McBSP0->SPSD = (0 << SRGR2_FSGM)
| (1 << SRGR2_CLKSM)//由CPU时钟产生的采样率时钟1
| (0 << SRGR2_CLKSP)//0
| (1 << SRGR2_GSYNC)//
| (0x0f << SRGR2_FPER);//0x0f
//SPCR1设置过程(CLKSTP=1Xb,RINTM=00b)
McBSP0->SPSA = SPCR1;
McBSP0->SPSD = (0x00 << SPCR1_RINTM) //接收中断模式00(每帧接收1次中?
| (0 << SPCR1_DLB) //禁止回送
| (1 << SPCR1_DXENA) //DX使能
| (0x00 << SPCR1_RJUST) //接收符号不扩展
| (0x02 << SPCR1_CLKSTP);//SPI模式时钟开始于上升沿(无延迟)
//SPCR2设置过程(XINTM=02b)
McBSP0->SPSA = SPCR2;
McBSP0->SPSD = (0x02 << SPCR2_XINTM)//发送中断模式02
| (1 << SPCR2_XEMPTY) //发送移位寄存器空
| (1 << SPCR2_XRDY); //发送准备好
//SPCR1复位过程
McBSP0->SPSA = SPCR1;
McBSP0->SPSD|= (1 << SPCR1_RRST);//接收器复位
//SPCR2复位过程
McBSP0->SPSA = SPCR2;
McBSP0->SPSD|= (1 << SPCR2_XRST)//发送器复位
| (1 << SPCR2_GRST)//采样率发生器复位
| (1 << SPCR2_FRST);//帧同步发生器复位
//清除允许BXINT0中断过程
// SREG->IFR = (1 << IFR_BXINT0);//清除BXINT0中断标志
// SREG->IMR |= (1 << IMR_BXINT0);//允许BXINT0中断
//清除允许BRINT0中断过程
SREG->IFR = (1 << IFR_BRINT0);//清除BRINT0中断标志
SREG->IMR |= (1 << IMR_BRINT0);//允许BRINT0中断
}
void McBSPObj::McBSP1Init(void)//GPIO配置
{
McBSP1->SPSA = SPCR1;
McBSP1->SPSD = 0;
McBSP1->SPSA = SPCR2;
McBSP1->SPSD = 0;
McBSP1->SPSA = PCR;
//设置收发为IO接口,DX输出,DR,CLKS输入
McBSP1->SPSD = (1 << PCR_RIOEN) //通用I/O模式位
//硬件RDDOG喂狗信号(BDX1)管脚输出模式默认I/O设置
| (1 << PCR_XIOEN) //通用I/O模式位
//硬件RD SIA信号(BFSR1)管脚输入模式设置
| (0 << PCR_FSRM) //FSR为输入IO
//硬件FM CE信号(BFSX1)管脚输出模式设置
| (1 << PCR_FSXM) //FSR为输出IO,FLASH的片选信号
//硬件FM CE信号(BFSX1)管脚高电平输出控制
| (1 << PCR_FSXP) //关闭FLASH的片选信号(FSX=1)
| (0 << PCR_CLKRM) //CLKR为输入IO
| (0 << PCR_CLKXM); //CLKX为输入IO,CLKX信号
}
- SPI驱动流程(S3C2440)
- S3C2440 spi驱动简单测试 (转)
- S3C2440 spi驱动简单测试
- S3C2440 spi驱动简单测试
- spi 驱动工作流程
- spi驱动数据传输流程
- spi驱动流程学习
- S3C2440 Linux驱动移植——SPI
- S3C2440驱动移植——SPI
- S3C2440驱动移植——SPI .
- S3C2440 SPI驱动简单测试
- S3C2440 SPI驱动简单测试
- s3c2440 spi驱动DMA模式
- s3c2440 spi驱动DMA模式
- S3C2440 Linux驱动移植——SPI
- S3C2440 Linux驱动移植——SPI
- linux S3C2440 spi 驱动简单测试 (基于简单字符驱动)
- linux S3C2440 spi 驱动简单测试 (基于简单字符驱动)
- treeview应用实例
- 思考总会有收获吧
- 敏捷开发(转自:http://www.cnblogs.com/blusehuang/archive/2007/10/17/926802.html)
- 如何在Linux环境下在CodeBlock中安装使用Google Protobuf
- 《深入解析ORACLE》学习笔记(1)---ORACLE数据库启动与关闭
- SPI驱动流程(S3C2440)
- 加入CSDN 绝对没错
- 浅析HTC进军大陆
- ConsistentHash 实现
- GPL 与 LGPL 的区别
- Zend_Paginator分页
- S3C2410中SD卡驱动的移植
- Mahout
- C# 线程间操作无效