61 OrangePi Linux内核里的spi控制器驱动

来源:互联网 发布:linux ntpdate 脚本 编辑:程序博客网 时间:2024/05/29 07:01

在全志H3里有2个spi控制器, 每个控制器可有4个片选信号:
这里写图片描述

//////////////////////////

script.bin里的spi控制器,spi设备相关设置:

[spi0]spi_used = 1spi_cs_bitmap = 1spi_mosi = port:PC00<3><default><default><default>spi_miso = port:PC01<3><default><default><default>spi_sclk = port:PC02<3><default><default><default>spi_cs0 = port:PC03<3><1><default><default>[spi1]spi_used = 0spi_cs_bitmap = 1spi_cs0 = port:PA13<2><1><default><default>spi_sclk = port:PA14<2><default><default><default>spi_mosi = port:PA15<2><default><default><default>spi_miso = port:PA16<2><default><default><default>[spi_devices]spi_dev_num = 1[spi_board0]modalias = "spidev"max_speed_hz = 33000000bus_num = 0chip_select = 0mode = 0full_duplex = 1manual_cs = 0

/////////////////在linux内核源码里spi控制器驱动//////

make menuconfig ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf-Device Drivers  --->     [*] SPI support  --->        <*>   SUNXI SPI Controller

控制器驱动源码在”drivers/spi/spi-sunxi.c”
控制器的驱动都是平台驱动,由平台设备来描述具体控制器的硬件资源。这spi控件器驱动与i2c控制器驱动一样,平台设备与平台驱动都在spi-sunxi.c文件里了。用起来方便,但不是很规范.

spi-sunxi.c的主过程分析:

