SPI驱动之主控制器驱动程序
2017-04-17 17:17
423 查看
嵌入式微处理器访问SPI设备有两种方式:使用GPIO模拟SPI接口的工作时序或者使用SPI控制器。使用GPIO模拟SPI接口的工作时序是非常容易实现的,但是会导致大量的时间耗费在模拟SPI接口的时序上,访问效率比较低,容易成为系统瓶颈。这里主要分析使用SPI控制器的情况。
在内核的drivers/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总线。UT4412BV01开发板预留了两路的spi接口(spi0和spi1),对于UT4412BV01开发板而言,使用的是spi_s3c64xx.c,也就是硬件SPI,不是软件SPI。注:下面是基于硬件SPI的spi1分析。
1. 定义platform device
kernel3.0.15/arch/arm/mach-exynos/dev-spi.c
[cpp]
view plain
copy
print?
static struct resource exynos_spi1_resource[] = {
[0] = {
.start = EXYNOS_PA_SPI1,
.end = EXYNOS_PA_SPI1 + 0x100 - 1,
.flags = IORESOURCE_MEM,
},
[1] = {
.start = DMACH_SPI1_TX,
.end = DMACH_SPI1_TX,
.flags = IORESOURCE_DMA,
},
[2] = {
.start = DMACH_SPI1_RX,
.end = DMACH_SPI1_RX,
.flags = IORESOURCE_DMA,
},
[3] = {
.start = IRQ_SPI1,
.end = IRQ_SPI1,
.flags = IORESOURCE_IRQ,
},
};
static struct s3c64xx_spi_info exynos_spi1_pdata = {
.cfg_gpio = exynos_spi_cfg_gpio,
.fifo_lvl_mask = 0x7f,
.rx_lvl_offset = 15,
.high_speed = 1,
.clk_from_cmu = true,
.tx_st_done = 25,
};
struct platform_device exynos_device_spi1 = {
.name = "s3c64xx-spi",
.id = 1,
.num_resources = ARRAY_SIZE(exynos_spi1_resource),
.resource = exynos_spi1_resource,
.dev = {
.dma_mask = &spi_dmamask,
.coherent_dma_mask = DMA_BIT_MASK(32),
.platform_data = &exynos_spi1_pdata,
},
};
2. 定义platform driver
kernel3.0.15/drivers/spi/spi_s3c64xx.c
[cpp]
view plain
copy
print?
static struct platform_driver s3c64xx_spi_driver = {
.driver = {
.name = "s3c64xx-spi",
.owner = THIS_MODULE,
},
.remove = s3c64xx_spi_remove,
.suspend = s3c64xx_spi_suspend,
.resume = s3c64xx_spi_resume,
};
MODULE_ALIAS("platform:s3c64xx-spi");
static int __init s3c64xx_spi_init(void)
{
<span style="white-space: pre;"> </span>//设备不可热插拔,所以使用该函数,而不是platform_driver_register
return platform_driver_probe(&s3c64xx_spi_driver, s3c64xx_spi_probe);
}
subsys_initcall(s3c64xx_spi_init);
static void __exit s3c64xx_spi_exit(void)
{
platform_driver_unregister(&s3c64xx_spi_driver);
}
module_exit(s3c64xx_spi_exit);
MODULE_AUTHOR("Jaswinder Singh <jassi.brar@samsung.com>");
MODULE_DESCRIPTION("S3C64XX SPI Controller Driver");
MODULE_LICENSE("GPL");
3. s3c64xx_spi_probe函数
kernel3.0.15/drivers/spi/spi_s3c64xx.c
当exynos_device_spi1中的name与s3c64xx_spi_driver中的name相同时,也就是是设备名字跟驱动名字可以匹配,s3c64xx_spi_probe驱动探测函数被调用,该函数代码如下所示:
[cpp]
view plain
copy
print?
static int __init s3c64xx_spi_probe(struct platform_device *pdev)
{
struct resource *mem_res, *dmatx_res, *dmarx_res;
struct s3c64xx_spi_driver_data *sdd;
struct s3c64xx_spi_info *sci;
struct spi_master *master;
int ret;
if (pdev->id < 0) { //pdev->id = 1
dev_err(&pdev->dev,
"Invalid platform device id-%d\n", pdev->id);
return -ENODEV;
}
if (pdev->dev.platform_data == NULL) { //pdev->dev.platform_data = &exynos_spi1_pdata
dev_err(&pdev->dev, "platform_data missing!\n");
return -ENODEV;
}
sci = pdev->dev.platform_data;
if (!sci->src_clk_name) { //在板级文件中通过调用s3c64xx_spi_set_info()来初始化
dev_err(&pdev->dev,
"Board init must call s3c64xx_spi_set_info()\n");
return -EINVAL;
}
/* Check for availability of necessary resource */
//获取DMA0资源
dmatx_res = platform_get_resource(pdev, IORESOURCE_DMA, 0);
if (dmatx_res == NULL) {
dev_err(&pdev->dev, "Unable to get SPI-Tx dma resource\n");
return -ENXIO;
}
//获取DMA1资源
dmarx_res = platform_get_resource(pdev, IORESOURCE_DMA, 1);
if (dmarx_res == NULL) {
dev_err(&pdev->dev, "Unable to get SPI-Rx dma resource\n");
return -ENXIO;
}
//获取IO内存资源
mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (mem_res == NULL) {
dev_err(&pdev->dev, "Unable to get SPI MEM resource\n");
return -ENXIO;
}
/**
* 通过跟踪spi_alloc_master相关源码可知,
* 此处分配struct spi_master + struct s3c64xx_spi_driver_data大小的数据,
* 把s3c64xx_spi_driver_data设为spi_master的私有数据
*/
master = spi_alloc_master(&pdev->dev,
sizeof(struct s3c64xx_spi_driver_data));
if (master == NULL) {
dev_err(&pdev->dev, "Unable to allocate SPI Master\n");
return -ENOMEM;
}
/**
* platform_set_drvdata 和 platform_get_drvdata
* probe函数中定义的局部变量,如果我想在其他地方使用它怎么办呢?
* 这就需要把它保存起来。内核提供了这个方法,
* 使用函数platform_set_drvdata()可以将master保存成平台总线设备的私有数据。
* 以后再要使用它时只需调用platform_get_drvdata()就可以了。
*/
platform_set_drvdata(pdev, master);
//从master中获得s3c64xx_spi_driver_data,并初始化相关成员
sdd = spi_master_get_devdata(master);
sdd->master = master;
sdd->cntrlr_info = sci;
sdd->pdev = pdev;
sdd->sfr_start = mem_res->start;
sdd->tx_dmach = dmatx_res->start;
sdd->rx_dmach = dmarx_res->start;
sdd->cur_bpw = 8;
//master相关成员的初始化
master->bus_num = pdev->id; //总线号
master->setup = s3c64xx_spi_setup;
master->transfer = s3c64xx_spi_transfer;
master->num_chipselect = sci->num_cs; //该总线上的设备数
master->dma_alignment = 8;
/* the spi->mode bits understood by this driver: */
master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH; //mode_3
//申请IO内存
if (request_mem_region(mem_res->start,
resource_size(mem_res), pdev->name) == NULL) {
dev_err(&pdev->dev, "Req mem region failed\n");
ret = -ENXIO;
goto err0;
}
//建立映射
sdd->regs = ioremap(mem_res->start, resource_size(mem_res));
if (sdd->regs == NULL) {
dev_err(&pdev->dev, "Unable to remap IO\n");
ret = -ENXIO;
goto err1;
}
//SPI的IO管脚配置,将相应的IO管脚设置为SPI功能
if (sci->cfg_gpio == NULL || sci->cfg_gpio(pdev)) {
dev_err(&pdev->dev, "Unable to config gpio\n");
ret = -EBUSY;
goto err2;
}
//使能时钟
sdd->clk = clk_get(&pdev->dev, "spi");
if (IS_ERR(sdd->clk)) {
dev_err(&pdev->dev, "Unable to acquire clock 'spi'\n");
ret = PTR_ERR(sdd->clk);
goto err3;
}
if (clk_enable(sdd->clk)) {
dev_err(&pdev->dev, "Couldn't enable clock 'spi'\n");
ret = -EBUSY;
goto err4;
}
sdd->src_clk = clk_get(&pdev->dev, sci->src_clk_name);
if (IS_ERR(sdd->src_clk)) {
dev_err(&pdev->dev,
"Unable to acquire clock '%s'\n", sci->src_clk_name);
ret = PTR_ERR(sdd->src_clk);
goto err5;
}
if (clk_enable(sdd->src_clk)) {
dev_err(&pdev->dev, "Couldn't enable clock '%s'\n",
sci->src_clk_name);
ret = -EBUSY;
goto err6;
}
//创建单个线程的工作队列,用于数据收发操作
sdd->workqueue = create_singlethread_workqueue(
dev_name(master->dev.parent));
if (sdd->workqueue == NULL) {
dev_err(&pdev->dev, "Unable to create workqueue\n");
ret = -ENOMEM;
goto err7;
}
//硬件初始化,初始化设置寄存器,包括对SPIMOSI、SPIMISO、SPICLK引脚的设置
s3c64xx_spi_hwinit(sdd, pdev->id);
//锁、工作队列等初始化
spin_lock_init(&sdd->lock);
init_completion(&sdd->xfer_completion);
INIT_WORK(&sdd->work, s3c64xx_spi_work);
INIT_LIST_HEAD(&sdd->queue);
if (spi_register_master(master)) {
dev_err(&pdev->dev, "cannot register SPI master\n");
ret = -EBUSY;
goto err8;
}
dev_dbg(&pdev->dev, "Samsung SoC SPI Driver loaded for Bus SPI-%d "
"with %d Slaves attached\n",
pdev->id, master->num_chipselect);
dev_dbg(&pdev->dev, "\tIOmem=[0x%x-0x%x]\tDMA=[Rx-%d, Tx-%d]\n",
mem_res->end, mem_res->start,
sdd->rx_dmach, sdd->tx_dmach);
return 0;
err8:
destroy_workqueue(sdd->workqueue);
err7:
clk_disable(sdd->src_clk);
err6:
clk_put(sdd->src_clk);
err5:
clk_disable(sdd->clk);
err4:
clk_put(sdd->clk);
err3:
err2:
iounmap((void *) sdd->regs);
err1:
release_mem_region(mem_res->start, resource_size(mem_res));
err0:
platform_set_drvdata(pdev, NULL);
spi_master_put(master);
return ret;
}
spi_alloc_master(kernel3.0.15/drivers/spi/spi.c)
spi_alloc_master函数用于请求分配一个spi_master。
[cpp]
view plain
copy
print?
struct spi_master *spi_alloc_master(struct device *dev, unsigned size)
{
struct spi_master *master;
if (!dev)
return NULL;
/* 分配内存,分配的内存大小是*master + size,包含了两部分内存 */
master = kzalloc(size + sizeof *master, GFP_KERNEL);
if (!master)
return NULL;
device_initialize(&master->dev); //设备模型中的初始设备函数
master->dev.class = &spi_master_class; //spi_master_class在SPI子系统初始化的时候就已经注册好了
master->dev.parent = get_device(dev); //设备当前设备的父设备,这与设备模型相关
spi_master_set_devdata(master, &master[1]); //&master[1]就是master之后的另一部分内存的起始地址
return master;
}
spi_master_register(kernel3.0.15/drivers/spi/spi.c)
spi_master_register函数用于向内核注册一个spi_master。
[cpp]
view plain
copy
print?
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;
struct boardinfo *bi;
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) //一个SPI控制器至少有一个片选,因此片选数为0则出错
return -EINVAL;
/* convention: dynamically assigned bus IDs count down from the max */
if (master->bus_num < 0) { //如果总线号小于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;
}
spin_lock_init(&master->bus_lock_spinlock);
mutex_init(&master->bus_lock_mutex);
master->bus_lock_flag = 0;
/* 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); //把master加入到设备模型中
status = device_add(&master->dev);
if (status < 0)
goto done;
dev_dbg(dev, "registered master %s%s\n", dev_name(&master->dev),
dynamic ? " (dynamic)" : "");
mutex_lock(&board_lock);
list_add_tail(&master->list, &spi_master_list);
list_for_each_entry(bi, &board_list, list) //遍历board_list这个链表
spi_match_master_to_boardinfo(master, &bi->board_info);
mutex_unlock(&board_lock);
status = 0;
/* Register devices from the device tree */
of_register_spi_devices(master);
done:
return status;
}
spi_match_master_to_boardinfo(kernel3.0.15/drivers/spi/spi.c)
[cpp]
view plain
copy
print?
static void spi_match_master_to_boardinfo(struct spi_master *master,
struct spi_board_info *bi)
{
struct spi_device *dev;
if (master->bus_num != bi->bus_num) //每找到一个成员就将它的总线号与master的总线号进行比较,如果相等则调用spi_new_device函数创建一个spi设备
return;
dev = spi_new_device(master, bi);
if (!dev)
dev_err(master->dev.parent, "can't create new device for %s\n",
bi->modalias);
}
spi_new_device(kernel3.0.15/drivers/spi/spi.c)
[cpp]
view plain
copy
print?
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;
}
[cpp]
view plain
copy
print?
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_device\n");
spi_master_put(master);
return NULL;
}
spi->master = master;
spi->dev.parent = dev;
spi->dev.bus = &spi_bus_type; //该spi设备属于SPI子系统初始化时注册的叫“spi”的总线
spi->dev.release = spidev_release;
device_initialize(&spi->dev); //设备模型方面的初始化
return spi;
}
[cpp]
view plain
copy
print?
int spi_add_device(struct spi_device *spi)
{
static DEFINE_MUTEX(spi_add_lock);
struct device *dev = spi->master->dev.parent;
struct device *d;
int status;
/* Chipselects are numbered 0..max; validate. */
if (spi->chip_select >= spi->master->num_chipselect) { //片选号是从0开始的,如果大于或者等于片选数的话则返回出错
dev_err(dev, "cs%d >= max %d\n",
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);
d = bus_find_device_by_name(&spi_bus_type, NULL, dev_name(&spi->dev)); //遍历spi总线,看是否已经注册过该设备
if (d != NULL) {
dev_err(dev, "chipselect %d already in use\n",
spi->chip_select);
put_device(d);
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 setup %s, status %d\n",
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 add %s, status %d\n",
dev_name(&spi->dev), status);
else
dev_dbg(dev, "registered child %s\n", dev_name(&spi->dev));
done:
mutex_unlock(&spi_add_lock);
return status;
}
[cpp]
view plain
copy
print?
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_err(&spi->dev, "setup: unsupported mode bits %x\n",
bad_bits);
return -EINVAL;
}
if (!spi->bits_per_word)
spi->bits_per_word = 8;
status = spi->master->setup(spi); //调用控制器驱动里的s3c64xx_spi_setup函数
dev_dbg(&spi->dev, "setup mode %d, %s%s%s%s"
"%u bits/w, %u Hz max --> %d\n",
(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;
}
s3c64xx_spi_setup(kernel3.0.15/drivers/spi/spi_s3c64xx.c)
[cpp]
view plain
copy
print?
static int s3c64xx_spi_setup(struct spi_device *spi)
{
//由此可知在实例化struct spi_board_info时,其controller_data成员就应该指向struct s3c64xx_spi_csinfo的对象
struct s3c64xx_spi_csinfo *cs = spi->controller_data;
struct s3c64xx_spi_driver_data *sdd;
struct s3c64xx_spi_info *sci;
struct spi_message *msg;
unsigned long flags;
int err = 0;
if (cs == NULL || cs->set_level == NULL) {
dev_err(&spi->dev, "No CS for SPI(%d)\n", spi->chip_select);
return -ENODEV;
}
sdd = spi_master_get_devdata(spi->master);
sci = sdd->cntrlr_info;
spin_lock_irqsave(&sdd->lock, flags);
list_for_each_entry(msg, &sdd->queue, queue) {
/* Is some mssg is already queued for this device */
if (msg->spi == spi) {
dev_err(&spi->dev,
"setup: attempt while mssg in queue!\n");
spin_unlock_irqrestore(&sdd->lock, flags);
return -EBUSY;
}
}
if (sdd->state & SUSPND) {
spin_unlock_irqrestore(&sdd->lock, flags);
dev_err(&spi->dev,
"setup: SPI-%d not active!\n", spi->master->bus_num);
return -ESHUTDOWN;
}
spin_unlock_irqrestore(&sdd->lock, flags);
if (spi->bits_per_word != 8
&& spi->bits_per_word != 16
&& spi->bits_per_word != 32) {
dev_err(&spi->dev, "setup: %dbits/wrd not supported!\n",
spi->bits_per_word);
err = -EINVAL;
goto setup_exit;
}
/* Check if we can provide the requested rate */
if (!sci->clk_from_cmu) {
u32 psr, speed;
/* Max possible */
speed = clk_get_rate(sdd->src_clk) / 2 / (0 + 1);
if (spi->max_speed_hz > speed)
spi->max_speed_hz = speed;
psr = clk_get_rate(sdd->src_clk) / 2 / spi->max_speed_hz - 1;
psr &= S3C64XX_SPI_PSR_MASK;
if (psr == S3C64XX_SPI_PSR_MASK)
psr--;
speed = clk_get_rate(sdd->src_clk) / 2 / (psr + 1);
if (spi->max_speed_hz < speed) {
if (psr+1 < S3C64XX_SPI_PSR_MASK) {
psr++;
} else {
err = -EINVAL;
goto setup_exit;
}
}
speed = clk_get_rate(sdd->src_clk) / 2 / (psr + 1);
if (spi->max_speed_hz >= speed)
spi->max_speed_hz = speed;
else
err = -EINVAL;
}
setup_exit:
/* setup() returns with device de-selected */
disable_cs(sdd, spi);
return err;
}
嵌入式微处理器访问SPI设备有两种方式:使用GPIO模拟SPI接口的工作时序或者使用SPI控制器。使用GPIO模拟SPI接口的工作时序是非常容易实现的,但是会导致大量的时间耗费在模拟SPI接口的时序上,访问效率比较低,容易成为系统瓶颈。这里主要分析使用SPI控制器的情况。
在内核的drivers/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总线。UT4412BV01开发板预留了两路的spi接口(spi0和spi1),对于UT4412BV01开发板而言,使用的是spi_s3c64xx.c,也就是硬件SPI,不是软件SPI。注:下面是基于硬件SPI的spi1分析。
1. 定义platform device
kernel3.0.15/arch/arm/mach-exynos/dev-spi.c
[cpp]
view plain
copy
print?
static struct resource exynos_spi1_resource[] = {
[0] = {
.start = EXYNOS_PA_SPI1,
.end = EXYNOS_PA_SPI1 + 0x100 - 1,
.flags = IORESOURCE_MEM,
},
[1] = {
.start = DMACH_SPI1_TX,
.end = DMACH_SPI1_TX,
.flags = IORESOURCE_DMA,
},
[2] = {
.start = DMACH_SPI1_RX,
.end = DMACH_SPI1_RX,
.flags = IORESOURCE_DMA,
},
[3] = {
.start = IRQ_SPI1,
.end = IRQ_SPI1,
.flags = IORESOURCE_IRQ,
},
};
static struct s3c64xx_spi_info exynos_spi1_pdata = {
.cfg_gpio = exynos_spi_cfg_gpio,
.fifo_lvl_mask = 0x7f,
.rx_lvl_offset = 15,
.high_speed = 1,
.clk_from_cmu = true,
.tx_st_done = 25,
};
struct platform_device exynos_device_spi1 = {
.name = "s3c64xx-spi",
.id = 1,
.num_resources = ARRAY_SIZE(exynos_spi1_resource),
.resource = exynos_spi1_resource,
.dev = {
.dma_mask = &spi_dmamask,
.coherent_dma_mask = DMA_BIT_MASK(32),
.platform_data = &exynos_spi1_pdata,
},
};
static struct resource exynos_spi1_resource[] = { [0] = { .start = EXYNOS_PA_SPI1, .end = EXYNOS_PA_SPI1 + 0x100 - 1, .flags = IORESOURCE_MEM, }, [1] = { .start = DMACH_SPI1_TX, .end = DMACH_SPI1_TX, .flags = IORESOURCE_DMA, }, [2] = { .start = DMACH_SPI1_RX, .end = DMACH_SPI1_RX, .flags = IORESOURCE_DMA, }, [3] = { .start = IRQ_SPI1, .end = IRQ_SPI1, .flags = IORESOURCE_IRQ, }, }; static struct s3c64xx_spi_info exynos_spi1_pdata = { .cfg_gpio = exynos_spi_cfg_gpio, .fifo_lvl_mask = 0x7f, .rx_lvl_offset = 15, .high_speed = 1, .clk_from_cmu = true, .tx_st_done = 25, }; struct platform_device exynos_device_spi1 = { .name = "s3c64xx-spi", .id = 1, .num_resources = ARRAY_SIZE(exynos_spi1_resource), .resource = exynos_spi1_resource, .dev = { .dma_mask = &spi_dmamask, .coherent_dma_mask = DMA_BIT_MASK(32), .platform_data = &exynos_spi1_pdata, }, };exynos4412总共定义了三个spi控制器平台设备,实际上UT4412BV01开发板只预留了两个spi控制器(spi0和spi1)。platform设备给出了spi1接口的寄存器地址资源及IRQ资源。注意其设备名为s3c64xx-spi。
2. 定义platform driver
kernel3.0.15/drivers/spi/spi_s3c64xx.c
[cpp]
view plain
copy
print?
static struct platform_driver s3c64xx_spi_driver = {
.driver = {
.name = "s3c64xx-spi",
.owner = THIS_MODULE,
},
.remove = s3c64xx_spi_remove,
.suspend = s3c64xx_spi_suspend,
.resume = s3c64xx_spi_resume,
};
MODULE_ALIAS("platform:s3c64xx-spi");
static int __init s3c64xx_spi_init(void)
{
<span style="white-space: pre;"> </span>//设备不可热插拔,所以使用该函数,而不是platform_driver_register
return platform_driver_probe(&s3c64xx_spi_driver, s3c64xx_spi_probe);
}
subsys_initcall(s3c64xx_spi_init);
static void __exit s3c64xx_spi_exit(void)
{
platform_driver_unregister(&s3c64xx_spi_driver);
}
module_exit(s3c64xx_spi_exit);
MODULE_AUTHOR("Jaswinder Singh <jassi.brar@samsung.com>");
MODULE_DESCRIPTION("S3C64XX SPI Controller Driver");
MODULE_LICENSE("GPL");
static struct platform_driver s3c64xx_spi_driver = { .driver = { .name = "s3c64xx-spi", .owner = THIS_MODULE, }, .remove = s3c64xx_spi_remove, .suspend = s3c64xx_spi_suspend, .resume = s3c64xx_spi_resume, }; MODULE_ALIAS("platform:s3c64xx-spi"); static int __init s3c64xx_spi_init(void) { //设备不可热插拔,所以使用该函数,而不是platform_driver_register return platform_driver_probe(&s3c64xx_spi_driver, s3c64xx_spi_probe); } subsys_initcall(s3c64xx_spi_init); static void __exit s3c64xx_spi_exit(void) { platform_driver_unregister(&s3c64xx_spi_driver); } module_exit(s3c64xx_spi_exit); MODULE_AUTHOR("Jaswinder Singh <jassi.brar@samsung.com>"); MODULE_DESCRIPTION("S3C64XX SPI Controller Driver"); MODULE_LICENSE("GPL");调用了platform_driver_probe注册platform驱动,注册完成以后将会调用platform的s3c64xx_spi_probe函数。注意:platform驱动的name和platform device的name是相同的。
3. s3c64xx_spi_probe函数
kernel3.0.15/drivers/spi/spi_s3c64xx.c
当exynos_device_spi1中的name与s3c64xx_spi_driver中的name相同时,也就是是设备名字跟驱动名字可以匹配,s3c64xx_spi_probe驱动探测函数被调用,该函数代码如下所示:
[cpp]
view plain
copy
print?
static int __init s3c64xx_spi_probe(struct platform_device *pdev)
{
struct resource *mem_res, *dmatx_res, *dmarx_res;
struct s3c64xx_spi_driver_data *sdd;
struct s3c64xx_spi_info *sci;
struct spi_master *master;
int ret;
if (pdev->id < 0) { //pdev->id = 1
dev_err(&pdev->dev,
"Invalid platform device id-%d\n", pdev->id);
return -ENODEV;
}
if (pdev->dev.platform_data == NULL) { //pdev->dev.platform_data = &exynos_spi1_pdata
dev_err(&pdev->dev, "platform_data missing!\n");
return -ENODEV;
}
sci = pdev->dev.platform_data;
if (!sci->src_clk_name) { //在板级文件中通过调用s3c64xx_spi_set_info()来初始化
dev_err(&pdev->dev,
"Board init must call s3c64xx_spi_set_info()\n");
return -EINVAL;
}
/* Check for availability of necessary resource */
//获取DMA0资源
dmatx_res = platform_get_resource(pdev, IORESOURCE_DMA, 0);
if (dmatx_res == NULL) {
dev_err(&pdev->dev, "Unable to get SPI-Tx dma resource\n");
return -ENXIO;
}
//获取DMA1资源
dmarx_res = platform_get_resource(pdev, IORESOURCE_DMA, 1);
if (dmarx_res == NULL) {
dev_err(&pdev->dev, "Unable to get SPI-Rx dma resource\n");
return -ENXIO;
}
//获取IO内存资源
mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (mem_res == NULL) {
dev_err(&pdev->dev, "Unable to get SPI MEM resource\n");
return -ENXIO;
}
/**
* 通过跟踪spi_alloc_master相关源码可知,
* 此处分配struct spi_master + struct s3c64xx_spi_driver_data大小的数据,
* 把s3c64xx_spi_driver_data设为spi_master的私有数据
*/
master = spi_alloc_master(&pdev->dev,
sizeof(struct s3c64xx_spi_driver_data));
if (master == NULL) {
dev_err(&pdev->dev, "Unable to allocate SPI Master\n");
return -ENOMEM;
}
/**
* platform_set_drvdata 和 platform_get_drvdata
* probe函数中定义的局部变量,如果我想在其他地方使用它怎么办呢?
* 这就需要把它保存起来。内核提供了这个方法,
* 使用函数platform_set_drvdata()可以将master保存成平台总线设备的私有数据。
* 以后再要使用它时只需调用platform_get_drvdata()就可以了。
*/
platform_set_drvdata(pdev, master);
//从master中获得s3c64xx_spi_driver_data,并初始化相关成员
sdd = spi_master_get_devdata(master);
sdd->master = master;
sdd->cntrlr_info = sci;
sdd->pdev = pdev;
sdd->sfr_start = mem_res->start;
sdd->tx_dmach = dmatx_res->start;
sdd->rx_dmach = dmarx_res->start;
sdd->cur_bpw = 8;
//master相关成员的初始化
master->bus_num = pdev->id; //总线号
master->setup = s3c64xx_spi_setup;
master->transfer = s3c64xx_spi_transfer;
master->num_chipselect = sci->num_cs; //该总线上的设备数
master->dma_alignment = 8;
/* the spi->mode bits understood by this driver: */
master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH; //mode_3
//申请IO内存
if (request_mem_region(mem_res->start,
resource_size(mem_res), pdev->name) == NULL) {
dev_err(&pdev->dev, "Req mem region failed\n");
ret = -ENXIO;
goto err0;
}
//建立映射
sdd->regs = ioremap(mem_res->start, resource_size(mem_res));
if (sdd->regs == NULL) {
dev_err(&pdev->dev, "Unable to remap IO\n");
ret = -ENXIO;
goto err1;
}
//SPI的IO管脚配置,将相应的IO管脚设置为SPI功能
if (sci->cfg_gpio == NULL || sci->cfg_gpio(pdev)) {
dev_err(&pdev->dev, "Unable to config gpio\n");
ret = -EBUSY;
goto err2;
}
//使能时钟
sdd->clk = clk_get(&pdev->dev, "spi");
if (IS_ERR(sdd->clk)) {
dev_err(&pdev->dev, "Unable to acquire clock 'spi'\n");
ret = PTR_ERR(sdd->clk);
goto err3;
}
if (clk_enable(sdd->clk)) {
dev_err(&pdev->dev, "Couldn't enable clock 'spi'\n");
ret = -EBUSY;
goto err4;
}
sdd->src_clk = clk_get(&pdev->dev, sci->src_clk_name);
if (IS_ERR(sdd->src_clk)) {
dev_err(&pdev->dev,
"Unable to acquire clock '%s'\n", sci->src_clk_name);
ret = PTR_ERR(sdd->src_clk);
goto err5;
}
if (clk_enable(sdd->src_clk)) {
dev_err(&pdev->dev, "Couldn't enable clock '%s'\n",
sci->src_clk_name);
ret = -EBUSY;
goto err6;
}
//创建单个线程的工作队列,用于数据收发操作
sdd->workqueue = create_singlethread_workqueue(
dev_name(master->dev.parent));
if (sdd->workqueue == NULL) {
dev_err(&pdev->dev, "Unable to create workqueue\n");
ret = -ENOMEM;
goto err7;
}
//硬件初始化,初始化设置寄存器,包括对SPIMOSI、SPIMISO、SPICLK引脚的设置
s3c64xx_spi_hwinit(sdd, pdev->id);
//锁、工作队列等初始化
spin_lock_init(&sdd->lock);
init_completion(&sdd->xfer_completion);
INIT_WORK(&sdd->work, s3c64xx_spi_work);
INIT_LIST_HEAD(&sdd->queue);
if (spi_register_master(master)) {
dev_err(&pdev->dev, "cannot register SPI master\n");
ret = -EBUSY;
goto err8;
}
dev_dbg(&pdev->dev, "Samsung SoC SPI Driver loaded for Bus SPI-%d "
"with %d Slaves attached\n",
pdev->id, master->num_chipselect);
dev_dbg(&pdev->dev, "\tIOmem=[0x%x-0x%x]\tDMA=[Rx-%d, Tx-%d]\n",
mem_res->end, mem_res->start,
sdd->rx_dmach, sdd->tx_dmach);
return 0;
err8:
destroy_workqueue(sdd->workqueue);
err7:
clk_disable(sdd->src_clk);
err6:
clk_put(sdd->src_clk);
err5:
clk_disable(sdd->clk);
err4:
clk_put(sdd->clk);
err3:
err2:
iounmap((void *) sdd->regs);
err1:
release_mem_region(mem_res->start, resource_size(mem_res));
err0:
platform_set_drvdata(pdev, NULL);
spi_master_put(master);
return ret;
}
static int __init s3c64xx_spi_probe(struct platform_device *pdev) { struct resource *mem_res, *dmatx_res, *dmarx_res; struct s3c64xx_spi_driver_data *sdd; struct s3c64xx_spi_info *sci; struct spi_master *master; int ret; if (pdev->id < 0) { //pdev->id = 1 dev_err(&pdev->dev, "Invalid platform device id-%d\n", pdev->id); return -ENODEV; } if (pdev->dev.platform_data == NULL) { //pdev->dev.platform_data = &exynos_spi1_pdata dev_err(&pdev->dev, "platform_data missing!\n"); return -ENODEV; } sci = pdev->dev.platform_data; if (!sci->src_clk_name) { //在板级文件中通过调用s3c64xx_spi_set_info()来初始化 dev_err(&pdev->dev, "Board init must call s3c64xx_spi_set_info()\n"); return -EINVAL; } /* Check for availability of necessary resource */ //获取DMA0资源 dmatx_res = platform_get_resource(pdev, IORESOURCE_DMA, 0); if (dmatx_res == NULL) { dev_err(&pdev->dev, "Unable to get SPI-Tx dma resource\n"); return -ENXIO; } //获取DMA1资源 dmarx_res = platform_get_resource(pdev, IORESOURCE_DMA, 1); if (dmarx_res == NULL) { dev_err(&pdev->dev, "Unable to get SPI-Rx dma resource\n"); return -ENXIO; } //获取IO内存资源 mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (mem_res == NULL) { dev_err(&pdev->dev, "Unable to get SPI MEM resource\n"); return -ENXIO; } /** * 通过跟踪spi_alloc_master相关源码可知, * 此处分配struct spi_master + struct s3c64xx_spi_driver_data大小的数据, * 把s3c64xx_spi_driver_data设为spi_master的私有数据 */ master = spi_alloc_master(&pdev->dev, sizeof(struct s3c64xx_spi_driver_data)); if (master == NULL) { dev_err(&pdev->dev, "Unable to allocate SPI Master\n"); return -ENOMEM; } /** * platform_set_drvdata 和 platform_get_drvdata * probe函数中定义的局部变量,如果我想在其他地方使用它怎么办呢? * 这就需要把它保存起来。内核提供了这个方法, * 使用函数platform_set_drvdata()可以将master保存成平台总线设备的私有数据。 * 以后再要使用它时只需调用platform_get_drvdata()就可以了。 */ platform_set_drvdata(pdev, master); //从master中获得s3c64xx_spi_driver_data,并初始化相关成员 sdd = spi_master_get_devdata(master); sdd->master = master; sdd->cntrlr_info = sci; sdd->pdev = pdev; sdd->sfr_start = mem_res->start; sdd->tx_dmach = dmatx_res->start; sdd->rx_dmach = dmarx_res->start; sdd->cur_bpw = 8; //master相关成员的初始化 master->bus_num = pdev->id; //总线号 master->setup = s3c64xx_spi_setup; master->transfer = s3c64xx_spi_transfer; master->num_chipselect = sci->num_cs; //该总线上的设备数 master->dma_alignment = 8; /* the spi->mode bits understood by this driver: */ master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH; //mode_3 //申请IO内存 if (request_mem_region(mem_res->start, resource_size(mem_res), pdev->name) == NULL) { dev_err(&pdev->dev, "Req mem region failed\n"); ret = -ENXIO; goto err0; } //建立映射 sdd->regs = ioremap(mem_res->start, resource_size(mem_res)); if (sdd->regs == NULL) { dev_err(&pdev->dev, "Unable to remap IO\n"); ret = -ENXIO; goto err1; } //SPI的IO管脚配置,将相应的IO管脚设置为SPI功能 if (sci->cfg_gpio == NULL || sci->cfg_gpio(pdev)) { dev_err(&pdev->dev, "Unable to config gpio\n"); ret = -EBUSY; goto err2; } //使能时钟 sdd->clk = clk_get(&pdev->dev, "spi"); if (IS_ERR(sdd->clk)) { dev_err(&pdev->dev, "Unable to acquire clock 'spi'\n"); ret = PTR_ERR(sdd->clk); goto err3; } if (clk_enable(sdd->clk)) { dev_err(&pdev->dev, "Couldn't enable clock 'spi'\n"); ret = -EBUSY; goto err4; } sdd->src_clk = clk_get(&pdev->dev, sci->src_clk_name); if (IS_ERR(sdd->src_clk)) { dev_err(&pdev->dev, "Unable to acquire clock '%s'\n", sci->src_clk_name); ret = PTR_ERR(sdd->src_clk); goto err5; } if (clk_enable(sdd->src_clk)) { dev_err(&pdev->dev, "Couldn't enable clock '%s'\n", sci->src_clk_name); ret = -EBUSY; goto err6; } //创建单个线程的工作队列,用于数据收发操作 sdd->workqueue = create_singlethread_workqueue( dev_name(master->dev.parent)); if (sdd->workqueue == NULL) { dev_err(&pdev->dev, "Unable to create workqueue\n"); ret = -ENOMEM; goto err7; } //硬件初始化,初始化设置寄存器,包括对SPIMOSI、SPIMISO、SPICLK引脚的设置 s3c64xx_spi_hwinit(sdd, pdev->id); //锁、工作队列等初始化 spin_lock_init(&sdd->lock); init_completion(&sdd->xfer_completion); INIT_WORK(&sdd->work, s3c64xx_spi_work); INIT_LIST_HEAD(&sdd->queue); if (spi_register_master(master)) { dev_err(&pdev->dev, "cannot register SPI master\n"); ret = -EBUSY; goto err8; } dev_dbg(&pdev->dev, "Samsung SoC SPI Driver loaded for Bus SPI-%d " "with %d Slaves attached\n", pdev->id, master->num_chipselect); dev_dbg(&pdev->dev, "\tIOmem=[0x%x-0x%x]\tDMA=[Rx-%d, Tx-%d]\n", mem_res->end, mem_res->start, sdd->rx_dmach, sdd->tx_dmach); return 0; err8: destroy_workqueue(sdd->workqueue); err7: clk_disable(sdd->src_clk); err6: clk_put(sdd->src_clk); err5: clk_disable(sdd->clk); err4: clk_put(sdd->clk); err3: err2: iounmap((void *) sdd->regs); err1: release_mem_region(mem_res->start, resource_size(mem_res)); err0: platform_set_drvdata(pdev, NULL); spi_master_put(master); return ret; }s3c64xx_spi_probe函数很长,但做的事情却很简单,从上面代码的注释可以基本理清整个探测流程。其中用到几个比较重要的函数,下面来一一解释。
spi_alloc_master(kernel3.0.15/drivers/spi/spi.c)
spi_alloc_master函数用于请求分配一个spi_master。
[cpp]
view plain
copy
print?
struct spi_master *spi_alloc_master(struct device *dev, unsigned size)
{
struct spi_master *master;
if (!dev)
return NULL;
/* 分配内存,分配的内存大小是*master + size,包含了两部分内存 */
master = kzalloc(size + sizeof *master, GFP_KERNEL);
if (!master)
return NULL;
device_initialize(&master->dev); //设备模型中的初始设备函数
master->dev.class = &spi_master_class; //spi_master_class在SPI子系统初始化的时候就已经注册好了
master->dev.parent = get_device(dev); //设备当前设备的父设备,这与设备模型相关
spi_master_set_devdata(master, &master[1]); //&master[1]就是master之后的另一部分内存的起始地址
return master;
}
struct spi_master *spi_alloc_master(struct device *dev, unsigned size) { struct spi_master *master; if (!dev) return NULL; /* 分配内存,分配的内存大小是*master + size,包含了两部分内存 */ master = kzalloc(size + sizeof *master, GFP_KERNEL); if (!master) return NULL; device_initialize(&master->dev); //设备模型中的初始设备函数 master->dev.class = &spi_master_class; //spi_master_class在SPI子系统初始化的时候就已经注册好了 master->dev.parent = get_device(dev); //设备当前设备的父设备,这与设备模型相关 spi_master_set_devdata(master, &master[1]); //&master[1]就是master之后的另一部分内存的起始地址 return master; }
spi_master_register(kernel3.0.15/drivers/spi/spi.c)
spi_master_register函数用于向内核注册一个spi_master。
[cpp]
view plain
copy
print?
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;
struct boardinfo *bi;
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) //一个SPI控制器至少有一个片选,因此片选数为0则出错
return -EINVAL;
/* convention: dynamically assigned bus IDs count down from the max */
if (master->bus_num < 0) { //如果总线号小于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;
}
spin_lock_init(&master->bus_lock_spinlock);
mutex_init(&master->bus_lock_mutex);
master->bus_lock_flag = 0;
/* 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); //把master加入到设备模型中
status = device_add(&master->dev);
if (status < 0)
goto done;
dev_dbg(dev, "registered master %s%s\n", dev_name(&master->dev),
dynamic ? " (dynamic)" : "");
mutex_lock(&board_lock);
list_add_tail(&master->list, &spi_master_list);
list_for_each_entry(bi, &board_list, list) //遍历board_list这个链表
spi_match_master_to_boardinfo(master, &bi->board_info);
mutex_unlock(&board_lock);
status = 0;
/* Register devices from the device tree */
of_register_spi_devices(master);
done:
return status;
}
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; struct boardinfo *bi; 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) //一个SPI控制器至少有一个片选,因此片选数为0则出错 return -EINVAL; /* convention: dynamically assigned bus IDs count down from the max */ if (master->bus_num < 0) { //如果总线号小于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; } spin_lock_init(&master->bus_lock_spinlock); mutex_init(&master->bus_lock_mutex); master->bus_lock_flag = 0; /* 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); //把master加入到设备模型中 status = device_add(&master->dev); if (status < 0) goto done; dev_dbg(dev, "registered master %s%s\n", dev_name(&master->dev), dynamic ? " (dynamic)" : ""); mutex_lock(&board_lock); list_add_tail(&master->list, &spi_master_list); list_for_each_entry(bi, &board_list, list) //遍历board_list这个链表 spi_match_master_to_boardinfo(master, &bi->board_info); mutex_unlock(&board_lock); status = 0; /* Register devices from the device tree */ of_register_spi_devices(master); done: return status; }
spi_match_master_to_boardinfo(kernel3.0.15/drivers/spi/spi.c)
[cpp]
view plain
copy
print?
static void spi_match_master_to_boardinfo(struct spi_master *master,
struct spi_board_info *bi)
{
struct spi_device *dev;
if (master->bus_num != bi->bus_num) //每找到一个成员就将它的总线号与master的总线号进行比较,如果相等则调用spi_new_device函数创建一个spi设备
return;
dev = spi_new_device(master, bi);
if (!dev)
dev_err(master->dev.parent, "can't create new device for %s\n",
bi->modalias);
}
static void spi_match_master_to_boardinfo(struct spi_master *master, struct spi_board_info *bi) { struct spi_device *dev; if (master->bus_num != bi->bus_num) //每找到一个成员就将它的总线号与master的总线号进行比较,如果相等则调用spi_new_device函数创建一个spi设备 return; dev = spi_new_device(master, bi); if (!dev) dev_err(master->dev.parent, "can't create new device for %s\n", bi->modalias); }
spi_new_device(kernel3.0.15/drivers/spi/spi.c)
[cpp]
view plain
copy
print?
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;
}
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; }spi_alloc_device(kernel3.0.15/drivers/spi/spi.c)
[cpp]
view plain
copy
print?
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_device\n");
spi_master_put(master);
return NULL;
}
spi->master = master;
spi->dev.parent = dev;
spi->dev.bus = &spi_bus_type; //该spi设备属于SPI子系统初始化时注册的叫“spi”的总线
spi->dev.release = spidev_release;
device_initialize(&spi->dev); //设备模型方面的初始化
return spi;
}
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_device\n"); spi_master_put(master); return NULL; } spi->master = master; spi->dev.parent = dev; spi->dev.bus = &spi_bus_type; //该spi设备属于SPI子系统初始化时注册的叫“spi”的总线 spi->dev.release = spidev_release; device_initialize(&spi->dev); //设备模型方面的初始化 return spi; }spi_add_device(kernel3.0.15/drivers/spi/spi.c)
[cpp]
view plain
copy
print?
int spi_add_device(struct spi_device *spi)
{
static DEFINE_MUTEX(spi_add_lock);
struct device *dev = spi->master->dev.parent;
struct device *d;
int status;
/* Chipselects are numbered 0..max; validate. */
if (spi->chip_select >= spi->master->num_chipselect) { //片选号是从0开始的,如果大于或者等于片选数的话则返回出错
dev_err(dev, "cs%d >= max %d\n",
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);
d = bus_find_device_by_name(&spi_bus_type, NULL, dev_name(&spi->dev)); //遍历spi总线,看是否已经注册过该设备
if (d != NULL) {
dev_err(dev, "chipselect %d already in use\n",
spi->chip_select);
put_device(d);
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 setup %s, status %d\n",
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 add %s, status %d\n",
dev_name(&spi->dev), status);
else
dev_dbg(dev, "registered child %s\n", dev_name(&spi->dev));
done:
mutex_unlock(&spi_add_lock);
return status;
}
int spi_add_device(struct spi_device *spi) { static DEFINE_MUTEX(spi_add_lock); struct device *dev = spi->master->dev.parent; struct device *d; int status; /* Chipselects are numbered 0..max; validate. */ if (spi->chip_select >= spi->master->num_chipselect) { //片选号是从0开始的,如果大于或者等于片选数的话则返回出错 dev_err(dev, "cs%d >= max %d\n", 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); d = bus_find_device_by_name(&spi_bus_type, NULL, dev_name(&spi->dev)); //遍历spi总线,看是否已经注册过该设备 if (d != NULL) { dev_err(dev, "chipselect %d already in use\n", spi->chip_select); put_device(d); 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 setup %s, status %d\n", 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 add %s, status %d\n", dev_name(&spi->dev), status); else dev_dbg(dev, "registered child %s\n", dev_name(&spi->dev)); done: mutex_unlock(&spi_add_lock); return status; }spi_setup(kernel3.0.15/drivers/spi/spi.c)
[cpp]
view plain
copy
print?
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_err(&spi->dev, "setup: unsupported mode bits %x\n",
bad_bits);
return -EINVAL;
}
if (!spi->bits_per_word)
spi->bits_per_word = 8;
status = spi->master->setup(spi); //调用控制器驱动里的s3c64xx_spi_setup函数
dev_dbg(&spi->dev, "setup mode %d, %s%s%s%s"
"%u bits/w, %u Hz max --> %d\n",
(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;
}
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_err(&spi->dev, "setup: unsupported mode bits %x\n", bad_bits); return -EINVAL; } if (!spi->bits_per_word) spi->bits_per_word = 8; status = spi->master->setup(spi); //调用控制器驱动里的s3c64xx_spi_setup函数 dev_dbg(&spi->dev, "setup mode %d, %s%s%s%s" "%u bits/w, %u Hz max --> %d\n", (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; }
s3c64xx_spi_setup(kernel3.0.15/drivers/spi/spi_s3c64xx.c)
[cpp]
view plain
copy
print?
static int s3c64xx_spi_setup(struct spi_device *spi)
{
//由此可知在实例化struct spi_board_info时,其controller_data成员就应该指向struct s3c64xx_spi_csinfo的对象
struct s3c64xx_spi_csinfo *cs = spi->controller_data;
struct s3c64xx_spi_driver_data *sdd;
struct s3c64xx_spi_info *sci;
struct spi_message *msg;
unsigned long flags;
int err = 0;
if (cs == NULL || cs->set_level == NULL) {
dev_err(&spi->dev, "No CS for SPI(%d)\n", spi->chip_select);
return -ENODEV;
}
sdd = spi_master_get_devdata(spi->master);
sci = sdd->cntrlr_info;
spin_lock_irqsave(&sdd->lock, flags);
list_for_each_entry(msg, &sdd->queue, queue) {
/* Is some mssg is already queued for this device */
if (msg->spi == spi) {
dev_err(&spi->dev,
"setup: attempt while mssg in queue!\n");
spin_unlock_irqrestore(&sdd->lock, flags);
return -EBUSY;
}
}
if (sdd->state & SUSPND) {
spin_unlock_irqrestore(&sdd->lock, flags);
dev_err(&spi->dev,
"setup: SPI-%d not active!\n", spi->master->bus_num);
return -ESHUTDOWN;
}
spin_unlock_irqrestore(&sdd->lock, flags);
if (spi->bits_per_word != 8
&& spi->bits_per_word != 16
&& spi->bits_per_word != 32) {
dev_err(&spi->dev, "setup: %dbits/wrd not supported!\n",
spi->bits_per_word);
err = -EINVAL;
goto setup_exit;
}
/* Check if we can provide the requested rate */
if (!sci->clk_from_cmu) {
u32 psr, speed;
/* Max possible */
speed = clk_get_rate(sdd->src_clk) / 2 / (0 + 1);
if (spi->max_speed_hz > speed)
spi->max_speed_hz = speed;
psr = clk_get_rate(sdd->src_clk) / 2 / spi->max_speed_hz - 1;
psr &= S3C64XX_SPI_PSR_MASK;
if (psr == S3C64XX_SPI_PSR_MASK)
psr--;
speed = clk_get_rate(sdd->src_clk) / 2 / (psr + 1);
if (spi->max_speed_hz < speed) {
if (psr+1 < S3C64XX_SPI_PSR_MASK) {
psr++;
} else {
err = -EINVAL;
goto setup_exit;
}
}
speed = clk_get_rate(sdd->src_clk) / 2 / (psr + 1);
if (spi->max_speed_hz >= speed)
spi->max_speed_hz = speed;
else
err = -EINVAL;
}
setup_exit:
/* setup() returns with device de-selected */
disable_cs(sdd, spi);
return err;
}
相关文章推荐
- SPI驱动之主控制器驱动程序
- Linux SPI 子系统驱动笔记之Linux spi设备驱动与SPI控制器驱动的匹配问题
- 基于MT7688 原厂SDK 使用SPI控制器驱动TFT屏幕ILI9225驱动器(spi接口)
- 62 linux内核里基于GPIO口的SPI控制器驱动
- Linux SPI总线和设备驱动架构之三:SPI控制器驱动
- lpc3250spi控制器驱动部分分析
- 63 linux内核的SPI设备驱动模型及应用程序调用SPI控制器的方法
- linux spi 主控制器驱动
- 23 H5的spi控制器驱动
- spi控制器驱动模型
- lpc3250spi控制器驱动部分分析
- SPI驱动之SPI设备驱动程序
- Serial Peripheral Interface Bus · Motorola SPI ARM PL022 控制器驱动移植 SPI接口的SD卡调试
- Linux SPI总线和设备驱动架构之三:SPI控制器驱动
- 深入浅出spi驱动之控制器驱动
- Linux SPI总线和设备驱动架构之三:SPI控制器驱动
- Linux SPI总线和设备驱动架构之三:SPI控制器驱动
- spi 主机控制器驱动(spi_master)
- Linux SPI总线和设备驱动架构之三:SPI控制器驱动
- 3.0.35 SPI主机控制器驱动和外设驱动