linux spi驱动开发学习(三)-----spi_bitbang.c详解
2017-07-19 17:30
597 查看
经过了前面两节的学习,现在到了这个环节了,spi驱动的完整工作过程渐渐明朗起来
不多说废话了,直接进主题,大家共同学习,共同进步
首先,还是先唠叨以下,以方便接下来对bitbang机制的学习,那就是spi 的工作时序,这里直接转载自己看到的一篇文章
http://www.52rd.com/Blog/Detail_RD.Blog_yuwenxin_21678.html?#Flag_Comment
这里也同时感谢楼主的分享
(2009-10-18 21:49)
SPI接口在模式0下输出第一位数据的时刻
SPI接口有四种不同的数据传输时序,取决于CPOL和CPHL这两位的组合。图1中表现了这四种时序,
时序与CPOL、CPHL的关系也可以从图中看出。
图1
CPOL是用来决定SCK时钟信号空闲时的电平,CPOL=0,空闲电平为低电平,CPOL=1时,
空闲电平为高电平。CPHA是用来决定采样时刻的,CPHA=0,在每个周期的第一个时钟沿采样,
CPHA=1,在每个周期的第二个时钟沿采样。
由于我使用的器件工作在模式0这种时序(CPOL=0,CPHA=0),所以将图1简化为图2,
只关注模式0的时序。
图2
我们来关注SCK的第一个时钟周期,在时钟的前沿采样数据(上升沿,第一个时钟沿),
在时钟的后沿输出数据(下降沿,第二个时钟沿)。首先来看主器件,主器件的输出口(MOSI)输出的数据bit1,
在时钟的前沿被从器件采样,那主器件是在何时刻输出bit1的呢?bit1的输出时刻实际上在SCK信号有效以前,
比 SCK的上升沿还要早半个时钟周期。bit1的输出时刻与SSEL信号没有关系。再来看从器件,
主器件的输入口MISO同样是在时钟的前沿采样从器件输出的bit1的,那从器件又是在何时刻输出bit1的呢。
从器件是在SSEL信号有效后,立即输出bit1,尽管此时SCK信号还没有起效。关于上面的主器件
和从器件输出bit1位的时刻,可以从图3、4中得到验证。
图3
注意图3中,CS信号有效后(低电平有效,注意CS下降沿后发生的情况),故意用延时程序
延时了一段时间,之后再向数据寄存器写入了要发送的数据,来观察主器件输出bit1的情况(MOSI)。
可以看出,bit1(值为1)是在SCK信号有效之前的半个时钟周期的时刻开始输出的(与CS信号无关),
到了SCK的第一个时钟周期的上升沿正好被从器件采样。
图4
图4中,注意看CS和MISO信号。我们可以看出,CS信号有效后,从器件立刻输出了bit1(值为1)。
通常我们进行的spi操作都是16位的。图5记录了第一个字节和第二个字节间的相互衔接的过程。
第一个字节的最后一位在SCK的上升沿被采样,随后的SCK下降沿,从器件就输出了第二个字节的第一位。
图5
熟悉了spi 的工作时序,接着往下说
注意:
CPOL是用来决定SCK时钟信号空闲时的电平,CPOL=0,空闲电平为低电平,CPOL=1时,空闲电平为高电平。
CPHA是用来决定采样时刻的,
CPHA=0,在每个周期的第一个时钟沿采样,
CPHA=1,在每个周期的第二个时钟沿采样。
linux的SPI模型中重要的有如下几个结构体,位置include/linux/spi/spi.h
struct spi_device {} //Master side proxy for an SPI slave device
struct spi_driver {} //Host side "protocol" driver
struct spi_master {} //interface to SPI master controller
struct spi_transfer{} //a read/write buffer pair
在这几个结构体中,我们只注意一下device结构体
struct spi_device
{
.....
#define SPI_CPHA 0x01 /* clock phase 同步*/
#define SPI_CPOL 0x02 /* clock polarity
*/
#define SPI_MODE_0 (0|0)
/*
(original MicroWire)
*/
#define SPI_MODE_1 (0|SPI_CPHA)
#define SPI_MODE_2 (SPI_CPOL|0)
#define SPI_MODE_3 (SPI_CPOL|SPI_CPHA)
.....
}
注意如上定义的 SPI_CPHA和SPI_CPOL
这个和一中的CPHA,以及CPOL是对应的,
然后在次基础上定义了MODE?,到此,你是否能相像出SPI接口的数据传输过程?
下面才是刚刚进入真正的主题
以下是spi_bitbang结构体的定义
点击(此处)折叠或打开
struct spi_bitbang {
struct workqueue_struct
*workqueue;
struct work_struct work;
spinlock_t lock;
struct list_head queue;
u8 busy;
u8 use_dma;
u8 flags;
/* extra spi->mode support
*/
struct spi_master *master;
/* setup_transfer() changes clock
and/or wordsize
to match settings
* for this transfer; zeroes restore defaults from spi_device.
*/
int (*setup_transfer)(struct spi_device
*spi,
struct spi_transfer *t);
void (*chipselect)(struct spi_device
*spi,
int is_on);
#define BITBANG_CS_ACTIVE 1 /* normally nCS, active low
*/
#define BITBANG_CS_INACTIVE 0
/* txrx_bufs() may handle dma mapping
for transfers that don't
* already have one
(transfer.{tx,rx}_dma
is zero),
or use PIO
*/
int
(*txrx_bufs)(struct
spi_device *spi, struct spi_transfer
*t);
/* txrx_word[SPI_MODE_*]()
just looks like a shift register */
u32 (*txrx_word[4])(struct
spi_device *spi,
unsigned nsecs,
u32 word, u8 bits);
};
这里我们只对加底纹部分做以下说明:
struct work_struct work;
这个让我们想入菲菲--bit bang是按照workqueue队列的形式来工作的吗?
worqueue这个是什么?简单地说,他就是一个队列,里面的每一个work节点代表着一个需要调度的工作。
具体可以百度,这个和tasklet有点相似的。以前自己有用过,经常用于中断的顶半部,底半部机制
既然是worqueue,那么我们可以猜想,spi是在每一个work中实现bit bang的。
事实上确实如此,在/driver/spi/spi_bitbang.c#L267中,我们可以看到如下函数
static void bitbang_work(struct work_struct *work){},
在/driver/spi/spi_bitbang.c中,我们可以发现
int spi_bitbang_start(struct spi_bitbang *bitbang);
这里还是有必要看一下这个方法的实现过程,看他都干了些什么:
点击(此处)折叠或打开
int spi_bitbang_start(struct spi_bitbang
*bitbang)
{
int status;
if (!bitbang->master
||
!bitbang->chipselect)
return -EINVAL;
INIT_WORK(&bitbang->work,
bitbang_work);
spin_lock_init(&bitbang->lock);
INIT_LIST_HEAD(&bitbang->queue);
if (!bitbang->master->mode_bits)//此处在s3c24xx_spi_probe方法中已定义,采用s3c24xx_spi_probe的设置
bitbang->master->mode_bits
= SPI_CPOL | SPI_CPHA
| bitbang->flags;
if (!bitbang->master->transfer)//此处在s3c24xx_spi_probe方法中没有定义,所以使用这里设置的方法
bitbang->master->transfer
= spi_bitbang_transfer;//之后发送数据的时候会用到这个接口
if (!bitbang->txrx_bufs)
{//此处在s3c24xx_spi_probe方法中已定义,采用s3c24xx_spi_probe的设置
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;
}
} else
if (!bitbang->master->setup)//此处在s3c24xx_spi_probe方法中已定义,从这里看来bitbang->master->setup必须定义,不可遗漏
return -EINVAL;
/* this task
is the only thing
to touch the SPI bits */
bitbang->busy
= 0;
bitbang->workqueue
= create_singlethread_workqueue(
dev_name(bitbang->master->dev.parent));
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;
}
EXPORT_SYMBOL_GPL(spi_bitbang_start);
看看spi_register_master的实现过程:
点击(此处)折叠或打开
int spi_register_master(struct spi_master
*master)
{
static atomic_t dyn_bus_id = ATOMIC_INIT((1<<15)
- 1);
struct device *dev
= master->dev.parent;
int status
= -ENODEV;
int dynamic
= 0;
if (!dev)
return -ENODEV;
/* even
if it's just one always-selected device, there must
* be at least one chipselect
*/
if (master->num_chipselect
== 0)
return -EINVAL;
/* convention: dynamically assigned bus IDs count down from the max
*/
if (master->bus_num
< 0)
{
/* FIXME switch
to an IDR based scheme, something like
* I2C
now uses, so we can't run out of
"dynamic" IDs
*/
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.
*/
dev_set_name(&master->dev,
"spi%u", master->bus_num);
status = device_add(&master->dev);//添加设备
if (status
< 0)
goto done;
dev_dbg(dev,
"registered master %s%sn", dev_name(&master->dev),
dynamic ?
" (dynamic)" :
"");
/* populate children from any spi device tables
*/
scan_boardinfo(master);
status = 0;
done:
return status;
}
EXPORT_SYMBOL_GPL(spi_register_master);
看看scan_boardinfo原型:
点击(此处)折叠或打开
static void scan_boardinfo(struct spi_master
*master)
{
struct boardinfo *bi;
mutex_lock(&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;
/* NOTE: this relies
on spi_new_device
to
* issue diagnostics when given bogus inputs
*/
(void)
spi_new_device(master,
chip);
}
}
mutex_unlock(&board_lock);
}
点击(此处)折叠或打开
struct spi_device *spi_new_device(struct spi_master
*master,
struct spi_board_info *chip)
{
struct spi_device *proxy;
int status;
/* NOTE: caller did any chip->bus_num checks necessary.
*
* Also, unless we change the return value convention
to use
* error-or-pointer
(not
NULL-or-pointer), troubleshootability
* suggests syslogged diagnostics are best here
(ugh).
*/
proxy = spi_alloc_device(master);
if (!proxy)
return NULL;
WARN_ON(strlen(chip->modalias)
>= sizeof(proxy->modalias));
proxy->chip_select
= chip->chip_select;
proxy->max_speed_hz
= chip->max_speed_hz;
proxy->mode
= chip->mode;
proxy->irq
= chip->irq;
strlcpy(proxy->modalias, chip->modalias,
sizeof(proxy->modalias));
proxy->dev.platform_data
= (void
*) chip->platform_data;
proxy->controller_data
= chip->controller_data;
proxy->controller_state
= NULL;
status = spi_add_device(proxy);
if (status
< 0)
{
spi_dev_put(proxy);
return NULL;
}
return proxy;
}
EXPORT_SYMBOL_GPL(spi_new_device);
点击(此处)折叠或打开
struct spi_device *spi_alloc_device(struct spi_master
*master)
{
struct spi_device *spi;
struct device *dev
= master->dev.parent;
if (!spi_master_get(master))
return NULL;
spi = kzalloc(sizeof
*spi, GFP_KERNEL);
if (!spi)
{
dev_err(dev,
"cannot alloc spi_devicen");
spi_master_put(master);
return NULL;
}
spi->master
= master;
spi->dev.parent
= dev;
spi->dev.bus
= &spi_bus_type;
spi->dev.release
= spidev_release;
device_initialize(&spi->dev);
return spi;
}
EXPORT_SYMBOL_GPL(spi_alloc_device);
点击(此处)折叠或打开
int spi_add_device(struct spi_device
*spi)
{
static DEFINE_MUTEX(spi_add_lock);
struct device *dev
= spi->master->dev.parent;
int status;
/* Chipselects are numbered 0..max; validate.
*/
if (spi->chip_select
>= spi->master->num_chipselect)
{
dev_err(dev,
"cs%d >= max %dn",
spi->chip_select,
spi->master->num_chipselect);
return -EINVAL;
}
/*
Set the bus ID string
*/
dev_set_name(&spi->dev,
"%s.%u", dev_name(&spi->master->dev),
spi->chip_select);
/* We need
to make sure there's no other device with this
* chipselect
**BEFORE** we
call setup(),
else we'll trash
* its configuration. Lock against concurrent add() calls.
*/
mutex_lock(&spi_add_lock);
if (bus_find_device_by_name(&spi_bus_type,
NULL, dev_name(&spi->dev))
!=
NULL)
{
dev_err(dev,
"chipselect %d already in usen",
spi->chip_select);
status =
-EBUSY;
goto done;
}
/* Drivers may modify this initial i/o setup, but will
* normally rely
on the device being setup. Devices
* using SPI_CS_HIGH can't coexist well otherwise...
*/
status = spi_setup(spi);
if (status
< 0)
{
dev_err(dev,
"can't %s %s, status %dn",
"setup", dev_name(&spi->dev),
status);
goto done;
}
/* Device may be bound
to an active driver when this returns
*/
status = device_add(&spi->dev);
if (status
< 0)
dev_err(dev,
"can't %s %s, status %dn",
"add", dev_name(&spi->dev),
status);
else
dev_dbg(dev,
"registered child %sn", dev_name(&spi->dev));
done:
mutex_unlock(&spi_add_lock);
return status;
}
EXPORT_SYMBOL_GPL(spi_add_device);
点击(此处)折叠或打开
int spi_setup(struct spi_device
*spi)
{
unsigned bad_bits;
int status;
/* help drivers fail
*cleanly* when they need options
* that aren't supported with their current master
*/
bad_bits = spi->mode
& ~spi->master->mode_bits;
if (bad_bits)
{
dev_dbg(&spi->dev,
"setup: unsupported mode bits %xn",
bad_bits);
return -EINVAL;
}
if (!spi->bits_per_word)
spi->bits_per_word
= 8;
status = spi->master->setup(spi);//这里其实调用的就是s3c24xx_spi_probe里填充的spi->master->setup
dev_dbg(&spi->dev,
"setup mode %d, %s%s%s%s"
"%u bits/w, %u Hz max --> %dn",
(int)
(spi->mode
& (SPI_CPOL
| SPI_CPHA)),
(spi->mode
& SPI_CS_HIGH)
? "cs_high, "
: "",
(spi->mode
& SPI_LSB_FIRST)
? "lsb, "
: "",
(spi->mode
& SPI_3WIRE)
? "3wire, "
: "",
(spi->mode
& SPI_LOOP)
? "loopback, "
: "",
spi->bits_per_word, spi->max_speed_hz,
status);
return status;
}
EXPORT_SYMBOL_GPL(spi_setup);
点击(此处)折叠或打开
static int s3c24xx_spi_setup(struct spi_device
*spi)
{
struct s3c24xx_spi_devstate *cs
= spi->controller_state;
struct s3c24xx_spi *hw
= to_hw(spi);
int ret;
/* allocate settings
on the first call
*/
if (!cs)
{
cs = kzalloc(sizeof(struct s3c24xx_spi_devstate), GFP_KERNEL);
if (!cs)
{
dev_err(&spi->dev,
"no memory for controller staten");
return -ENOMEM;
}
cs->spcon
= SPCON_DEFAULT;
cs->hz
= -1;
spi->controller_state
= cs;
}
/* initialise the state from the device
*/
ret = s3c24xx_spi_update_state(spi,
NULL);
if (ret)
return ret;
spin_lock(&hw->bitbang.lock);
if (!hw->bitbang.busy)
{
hw->bitbang.chipselect(spi,
BITBANG_CS_INACTIVE);
/* need
to ndelay for 0.5 clocktick
? */
}
spin_unlock(&hw->bitbang.lock);
return 0;
}
好了,至此spi主控制器(驱动)和板上spi设备注册完毕,以后要使用spi来传输数据的话,只要先获得spi设备结构,然后就可以利用它来和spi驱动打交道了(就好像你要操作一个文件,先要获取文件句柄一样,明白吧^_^)
不多说废话了,直接进主题,大家共同学习,共同进步
首先,还是先唠叨以下,以方便接下来对bitbang机制的学习,那就是spi 的工作时序,这里直接转载自己看到的一篇文章
http://www.52rd.com/Blog/Detail_RD.Blog_yuwenxin_21678.html?#Flag_Comment
这里也同时感谢楼主的分享
SPI时序图详解
(2009-10-18 21:49)SPI接口在模式0下输出第一位数据的时刻
SPI接口有四种不同的数据传输时序,取决于CPOL和CPHL这两位的组合。图1中表现了这四种时序,
时序与CPOL、CPHL的关系也可以从图中看出。
图1
CPOL是用来决定SCK时钟信号空闲时的电平,CPOL=0,空闲电平为低电平,CPOL=1时,
空闲电平为高电平。CPHA是用来决定采样时刻的,CPHA=0,在每个周期的第一个时钟沿采样,
CPHA=1,在每个周期的第二个时钟沿采样。
由于我使用的器件工作在模式0这种时序(CPOL=0,CPHA=0),所以将图1简化为图2,
只关注模式0的时序。
图2
我们来关注SCK的第一个时钟周期,在时钟的前沿采样数据(上升沿,第一个时钟沿),
在时钟的后沿输出数据(下降沿,第二个时钟沿)。首先来看主器件,主器件的输出口(MOSI)输出的数据bit1,
在时钟的前沿被从器件采样,那主器件是在何时刻输出bit1的呢?bit1的输出时刻实际上在SCK信号有效以前,
比 SCK的上升沿还要早半个时钟周期。bit1的输出时刻与SSEL信号没有关系。再来看从器件,
主器件的输入口MISO同样是在时钟的前沿采样从器件输出的bit1的,那从器件又是在何时刻输出bit1的呢。
从器件是在SSEL信号有效后,立即输出bit1,尽管此时SCK信号还没有起效。关于上面的主器件
和从器件输出bit1位的时刻,可以从图3、4中得到验证。
图3
注意图3中,CS信号有效后(低电平有效,注意CS下降沿后发生的情况),故意用延时程序
延时了一段时间,之后再向数据寄存器写入了要发送的数据,来观察主器件输出bit1的情况(MOSI)。
可以看出,bit1(值为1)是在SCK信号有效之前的半个时钟周期的时刻开始输出的(与CS信号无关),
到了SCK的第一个时钟周期的上升沿正好被从器件采样。
图4
图4中,注意看CS和MISO信号。我们可以看出,CS信号有效后,从器件立刻输出了bit1(值为1)。
通常我们进行的spi操作都是16位的。图5记录了第一个字节和第二个字节间的相互衔接的过程。
第一个字节的最后一位在SCK的上升沿被采样,随后的SCK下降沿,从器件就输出了第二个字节的第一位。
图5
熟悉了spi 的工作时序,接着往下说
注意:
CPOL是用来决定SCK时钟信号空闲时的电平,CPOL=0,空闲电平为低电平,CPOL=1时,空闲电平为高电平。
CPHA是用来决定采样时刻的,
CPHA=0,在每个周期的第一个时钟沿采样,
CPHA=1,在每个周期的第二个时钟沿采样。
linux的SPI模型中重要的有如下几个结构体,位置include/linux/spi/spi.h
struct spi_device {} //Master side proxy for an SPI slave device
struct spi_driver {} //Host side "protocol" driver
struct spi_master {} //interface to SPI master controller
struct spi_transfer{} //a read/write buffer pair
在这几个结构体中,我们只注意一下device结构体
struct spi_device
{
.....
#define SPI_CPHA 0x01 /* clock phase 同步*/
#define SPI_CPOL 0x02 /* clock polarity
*/
#define SPI_MODE_0 (0|0)
/*
(original MicroWire)
*/
#define SPI_MODE_1 (0|SPI_CPHA)
#define SPI_MODE_2 (SPI_CPOL|0)
#define SPI_MODE_3 (SPI_CPOL|SPI_CPHA)
.....
}
注意如上定义的 SPI_CPHA和SPI_CPOL
这个和一中的CPHA,以及CPOL是对应的,
然后在次基础上定义了MODE?,到此,你是否能相像出SPI接口的数据传输过程?
下面才是刚刚进入真正的主题
以下是spi_bitbang结构体的定义
点击(此处)折叠或打开
struct spi_bitbang {
struct workqueue_struct
*workqueue;
struct work_struct work;
spinlock_t lock;
struct list_head queue;
u8 busy;
u8 use_dma;
u8 flags;
/* extra spi->mode support
*/
struct spi_master *master;
/* setup_transfer() changes clock
and/or wordsize
to match settings
* for this transfer; zeroes restore defaults from spi_device.
*/
int (*setup_transfer)(struct spi_device
*spi,
struct spi_transfer *t);
void (*chipselect)(struct spi_device
*spi,
int is_on);
#define BITBANG_CS_ACTIVE 1 /* normally nCS, active low
*/
#define BITBANG_CS_INACTIVE 0
/* txrx_bufs() may handle dma mapping
for transfers that don't
* already have one
(transfer.{tx,rx}_dma
is zero),
or use PIO
*/
int
(*txrx_bufs)(struct
spi_device *spi, struct spi_transfer
*t);
/* txrx_word[SPI_MODE_*]()
just looks like a shift register */
u32 (*txrx_word[4])(struct
spi_device *spi,
unsigned nsecs,
u32 word, u8 bits);
};
这里我们只对加底纹部分做以下说明:
struct work_struct work;
这个让我们想入菲菲--bit bang是按照workqueue队列的形式来工作的吗?
worqueue这个是什么?简单地说,他就是一个队列,里面的每一个work节点代表着一个需要调度的工作。
具体可以百度,这个和tasklet有点相似的。以前自己有用过,经常用于中断的顶半部,底半部机制
既然是worqueue,那么我们可以猜想,spi是在每一个work中实现bit bang的。
事实上确实如此,在/driver/spi/spi_bitbang.c#L267中,我们可以看到如下函数
static void bitbang_work(struct work_struct *work){},
在/driver/spi/spi_bitbang.c中,我们可以发现
int spi_bitbang_start(struct spi_bitbang *bitbang);
这里还是有必要看一下这个方法的实现过程,看他都干了些什么:
点击(此处)折叠或打开
int spi_bitbang_start(struct spi_bitbang
*bitbang)
{
int status;
if (!bitbang->master
||
!bitbang->chipselect)
return -EINVAL;
INIT_WORK(&bitbang->work,
bitbang_work);
spin_lock_init(&bitbang->lock);
INIT_LIST_HEAD(&bitbang->queue);
if (!bitbang->master->mode_bits)//此处在s3c24xx_spi_probe方法中已定义,采用s3c24xx_spi_probe的设置
bitbang->master->mode_bits
= SPI_CPOL | SPI_CPHA
| bitbang->flags;
if (!bitbang->master->transfer)//此处在s3c24xx_spi_probe方法中没有定义,所以使用这里设置的方法
bitbang->master->transfer
= spi_bitbang_transfer;//之后发送数据的时候会用到这个接口
if (!bitbang->txrx_bufs)
{//此处在s3c24xx_spi_probe方法中已定义,采用s3c24xx_spi_probe的设置
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;
}
} else
if (!bitbang->master->setup)//此处在s3c24xx_spi_probe方法中已定义,从这里看来bitbang->master->setup必须定义,不可遗漏
return -EINVAL;
/* this task
is the only thing
to touch the SPI bits */
bitbang->busy
= 0;
bitbang->workqueue
= create_singlethread_workqueue(
dev_name(bitbang->master->dev.parent));
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;
}
EXPORT_SYMBOL_GPL(spi_bitbang_start);
看看spi_register_master的实现过程:
点击(此处)折叠或打开
int spi_register_master(struct spi_master
*master)
{
static atomic_t dyn_bus_id = ATOMIC_INIT((1<<15)
- 1);
struct device *dev
= master->dev.parent;
int status
= -ENODEV;
int dynamic
= 0;
if (!dev)
return -ENODEV;
/* even
if it's just one always-selected device, there must
* be at least one chipselect
*/
if (master->num_chipselect
== 0)
return -EINVAL;
/* convention: dynamically assigned bus IDs count down from the max
*/
if (master->bus_num
< 0)
{
/* FIXME switch
to an IDR based scheme, something like
* I2C
now uses, so we can't run out of
"dynamic" IDs
*/
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.
*/
dev_set_name(&master->dev,
"spi%u", master->bus_num);
status = device_add(&master->dev);//添加设备
if (status
< 0)
goto done;
dev_dbg(dev,
"registered master %s%sn", dev_name(&master->dev),
dynamic ?
" (dynamic)" :
"");
/* populate children from any spi device tables
*/
scan_boardinfo(master);
status = 0;
done:
return status;
}
EXPORT_SYMBOL_GPL(spi_register_master);
看看scan_boardinfo原型:
点击(此处)折叠或打开
static void scan_boardinfo(struct spi_master
*master)
{
struct boardinfo *bi;
mutex_lock(&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;
/* NOTE: this relies
on spi_new_device
to
* issue diagnostics when given bogus inputs
*/
(void)
spi_new_device(master,
chip);
}
}
mutex_unlock(&board_lock);
}
点击(此处)折叠或打开
struct spi_device *spi_new_device(struct spi_master
*master,
struct spi_board_info *chip)
{
struct spi_device *proxy;
int status;
/* NOTE: caller did any chip->bus_num checks necessary.
*
* Also, unless we change the return value convention
to use
* error-or-pointer
(not
NULL-or-pointer), troubleshootability
* suggests syslogged diagnostics are best here
(ugh).
*/
proxy = spi_alloc_device(master);
if (!proxy)
return NULL;
WARN_ON(strlen(chip->modalias)
>= sizeof(proxy->modalias));
proxy->chip_select
= chip->chip_select;
proxy->max_speed_hz
= chip->max_speed_hz;
proxy->mode
= chip->mode;
proxy->irq
= chip->irq;
strlcpy(proxy->modalias, chip->modalias,
sizeof(proxy->modalias));
proxy->dev.platform_data
= (void
*) chip->platform_data;
proxy->controller_data
= chip->controller_data;
proxy->controller_state
= NULL;
status = spi_add_device(proxy);
if (status
< 0)
{
spi_dev_put(proxy);
return NULL;
}
return proxy;
}
EXPORT_SYMBOL_GPL(spi_new_device);
点击(此处)折叠或打开
struct spi_device *spi_alloc_device(struct spi_master
*master)
{
struct spi_device *spi;
struct device *dev
= master->dev.parent;
if (!spi_master_get(master))
return NULL;
spi = kzalloc(sizeof
*spi, GFP_KERNEL);
if (!spi)
{
dev_err(dev,
"cannot alloc spi_devicen");
spi_master_put(master);
return NULL;
}
spi->master
= master;
spi->dev.parent
= dev;
spi->dev.bus
= &spi_bus_type;
spi->dev.release
= spidev_release;
device_initialize(&spi->dev);
return spi;
}
EXPORT_SYMBOL_GPL(spi_alloc_device);
点击(此处)折叠或打开
int spi_add_device(struct spi_device
*spi)
{
static DEFINE_MUTEX(spi_add_lock);
struct device *dev
= spi->master->dev.parent;
int status;
/* Chipselects are numbered 0..max; validate.
*/
if (spi->chip_select
>= spi->master->num_chipselect)
{
dev_err(dev,
"cs%d >= max %dn",
spi->chip_select,
spi->master->num_chipselect);
return -EINVAL;
}
/*
Set the bus ID string
*/
dev_set_name(&spi->dev,
"%s.%u", dev_name(&spi->master->dev),
spi->chip_select);
/* We need
to make sure there's no other device with this
* chipselect
**BEFORE** we
call setup(),
else we'll trash
* its configuration. Lock against concurrent add() calls.
*/
mutex_lock(&spi_add_lock);
if (bus_find_device_by_name(&spi_bus_type,
NULL, dev_name(&spi->dev))
!=
NULL)
{
dev_err(dev,
"chipselect %d already in usen",
spi->chip_select);
status =
-EBUSY;
goto done;
}
/* Drivers may modify this initial i/o setup, but will
* normally rely
on the device being setup. Devices
* using SPI_CS_HIGH can't coexist well otherwise...
*/
status = spi_setup(spi);
if (status
< 0)
{
dev_err(dev,
"can't %s %s, status %dn",
"setup", dev_name(&spi->dev),
status);
goto done;
}
/* Device may be bound
to an active driver when this returns
*/
status = device_add(&spi->dev);
if (status
< 0)
dev_err(dev,
"can't %s %s, status %dn",
"add", dev_name(&spi->dev),
status);
else
dev_dbg(dev,
"registered child %sn", dev_name(&spi->dev));
done:
mutex_unlock(&spi_add_lock);
return status;
}
EXPORT_SYMBOL_GPL(spi_add_device);
点击(此处)折叠或打开
int spi_setup(struct spi_device
*spi)
{
unsigned bad_bits;
int status;
/* help drivers fail
*cleanly* when they need options
* that aren't supported with their current master
*/
bad_bits = spi->mode
& ~spi->master->mode_bits;
if (bad_bits)
{
dev_dbg(&spi->dev,
"setup: unsupported mode bits %xn",
bad_bits);
return -EINVAL;
}
if (!spi->bits_per_word)
spi->bits_per_word
= 8;
status = spi->master->setup(spi);//这里其实调用的就是s3c24xx_spi_probe里填充的spi->master->setup
dev_dbg(&spi->dev,
"setup mode %d, %s%s%s%s"
"%u bits/w, %u Hz max --> %dn",
(int)
(spi->mode
& (SPI_CPOL
| SPI_CPHA)),
(spi->mode
& SPI_CS_HIGH)
? "cs_high, "
: "",
(spi->mode
& SPI_LSB_FIRST)
? "lsb, "
: "",
(spi->mode
& SPI_3WIRE)
? "3wire, "
: "",
(spi->mode
& SPI_LOOP)
? "loopback, "
: "",
spi->bits_per_word, spi->max_speed_hz,
status);
return status;
}
EXPORT_SYMBOL_GPL(spi_setup);
点击(此处)折叠或打开
static int s3c24xx_spi_setup(struct spi_device
*spi)
{
struct s3c24xx_spi_devstate *cs
= spi->controller_state;
struct s3c24xx_spi *hw
= to_hw(spi);
int ret;
/* allocate settings
on the first call
*/
if (!cs)
{
cs = kzalloc(sizeof(struct s3c24xx_spi_devstate), GFP_KERNEL);
if (!cs)
{
dev_err(&spi->dev,
"no memory for controller staten");
return -ENOMEM;
}
cs->spcon
= SPCON_DEFAULT;
cs->hz
= -1;
spi->controller_state
= cs;
}
/* initialise the state from the device
*/
ret = s3c24xx_spi_update_state(spi,
NULL);
if (ret)
return ret;
spin_lock(&hw->bitbang.lock);
if (!hw->bitbang.busy)
{
hw->bitbang.chipselect(spi,
BITBANG_CS_INACTIVE);
/* need
to ndelay for 0.5 clocktick
? */
}
spin_unlock(&hw->bitbang.lock);
return 0;
}
好了,至此spi主控制器(驱动)和板上spi设备注册完毕,以后要使用spi来传输数据的话,只要先获得spi设备结构,然后就可以利用它来和spi驱动打交道了(就好像你要操作一个文件,先要获取文件句柄一样,明白吧^_^)
相关文章推荐
- linux spi驱动开发学习(一)-----spi子系统架构
- linux spi驱动开发学习-----spidev.c和spi test app
- linux spi驱动开发学习-----spidev.c和spi test app
- linux&nbsp;spi驱动开发学习(三)--…
- linux&nbsp;spi驱动开发学习(三)--…
- linux spi驱动开发学习(四)-----spi驱动程序完整流程分析
- linux spi驱动开发学习-----spidev.c和spi test app
- Linux驱动开发学习的一些必要步骤
- Linux 驱动开发学习基本步骤
- Linux驱动开发学习的一些必要步骤
- 《Linux内核修炼之道》 之 高效学习Linux驱动开发
- Linux驱动开发学习步骤
- Linux设备驱动开发学习笔录-(init函数和exit函数的书写)
- Linux下spi驱动开发(1)
- socket编程的最简单实例 - linux系统编程及驱动开发 - 小超嵌入式工作室 嵌入式开发学习交流论坛 XC-STC XC2440技术支持 - Powered by Discuz!
- linux驱动开发学习
- 《Linux内核修炼之道》 之 高效学习Linux驱动开发
- 《Linux内核修炼之道》 之 高效学习Linux驱动开发
- 《Linux内核修炼之道》 之 高效学习Linux驱动开发
- 《Linux内核修炼之道》之高效学习linux驱动开发