Linux SPI初始化及接口函数代码细究

2012-01-08 22:11:38

      目的:我需要掌握spi驱动相关数据结构关系,及在哪部分函数中把这些数值进行底层寄存器赋值的。结合应用层函数完成spi驱动的代码测试。已达到灵活修改的目的。

按顺序看probe函数中

if (!pdata->set_cs)

则              hw->set_cs = s3c24xx_spi_gpiocs;

                  gpio_direction_output(pdata->pin_cs, 1);

由于我的platform_device.platform_data没设置set_cs。默认设置gpio片选。并且把pin_cs脚设置为输出。接着

s3c24xx_spi_initialsetup(hw);函数里面有设置寄存器操作,设置默认值,代码如下:

        /* for the moment, permanently enable the clock */

        clk_enable(hw->clk);

        /* program defaults into the registers */

        writeb(0xff, hw->regs + S3C2410_SPPRE);

        writeb(SPPIN_DEFAULT, hw->regs + S3C2410_SPPIN);

        writeb(SPCON_DEFAULT, hw->regs + S3C2410_SPCON);

再接着就是spi_add_device函数中调用s3c24xx_spi_setup,里面有设置寄存器。

        if (!spi->bits_per_word)

                  spi->bits_per_word = 8;

由于我的spi->bits_per_word之前都没有定义过。所以为0,那么默认设置spi->bits_per_word = 8;

接着调用s3c24xx_spi_setupxfer函数第一句就是struct s3c24xx_spi *hw = to_hw(spi);分析一下。

仔细看了下to_hw就是说spi把spi_device结构转换为s3c24xx_spi结构。

方法就是spi(spi_device结构)先指向父指针master(spi_master结构)。

接着就是指向dev(spi_master的device成员),再接着指向driver_data(device结构的driver_data成员)为什么说这就是s3c24xx_spi的类型哪?看probe一开始的几句代码即可。

struct s3c24xx_spi *hw;

hw = spi_master_get_devdata(master);

memset(hw, 0, sizeof(struct s3c24xx_spi));

接着回到s3c24xx_spi_setupxfer函数下面的代码是

        bpw = t ? t->bits_per_word : spi->bits_per_word;

        hz  = t ? t->speed_hz : spi->max_speed_hz;

由于t传进来是NULL,所以bpw= spi->bits_per_word;刚才分析过了s3c24xx_spi_setup中把它设置为默认值8。hz  = spi->max_speed_hz;在spi_add_device函数之前的spi_new_device有赋值proxy->max_speed_hz = chip->max_speed_hz;就是spi_board_info里的赋值,我自己设置的值。至于要写入寄存器是在接下来的hw->bitbang.chipselect(spi, BITBANG_CS_INACTIVE)中调用hw->set_cs(hw->pdata, spi->chip_select, cspol^1);(probe中hw->set_cs = s3c24xx_spi_gpiocs里定义的) s3c24xx_spi_gpiocs函数,设置cspol值为0。

此函数也调用div = clk_get_rate(hw->clk) / hz;设置波特率寄存器。

===========================================================bitbang_work函数中有调用bitbang->chipselect(spi, BITBANG_CS_ACTIVE);

s3c24xx_spi_probe中有定义       hw->bitbang.chipselect     = s3c24xx_spi_chipsel;

s3c24xx_spi_chipsel函数里面有设置spi模式。数据来源是spi->mode。spi->mode又是在那里赋值的呢?带着问题做了如下探索

bitbang_work是在spi_bitbang_start中调用。

s3c24xx_spi_probe->spi_bitbang_start->bitbang_work但此时,spi-mode并没有赋值。

赋值是在哪里呢?换个方法,按顺序查找。

s3c24xx_spi_probe->spi_bitbang_start->spi_register_master->scan_boardinfo->spi_new_device通过从头到尾的方式查找,唯一首先出现spi_master与spi_device关系的是spi_new_device函数。进入其中调用的spi_alloc_device函数,查看注释发现。

* Caller is responsible to call spi_add_device() on the returned

* spi_device structure to add it to the SPI master.