module_init(sunxi_spi_init);static struct platform_driver sunxi_spi_driver = {    .probe   = sunxi_spi_probe,    .remove  = sunxi_spi_remove,    .driver = {        .name   = SUNXI_SPI_DEV_NAME, //名字为"spi"        .owner  = THIS_MODULE,        .pm     = SUNXI_SPI_DEV_PM_OPS,    },};static int __init sunxi_spi_init(void){    int i, ret = 0;     sunxi_spi_device_scan();  //初始化控制器的平台设备,并提供控制器的配置寄存器地址,中断号等硬件资源    sunxi_spi_chan_cfg(sunxi_spi_pdata); //用全局设量spi_used_mask记录script.bin里"spi_used=1"的控制器    ret = sunxi_spi_register_spidev(); //注册script.bin里描述的spi设备    ...#ifdef CONFIG_EVB_PLATFORM //此宏成立    for (i=0; i<SUNXI_SPI_NUM; i++)  // SUNXI_SPI_NUM的值为2#else    i = CONFIG_SPI_CHAN_NUM; /* In FPGA, only one channel is available. */#endif    {            if (sunxi_spi_chan_is_enable(i)) {            SPI_DBG("Sunxi SPI init channel %d \n", i);            ret = platform_device_register(&sunxi_spi_device[i]); //注册script.bin里"spi_used=1"的控制器的平台设备        ...            sunxi_spi_sysfs(&sunxi_spi_device[i]); //并在"/sys"目录下的控制器的平台设备目录下创建出"info", "status"属性文件。        }    }    if (spi_used_mask)        return platform_driver_register(&sunxi_spi_driver); //如有控制器的平台设备注册,则也注册控制器的平台驱动    ...}

///////////////////////////细看初始化函数里调用到的函数////////////////////////////

static int spi_used_mask = 0; //全局变量用于记录script.bin里哪个控制器是"spi_used=1"的//下面三个全局数组表示每个spi控制器使用的平台设备对象,资源,平台数据等 static struct resource sunxi_spi_resources[SUNXI_SPI_NUM * SUNXI_SPI_RES_NUM];static struct sunxi_spi_platform_data sunxi_spi_pdata[SUNXI_SPI_NUM];static struct platform_device sunxi_spi_device[SUNXI_SPI_NUM];//初始化控制器的平台设备,并提供控制器的配置寄存器地址,中断号等硬件资源static void __init sunxi_spi_device_scan(void){    int i;    memset(sunxi_spi_device, 0, sizeof(sunxi_spi_device));    memset(sunxi_spi_pdata, 0, sizeof(sunxi_spi_pdata));    memset(sunxi_spi_resources, 0, sizeof(sunxi_spi_resources));    for (i=0; i<SUNXI_SPI_NUM; i++) {        sunxi_spi_resources[i * SUNXI_SPI_RES_NUM].start = SUNXI_SPI_MEM_START(i);    ...        sunxi_spi_resources[i * SUNXI_SPI_RES_NUM + 1].start = SUNXI_SPI_IRQ(i);    ...        sunxi_spi_pdata[i].cs_bitmap = SUNXI_CS_BITMAP(i); // 值为1        sunxi_spi_pdata[i].cs_num    = SUNXI_CS_NUM(i);    // 值为1        sunxi_spi_device[i].name = SUNXI_SPI_DEV_NAME; //"spi"        sunxi_spi_device[i].id   = i; //平台设备的ID,也就驱动好的控制器编号    ... //下面准备控制器的平台设备的资源,平台数据        sunxi_spi_device[i].resource = &sunxi_spi_resources[i * SUNXI_SPI_RES_NUM];        sunxi_spi_device[i].num_resources = SUNXI_SPI_RES_NUM;        sunxi_spi_device[i].dev.platform_data = &sunxi_spi_pdata[i];    }}//用全局设量spi_used_mask记录script.bin里"spi_used=1"的控制器static void sunxi_spi_chan_cfg(struct sunxi_spi_platform_data *pdata){    int i;    script_item_u item = {0};    script_item_value_type_e type = 0;    char spi_para[16] = {0};    for (i=0; i<SUNXI_SPI_NUM; i++) {        sprintf(spi_para, SUNXI_SPI_DEV_NAME"%d", i);        type = script_get_item(spi_para, "spi_used", &item);        ...        if (item.val)            spi_used_mask |= SUNXI_SPI_CHAN_MASK(i);            ...    }}
////////////////就像i2c设备驱动模型一样,spi设备驱动模型里用spi_board_info来描述spi设备,然后用spi_register_board_info函数来注册spi_board_info设备信息. 最后在控制器驱动对象注册时再创建出相应的spi_device设备对象struct spi_board_info {        char            modalias[SPI_NAME_SIZE]; //设备名字        const void      *platform_data;          //spi_device.dev.platform_data        void            *controller_data;        // spi_device.controller_data, 这里通常指定一个片选的操作函数及IO口. 在控制器驱动里会调用到        int             irq;                     // spi_device.irq        u32             max_speed_hz;            // spi_device.max_speed_hz;    u16             bus_num;                 //标明此SPI设备是挂载到哪个SPI控制器上        u16             chip_select;             //一个控制器可以挂载多个设备,这里指定此设备使用的是控制器上的第几个片选, 使片选数组里的第几个片选        u8              mode;    //工作时序方式 };//根据script.bin里描述的spi设备,创建出对应的spi_board_info对象,并注册static int __init sunxi_spi_register_spidev(void){    script_item_u spi_dev_num, temp_info;    script_item_value_type_e type;    int i, spidev_num;    char spi_board_name[32] = {0};    struct spi_board_info* board;    type = script_get_item("spi_devices", "spi_dev_num", &spi_dev_num); //获取script.bin里的"spi_devices"的键值    ...    spidev_num = spi_dev_num.val; //表示在script.bin里描述spi设备的个数, 在script.bin里每个设备的主键是"[spi_board%d]"    ...    spi_boards = (struct spi_board_info*)kzalloc(sizeof(struct spi_board_info) * spidev_num, GFP_KERNEL);  //创建出所需的spi_board_info对象空间.    for (i=0; i<spidev_num; i++) {        board = &spi_boards[i];        sprintf(spi_board_name, "spi_board%d", i);        type = script_get_item(spi_board_name, "modalias", &temp_info); //获取"spi_board0"主键下的"modalias"子键的值    ...        sprintf(board->modalias, "%s", temp_info.str); //把获取出的名字设为spi_board_info对象的名字        type = script_get_item(spi_board_name, "max_speed_hz", &temp_info); //设备的最大工作时钟    ...        board->max_speed_hz = temp_info.val;        type = script_get_item(spi_board_name, "bus_num", &temp_info); //表示此设备是连接在哪个编号的控制器    ...        board->bus_num = temp_info.val;        type = script_get_item(spi_board_name, "chip_select", &temp_info);//获取设备的片选线    ...        board->chip_select = temp_info.val;        type = script_get_item(spi_board_name, "mode", &temp_info); //获取设备的时序工作方式    ...        board->mode = temp_info.val;        SPI_INF("%-16d %-16s %-16d %-8d %-4d %-4d\n", i, board->modalias, board->max_speed_hz,                board->bus_num, board->chip_select, board->mode);    }    if (spi_register_board_info(spi_boards, spidev_num)) { //最后注册所有的spi_board_info信息    ...    }    return 0;}

///////////////////////////////////////////////////////////////////////

/////////// 在linux内核里,spi控制器驱动好后用struct spi_master的一个对象来描述.struct spi_master {    struct device   dev; //基于device成员扩展    ...    s16         bus_num; //控制器对象的编号,由平台设备提供    ...    u16         num_chipselect;    ...    u16         mode_bits; //工作时序方式    ...};  在spi-sunxi.c里,封装了结构体struct sunxi_spi用于记录每个控制器对象在驱动里的数据struct sunxi_spi {    struct platform_device *pdev; //指定控制器的平台设备对象地址    struct spi_master *master;  //指定驱动好后创建出来的spi_master对象地址    ...    enum spi_mode_type mode_type; //工作时序方式    unsigned int irq;  //中断号    char dev_name[48];  //名字为"spi.%d"    ...};控制器的平台驱动的probe函数static int __devinit sunxi_spi_probe(struct platform_device *pdev){    struct resource *mem_res;    struct sunxi_spi *sspi;    struct sunxi_spi_platform_data *pdata;    struct spi_master *master;    int ret = 0, err = 0, irq;    int cs_bitmap = 0;    ...    pdata = pdev->dev.platform_data;    //获取平台设备提供的资源    mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);    ...     irq = platform_get_irq(pdev, 0);    //创建出spi_master对象的空间和struct sunxi_spi对象的空间, 并dev_set_drvdata(&spi_master.dev, sunxi_spi);    master = spi_alloc_master(&pdev->dev, sizeof(struct sunxi_spi));    ...    platform_set_drvdata(pdev, master);    sspi = spi_master_get_devdata(master); // dev_get_drvdata(...)    memset(sspi, 0, sizeof(struct sunxi_spi));    sspi->master        = master;    sspi->irq           = irq;    ..    sspi->cs_control    = sunxi_spi_cs_control;    sspi->cs_bitmap     = pdata->cs_bitmap; /* cs0-0x1; cs1-0x2; cs0&cs1-0x3. */    sspi->busy          = SPI_FREE;    sspi->mode_type     = MODE_TYPE_NULL;    //初始化spi_master对象    master->bus_num         = pdev->id;    master->setup           = sunxi_spi_setup;    master->cleanup         = sunxi_spi_cleanup;    master->transfer        = sunxi_spi_transfer;    master->num_chipselect  = pdata->cs_num;    master->mode_bits       = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH| SPI_LSB_FIRST;    ...    cs_bitmap = sunxi_spi_get_cfg_csbitmap(pdev->id);    ...    snprintf(sspi->dev_name, sizeof(sspi->dev_name), SUNXI_SPI_DEV_NAME"%d", pdev->id);    err = request_irq(sspi->irq, sunxi_spi_handler, IRQF_DISABLED, sspi->dev_name, sspi);    ...    sspi->base_addr = ioremap(mem_res->start, resource_size(mem_res));    ..    sspi->base_addr_phy = mem_res->start;    sspi->workqueue = create_singlethread_workqueue(dev_name(master->dev.parent)); //创建一个基于线程的工作队列,用于spi控制器的数据传输工作任务    ...    sspi->pdev = pdev;    pdev->dev.init_name = sspi->dev_name;    ret = sunxi_spi_hw_init(sspi, pdata); //根据参数配置控制器的寄存器    ...    spin_lock_init(&sspi->lock);    init_completion(&sspi->done);    INIT_WORK(&sspi->work, sunxi_spi_work); //初始化工作任务,当工作任务得到调用时,sunxi_spi_work函数就会触发调用    INIT_LIST_HEAD(&sspi->queue);    if (spi_register_master(master)) { //注册spi_master对象    ...    }    ...}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
spi控制器驱动好不会在/sys目录下创建spi_master对象的子目录,只可以通过”/sys/bus/platform/drivers”目录下查看平台驱动目录下有哪些匹配上的平台设备.

阅读全文
0 0
原创粉丝点击