可以确定,spi->mode的首次赋值就是词句代码

proxy->mode = chip->mode;在bitbang_work函数中spi-mode并没有赋值但是还在用是为什么呢?再仔细看代码,原来while有个判断条件。之前没看到,搜索错了方向。

在spi_new_device函数中proxy是spi_device结构。Spi->mode等都是在如下这里赋值

        proxy->chip_select = chip->chip_select;

        proxy->max_speed_hz = chip->max_speed_hz;

        proxy->mode = chip->mode; //s3c2410_spi1_board中没定义,则默认为0

        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;

========================

接着就是spi_add_device函数,里面有一句if (spi->chip_select >= spi->master->num_chipselect) 他们分别是什么值呢?spi->master->num_chipselect在哪里?带着问题,开始了如下探索历程

 

spi->chip_select在spi_board_info s3c2410_spi1_board[]结构赋值中为0。

static struct spi_board_info s3c2410_spi1_board[] = {

        [0] = {

                 .modalias = "spidev",

                .bus_num        = 1,                 .chip_select        = 0,

                .irq = IRQ_EINT9,

                .max_speed_hz         = 2000*1000,

        },

};

在scan_boardinfo 中调用函数时候传递的参数是(void) spi_new_device(master, chip);master是spi_master结构。再之前的spi_register_master函数中有对spi_master的num_chipselect成员赋值的核对。代码如下:

        if (master->num_chipselect == 0)

                  return -EINVAL;再往前找应该就能找到赋值了。

spi_bitbang_start中有对spi_register_master的调用。代码如下:

status = spi_register_master(bitbang->master);

再往前看。s3c24xx_spi_probe函数中调用spi_bitbang_start,往上看到了master->num_chipselect的赋值语句了。代码如下:

master->num_chipselect = hw->pdata->num_cs;

而hw->pdata = pdata = pdev->dev.platform_data;

而struct platform_device *pdev是platform_device中的platform_data结构,已经赋值了如下

static struct s3c2410_spi_info s3c2410_spi1_platdata = {

        .pin_cs = S3C2410_GPG3,

        .num_cs = 1,

        .bus_num = 1,

};

所以chip_select= 0, spi->master->num_chipselect值为1.

通过倒序来找,从spi_add_device一直找到了s3c24xx_spi_probe,按顺序写下:

s3c24xx_spi_probe->spi_bitbang_start->spi_register_master->scan_boardinfo->spi_new_device->spi_add_device

一个接口对应一个master,一个master对应一条SPI总线,一条总线上可能挂有多个设备,num_chipselect 就表示该总线上的设备, chip_select表示该SPI设备在该条SPI总线上的设备号的唯一标识。

====================================================probe中函数绑定与调用分析

        hw->bitbang.master         = hw->master;

        hw->bitbang.setup_transfer = s3c24xx_spi_setupxfer;// s3c24xx_spi_setup中调用ret = s3c24xx_spi_setupxfer(spi, NULL);

        hw->bitbang.chipselect     = s3c24xx_spi_chipsel;//s3c24xx_spi_setupxfer中调用hw->bitbang.chipselect(spi, BITBANG_CS_INACTIVE);

        hw->bitbang.txrx_bufs      = s3c24xx_spi_txrx;// bitbang_work中调用status = bitbang->txrx_bufs(spi, t);

        hw->bitbang.master->setup  = s3c24xx_spi_setup;// spi_add_device中调用status = spi->master->setup(spi);

按probe初始化顺序,则spi_add_device函数中调用

status = spi->master->setup(spi);//(s3c24xx_spi_setup)

接着s3c24xx_spi_setup函数中调用

ret = s3c24xx_spi_setupxfer(spi, NULL);

接着s3c24xx_spi_setupxfer函数中调用

hw->bitbang.chipselect(spi, BITBANG_CS_INACTIVE) 

关于如上图的问题,怎么HZ数一开始不是我默认设置的呢?原来是因为,用户层函数调用的时候先是从octl函数开始的不是从probe函数开始的。

probe函数打印出来的信息完全正确。如下图:

 

先调用spidev_message如何调用到bitbang_work的?

spi_bitbang_start->bitbang_work接着就没方向了,不可能到spidev_message了。于是查了网上的资料,是spi_bitbang_transfer 中的代码如下:queue_work(bitbang->workqueue, &bitbang->work);  调用了bitbang_work函数。为什么bitbang->work指示的就是bitbang_work函数呢?原来在       spi_bitbang_start函数中的第一句代码INIT_WORK(&bitbang->work, bitbang_work);就说明了。好了,这样就有方向继续摸索了。关系流程如下:

spidev_message ->spidev_sync->spi_async【spi->master->transfer(spi, message);  定义在spi_bitbang_start函数中bitbang->master->transfer = spi_bitbang_transfer;】 -> spi_bitbang_transfer->bitbang_work

===============================================================

bitbang_work函数中有句代码

setup_transfer = bitbang->setup_transfer;

                             if (setup_transfer) {

                                       status = setup_transfer(spi, t);

bitbang->setup_transfer(spi, t)又是调用哪个具体函数呢?

很面熟,想起来之前好像看到过在bitbang相关函数中,于是找到了spi_bitbang_start函数中有,代码如下:

if (!bitbang->master->setup) {

                             if (!bitbang->setup_transfer)

                                       bitbang->setup_transfer =                                               spi_bitbang_setup_transfer;

原来是调用准备spi_bitbang_setup_transfer函数,先设置指针,传递进来的参数是(spi, t); 接着就在bitbang_work函数中调用status = setup_transfer(spi, t);即spi_bitbang_setup_transfer函数

其中与修改频率相关的代码如下:t是指向用户传递来的数值,如果应用层函数没有传递Hz值,则使用spi->max_speed_hz;就是spi_board_info中我自己定义的值。终于找到了赋值的元凶。代码如下:

        /* nsecs = (clock period)/2 */

        if (!hz)

                  hz = spi->max_speed_hz;

        if (hz) {

                  cs->nsecs = (1000000000/2) / hz;

                  if (cs->nsecs > (MAX_UDELAY_MS * 1000 * 1000))

                             return -EINVAL;

        }

从打印的信息看出spi_bitbang_setup_transfer会调用s3c24xx_spi_setupxfer函数?原来之前分析错了status = setup_transfer(spi, t);调用的probe中的s3c24xx_spi_setupxfer。因为这个if (!bitbang->master->setup) 条件不成了,所以bitbang.setup_transfer = s3c24xx_spi_setupxfer。这样就能进入s3c24xx_spi_setupxfer函数了,并且这时候t不等于NULL。

bitbang_work函数中的struct spi_transfer  *t = NULL;那么到底是哪一句为t赋值的呢?再调试。应该是list_for_each_entry (t, &m->transfers, transfer_list) 句。

继续分析串口信息,又进入了s3c24xx_spi_setupxfer啊!bitbang_work有调用吗?继续看,代码如下

                  

/* restore speed and wordsize */                    

if (setup_transfer)

                             setup_transfer(spi, NULL);

                  if (!(status == 0 && cs_change)) {

                             ndelay(nsecs);

                             bitbang->chipselect(spi, BITBANG_CS_INACTIVE);

                             ndelay(nsecs);

                  }

天呢!原来回复数据。也就是说我的此次spi自收发,用的不是初始化的值。原因是在应用层函数中传入了if (t->speed_hz || t->bits_per_word) 判断条件中的2个值。如果不传这2个值,那么应该就用的是我在kernel中设置的默认值了。试一下,名称为spidev_test5

见下图,效果和想象的是一致的。speed_hz与bits_per_word我在应用层注释掉了。就没调用如下代码段,途中的“ready to change freq”信息应该写成finish to change freq and other比较好,呵呵。 

                             if (t->speed_hz || t->bits_per_word) {

                                       printk("go bitbang_work 1\n");//by apple

                                       setup_transfer = bitbang->setup_transfer;

                                       if (!setup_transfer) {

                                                  status = -ENOPROTOOPT;

                                                  break;

                                       }

                             }

                             if (setup_transfer) {

                                       printk("setup_transfer\n"); //by apple

                                       printk("NULL addr is 0x%2x,t addr is 0x%2x ",NULL,t); //by apple

                                       status = setup_transfer(spi, t);

                                       if (status < 0)

                                                  break;

                             }

备注:我的spidev_test函数的主函数中write和read函数都被我注释掉了。直接先transfer(fd);接着在read 2个寄存器后打印出来mode和speed。


上图1:

 

 

 上图2:

 

 

 上图3:

 

 

转载于:https://www.cnblogs.com/AppleCai/p/10778167.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 重新初始化SPI驱动可以采取以下步骤: 1. 在Linux系统中,驱动程序通常由内核模块加载。因此,首先需要卸载旧的SPI驱动程序模块,可以使用以下命令: ``` sudo rmmod <driver_name> ``` 2. 然后,重新加载新的SPI驱动程序模块,可以使用以下命令: ``` sudo modprobe <driver_name> ``` 3. 如果有必要,可以重新设置SPI设备的配置参数,例如时钟速度、传输模式等。这可以通过SPI设备的ioctl操作来完成。 4. 最后,可以通过读写SPI设备来测试SPI驱动程序是否正确初始化。 请注意,在重新初始化SPI驱动程序之前,请确保已备份所有重要数据,并确保您知道如何恢复系统以避免潜在的损失。 ### 回答2: 要重新初始化Linux驱动程序中的SPI总线,您需要执行以下步骤: 1. 确保您已经正确连接了SPI设备到合适的硬件接口上,并且SPI总线驱动已经正确加载到Linux内核中。 2. 使用命令行界面进入Linux系统,确保您具有root权限。 3. 确定您要重新初始化SPI设备的名称或标识符。您可以通过运行命令 `ls /dev/spi*` 来查找可用的SPI设备。 4. 执行 `rmmod` 命令,将现有的SPI总线驱动从内核中删除。例如,如果您的SPI总线驱动名为 `spi_dev`,您可以运行命令 `rmmod spi_dev` 来删除该驱动。 5. 执行 `modprobe` 命令,重新加载SPI总线驱动到内核中。您需要提供驱动名称作为参数。例如,如果您的SPI总线驱动名为 `spi_dev`,您可以运行命令 `modprobe spi_dev` 来重新加载该驱动。 6. 确认SPI总线驱动已成功加载到内核中。您可以通过运行命令 `lsmod | grep spi_dev` 来检查驱动的状态。 7. 如果需要,您可以重新配置SPI总线驱动的参数。您可以编辑相关配置文件(通常位于 `/etc/modprobe.d/` 目录下)来修改驱动的参数,然后重新加载驱动。 请注意,重新初始化SPI总线驱动可能会影响与SPI设备相关的应用程序和数据传输。在执行此操作之前,请确保备份相关的配置和数据,并确保您了解该操作的风险和可能的影响。 ### 回答3: 在Linux中,如果需要重新初始化SPI驱动,可以按照以下步骤进行操作: 1. 首先,需要确定已安装了spi驱动。可以通过使用`lsmod`命令来列出已加载的驱动程序列表,查看是否存在相应的spi驱动程序。 2. 如果确定已安装spi驱动,可以尝试重新加载该驱动,以重新初始化。首先,可以使用`rmmod`命令卸载该驱动程序,如:`sudo rmmod spi_driver`,其中`spi_driver`为相应的spi驱动模块名。 3. 卸载完驱动后,可以使用`modprobe`命令重新加载spi驱动,如:`sudo modprobe spi_driver`,其中`spi_driver`为驱动模块名。 4. 接下来,可以进行配置spi驱动的相关参数。可以通过编辑`/etc/modprobe.d/spi_driver.conf`文件,将相关参数添加或修改为所需的配置。 5. 最后,可以重新启动系统,使新的spi驱动配置生效。 需要注意的是,在进行任何驱动操作之前,建议先备份相关的配置文件,以防出现意外问题。 以上就是重新初始化SPI驱动的一般步骤,但具体操作还需要根据具体情况进行调整和检查,确保操作正确无误。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值