SPI在linux3.14.78 FS_S5PC100(Cortex A8)和S3C2440上驱动移植(deep dive)

发布者:MysticJourney最新更新时间:2024-08-09 来源: cnblogs关键字:SPI  linux  Cortex  S3C2440  驱动移植 手机看文章 扫描二维码
随时随地手机看文章

由于工作的原因,对SPI的理解最为深刻,也和SPI最有感情了,之前工作都是基于OSEK操作系统上进行实现,也在US/OS3上实现过SPI驱动的实现和测试,但是都是基于基本的寄存器操作,没有一个系统软件架构的思想,感觉linux SPI驱动很强大,水很深,废话少说,SPI总线上有两类设备:一类是主机端,通常作为SOC系统的一个子模块出现,比如很多嵌入式MPU中都常常包含SPI模块。一类是从机被控端,例如一些SPI接口的Flash、传感器等等。主机端是SPI总线的控制者,通过使用SPI协议主动发起SPI总线上的会话。而受控端则被动接受SPI主控端的指令,并作出响应的响应,本文读者前提是必须熟练掌握linux Platform总线驱动模型 和基本字符设备驱动的实现。


SPI总线由MISO(串行数据输入)、MOSI(串行数据输出)、SCK(串行移位时钟)、CS(使能信号)4个信号线组成。SPI常用四种数据传输模式,主要差别在于:输出串行同步时钟极性(CPOL)和相位(CPHA)可以进行配置。如果CPOL= 0,串行同步时钟的空闲状态为低电平;如果CPOL= 1,串行同步时钟的空闲状态为高电平。如果CPHA= 0,在串行同步时钟的前沿(上升或下降)数据被采样;如果CPHA = 1,在串行同步时钟的后沿(上升或下降)数据被采样。同I2C子系统类似,SPI子系统分为3个部分,分别是SPI核心层、主控制器驱动和协议驱动,通俗一点就是SPI核心层主要完成1.定义并注册SPI总线spi_bus_type和控制器类spi_master_class;2.提供spi_driver,spi_device和spi_master的分配,创建,注册和注销;3.实现SPI通信方法的上层代码。主控制器驱动对应I2C的适配器驱动,SPI用spi_master来描述相应的控制器,通常用spi_bitbang来控制实际的数据传输,功能非常类似与i2c_algorithm。协议驱动类比I2C设备驱动,可以理解成客户端驱动,下面详细分析和实现基于linux3.14.78内核版本 SPI在S3C2440/6410上驱动移植.


Step1,实现SPI控制器的设备接口(相对应有两种方式,一种实现方式是S3c2440纯粹通过配置来实现,另一种实现方式针对m25p10,独立编写单独的spi驱动模块)


首先针对S3C2440,SPI控制器的设备接口在drivers/spi/spidev.c中实现,下图为相应的软件流程图,spidev.c中的spidev_init()作为模块初始化函数,在系统启动或者模块加载是被调用,主要完成以下三种操作:


  1、调用register_chrdev()为SPI控制器注册主设备号为153,次设备号范围为0~255,文件操作集合为spidev_fops的字符设备;


  2、调用class_create()注册一个名为“spidev”的设备类;


  3、调用spi_register_driver()向系统添加SPI控制器的设备驱动spidev_spi;


spi_register_driver()将驱动spidev_spi添加到SPI核心层注册的spi_bus_type总线上,注意,该总线属于spi总线,Spi总线对应的总线类型为spi_bus_type,在内核的drivers/spi/spi.c中定义,对应的匹配规则是(高版本中的匹配规则会稍有变化,引入了id_table,可以匹配多个spi设备名称)详见以下代码。由于spi总线的匹配方式是检查spi_device.modalias与spi_driver.driver.name是否相同,而spidev_spi的driver.name是“spidev”,so 只有spi_bus_type总线上的modalias为“spidev”的设备,才可以与spidev_spi驱动匹配。由于S3C2440拥有两个SPI控制器,对应的平台信息是s3c_device_spi0和s3c_device_spi1,可以将其添加到机器配置文件的My2440_devices数组中,驱动中probe方法中用到的总线编号、片选总数等信息来源于平台数据,这些数据需要用户添加,添加方式有两种,平台设备的平台数据类型是S3C2410_SPI_info,在板级初始化文件中可以为两个SPI平台设备分别定义和添加这些平台数据,此外为了支持SPI控制器设备接口功能,还需要在机器配置文件为SPI控制器设备添加并注册spi_board_info对象,详见下面代码。


备与驱动匹配后,通过调用spidev_spi的probe方法来绑定工作见代码。SPI控制器设备操作集合是spidev_fops,主要包括spidev_write,spidev_read,spidev_ioctl,spidev_open和spidev_release。下面的代码注释中详尽分析了spidev_read()函数的实现过程,spidev_write()的分析和实现方法与read类似,spidev_open()会遍历device_list链表,找出其中设备号与打开设备文件的inode.i_rdev相等的spidev_data对象,并将该对象记录在文件的私有数据filp->private_data中,以供read和write函数获取,此外要实现全双工的传输需要借助控制器设备的ioctl方法,对应实现函数是spidev_ioctl(),全双工传输也是通过spidev_sync()完成的,与半双工传输唯一不同的是在消息的传输段中,tx_buffer和rx_buffer同时被设置,它还提供获取和修改时钟模式、子宽、最大时钟频率属性的命令。


 1 static struct s3c2410_spi_info s3c2410_spi0_platdata = {  

 2     .pin_cs = S3C2410_GPG2,  

 3     .num_cs = 2,  

 4     .bus_num = 0,  

 5     .gpio_setup = s3c24xx_spi_gpiocfg_bus0_gpe11_12_13,  

 6 };  

 7 static struct spi_board_info s3c2410_spi0_board[] =  

 8 {  

 9     [0] = {  

10         .modalias = 'spidev',  

11         .bus_num = 0,  

12         .chip_select = 0,  

13         .max_speed_hz = 500 * 1000,  

14     },

15     [1] = {  

16         .modalias = 'at25',  

17         .platform_data = &at25_eeprom_data,

18         .bus_num = 0,  

19         .chip_select = 1,  

20         .max_speed_hz = 500 * 1000,  

21     }  

22 };  

23 static struct s3c2410_spi_info s3c2410_spi1_platdata = {  

24     .pin_cs = S3C2410_GPG3,  

25     .num_cs = 1,  

26     .bus_num = 1,  

27     .gpio_setup = s3c24xx_spi_gpiocfg_bus1_gpg5_6_7,  

28 };  

29 static struct spi_board_info s3c2410_spi1_board[] =  

30 {  

31     [0] = {  

32         .modalias = 'spidev',  

33         .bus_num = 1,  

34         .chip_select = 0,  

35         .max_speed_hz = 500 * 1000,  

36     }  

37 };

38 static void __init My2440_machine_init(void)

39 {

40 s3c24xx_fb_set_platdata(&My2440_fb_info);

41 s3c_i2c0_set_platdata(NULL);

42 i2c_register_board_info(0, i2c_devices, ARRAY_SIZE(i2c_devices));

43 s3c_device_spi0.dev.platform_data= &s3c2410_spi0_platdata; 

44 spi_register_board_info(s3c2410_spi0_board, ARRAY_SIZE(s3c2410_spi0_board)); 

45 s3c_device_spi1.dev.platform_data= &s3c2410_spi1_platdata; 

46 spi_register_board_info(s3c2410_spi1_board, ARRAY_SIZE(s3c2410_spi1_board)); 

47 s3c_device_nand.dev.platform_data = &My2440_nand_info;

48 s3c_device_sdi.dev.platform_data = &My2440_mmc_cfg;

49 platform_add_devices(My2440_devices, ARRAY_SIZE(My2440_devices));

50 }   


1 struct bus_type spi_bus_type = {

2                 .name = 'spi',

3                 .dev_attrs = spi_dev_attrs,

4                 .match = spi_match_device,

5                 .uevent = spi_uevent,

6                 .suspend = spi_suspend,

7                 .resume = spi_resume,

8         };


static int spi_match_device(struct device *dev, struct device_driver *drv)

         {

                 const struct spi_device *spi = to_spi_device(dev);

                 return strcmp(spi->modalias, drv->name) == 0;

         }


static int spidev_probe(struct spi_device *spi)

{

    struct spidev_data    *spidev;

    int            status;

    unsigned long        minor;

    /* Allocate driver data, 分配并初始化一个spidev_data对象,用来描述SPI控制器设备驱动操作的控制器设备,包含它的设备号,传输缓存和内嵌spi_device*/

    spidev = kzalloc(sizeof(*spidev), GFP_KERNEL);

    if (!spidev)

        return -ENOMEM;

    /* Initialize the driver data ,将与spidev_spi驱动匹配的spi_device赋值给spidev_data.spi,通过find_first_zero_bit()从位图minors中分配到次设备号,

  与主设备号SPIDEV_MAJOR构成设备号并赋值给spidev_data.devt,然后调用device_create()在/dev下创建名称格式为“spidev%d%d”的字符设备文件,第一个%d表示控制器编号,

  即spi_device.master.bus_num,第二个对应片选号,即spi_device.chip_select*/

    spidev->spi = spi;

    spin_lock_init(&spidev->spi_lock);

    mutex_init(&spidev->buf_lock);

    INIT_LIST_HEAD(&spidev->device_entry);

    /* If we can allocate a minor number, hook up this device.

     * Reusing minors is fine so long as udev or mdev is working.

     */

    mutex_lock(&device_list_lock);

    minor = find_first_zero_bit(minors, N_SPI_MINORS);

    if (minor < N_SPI_MINORS) {

        struct device *dev;

        spidev->devt = MKDEV(SPIDEV_MAJOR, minor);

        dev = device_create(spidev_class, &spi->dev, spidev->devt,

                    spidev, 'spidev%d.%d',

                    spi->master->bus_num, spi->chip_select);

        status = PTR_ERR_OR_ZERO(dev);

    } else {

        dev_dbg(&spi->dev, 'no minor number available!n');

        status = -ENODEV;

    }

    if (status == 0) {

        set_bit(minor, minors);

/*将spidev_data对象添加到一个名为device_list的设备列表中,最后将spidev_data对象设置为对应spi_device的驱动数据,将其值赋值给dpi_device.dev.driver_data*/

        list_add(&spidev->device_entry, &device_list);

    }

    mutex_unlock(&device_list_lock);

    if (status == 0)

        spi_set_drvdata(spi, spidev);

    else

        kfree(spidev);

    return status;

}


/*spidev_read(),spidev_write()实现的读写操作都是半双工的传输,要实现全双工传输需要调用spidev_ioctl()操作,在spidev_read()中,通过调用spidev_sync_read()

  同步读取指定长度数据到spidev_data_buffer中,然后调用copy_to_user()将读取的数据复制到用户空间缓冲*/

static ssize_t spidev_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos)

{

    struct spidev_data    *spidev;

    ssize_t            status = 0;

    /* chipselect only toggles at start or end of operation */

    if (count > bufsiz)

        return -EMSGSIZE;

    spidev = filp->private_data;

    mutex_lock(&spidev->buf_lock);

    status = spidev_sync_read(spidev, count);

    if (status > 0) {

        unsigned long    missing;

        missing = copy_to_user(buf, spidev->buffer, status);

        if (missing == status)

            status = -EFAULT;

        else

            status = status - missing;

    }

    mutex_unlock(&spidev->buf_lock);

    return status;

}


/*spidev_sync_read()中定义一个spi_transfer和spi_message对象,使用spidev_data.buffer初始化spi_transfer.rx_buf,

  然后将spi_transfer对象添加到spi_message中,调用spidev_sync()传输信息;*/


spidev_sync_read(struct spidev_data *spidev, size_t len)

{

    struct spi_transfer    t = {

            .rx_buf        = spidev->buffer,

[1] [2] [3] [4] [5]
关键字:SPI  linux  Cortex  S3C2440  驱动移植 引用地址:SPI在linux3.14.78 FS_S5PC100(Cortex A8)和S3C2440上驱动移植(deep dive)

上一篇:S3C2440的中断体系结构
下一篇:LCD驱动移植在在mini2440(linux2.6.29)和FS4412(linux3.14.78)上实现对比(deep dive)

推荐阅读最新更新时间:2026-03-21 11:49

linux2.6.32 内核移植s3c2440 - DM9000网卡驱动移植
参考: http://caiming1987612.blog.163.com/blog/static/118556676200961752714307/ http://blog.chinaunix.net/u1/34474/showart_401078.html http://hi.baidu.com/%D3%F3%C4%E0%C4%EA%B8%E2/blog/item/6256fea7bfceac98d0435819.html 时序图和引脚连接 :http://blog.chinaunix.net/u1/57901/showart_2023852.html (一)打开arch/arm/mach-s3c2440/mach-test
[单片机]
S3C2440 Linux驱动移植——LCD
PC主机:Ubuntu 10.4 和redhat 9.0 目标板:TQ2440开发板 Linux内核:2.6.30 屏幕型号:WXCAT35-TG3#001F 分辨率: 320X240 本文将介绍如何移植LCD设备。 在移植前,先配置下内核,将LCD设备编译进内核。 1.移植 移植LCD设置只须修改位于arch/arm/mach-s3c2440/mach-smdk2440.c中的两个结构体的数据。 1.1 s3c2410fb_display结构 修改后的内容如下: /* LCD driver info */ static struct s3c2410fb_display smdk2440_lcd_
[单片机]
<font color='red'>S3C2440</font> <font color='red'>Linux</font><font color='red'>驱动</font><font color='red'>移植</font>——LCD
LinuxSPI子系统图解和mini2440下的SPI驱动移植
1.SPI子系统框图详解: SPI总线上有两类设备:一类是主控端,通常作为SOC系统的一个子模块出现,比如很多嵌入式MPU中都常常包含SPI模块。一类是受控端,例如一些SPI接口的Flash、传感器等等。主控端是SPI总线的控制者,通过使用SPI协议主动发起SPI总线上的会话。而受控端则被动接受SPI主控端的指令,并作出响应的响应。 如图1所示,linux下的spi子系统主要涉及3个部分:spi核心、spiprotocol驱动和spi主控制器驱动。如图1.1,spi核心负责抽象出所有控制器都具备的功能代码,同时向上为spiprotocol驱动提供接口比如spi_message、spi_transfer,spi_async等,具
[单片机]
<font color='red'>Linux</font>中<font color='red'>SPI</font>子系统图解和mini2440下的<font color='red'>SPI</font><font color='red'>驱动</font><font color='red'>移植</font>
基于ARM9芯片的S3C2440Linux操作系统设计SPI驱动程序
  在嵌入式开发过程中,许多系统通常使用串口驱动来满足通信要求,但在实际应用中,使用SPI通信方式会更加高效和快捷[2]。SPI接口是一种高速、高效的串行接口技术,因而SPI设备在数据通信应用中十分方便[3]。本文基于ARM9芯片的S3C2440和Linux操作系统,设计了一种SPI驱动程序,该驱动程序功能可靠灵活、易于移植,可应用于多种嵌入式平台,实现ARM与设备之间的通信。   1 硬件说明   1.1 S3C2440开发平台   采用三星公司的SoC芯片S3C2440[4]作为核心处理器,主频为400 MHz,并与64 MB SDRAM和64 MB NAND Flash共同组成核心部分。此外,该平台也为用户提供了大量的通
[单片机]
基于ARM9芯片的<font color='red'>S3C2440</font>和<font color='red'>Linux</font>操作系统设计<font color='red'>SPI</font><font color='red'>驱动</font>程序
S3C2440之NAND FLASH移植到S3C2410的驱动分析
S3C2440A的NAND FLASH驱动移植到S3C2410还是有些不一样的,最主要的这两个IC的NAND FLASH的寄存器有些不一样,请看下面的两者之间不同: // // Copyright (c) Microsoft Corporation. All rights reserved. // // // Use of this source code is subject to the terms of the Microsoft end-user // license agreement (EULA) under which you licensed this SOFTWARE PRODUC
[单片机]
基于S3C2440的DM9000网卡驱动移植
0 引言   以太网(Ethernet)是一种计算机局域网组网技术。在局域网中,多个节点是共享传输介质的,这就必须由某种机制来决定某个时刻哪个设备占用传输介质来传输数据,因此,局域网的链路层要有介质访问控制的功能,即数据链路层分为逻辑链路控制LLC子层和介质访问控制MAC子层 。   本文主要研究基于S3C2440的DM9000网卡驱动的移植。网络对于嵌入式系统来说是必不可少的,但是S3C2440没有集成以太网接口,所以要想使S3C2440具备以太网的功能,就必须扩展网卡接口。本文选择外接DM9000,使其可以与以太网相连接,并完成DM9000网卡驱动的移植。 1 Linux网络驱动层次   Linux网络驱动可以划分为4层,
[单片机]
S3C2440 音频解码芯片WM8976声卡驱动移植 (三十一)
本节学习: 分析linux中的OOS声卡系统 修改s3c2410-uda1341.c的控制部分,移植wm8976声卡 使用madplay应用程序播放mp3 本节常用英语单词: volume:音量,dsp:数字信号处理(Digital Signal Processing),mixer:混音器,unit:单位,个体 1、声音三要素 采样频率 音频采样率是指录音设备在一秒钟内对声音信号的采样次数,常用的采样率有: 8KHz ---电话所用采样率,对于人的说话已经足够清楚 22.05KHz ---无线电广播所用采样率 32KHz ---miniDV数码视频、DAT所用采样率 44.1KHz ---音频CD,也常用于
[单片机]
<font color='red'>S3C2440</font> 音频解码芯片WM8976声卡<font color='red'>驱动</font><font color='red'>移植</font> (三十一)
基于ARM v7 Cortex A8的开发平台
一、ARM Cortex处理器概述 随着嵌入式技术应用领域的不断扩展,对嵌入式系统的要求越来越高,而作为嵌入式系统核心的微处理器也面临日益严竣的挑战。ARM公司从成立以来,一直以知识产权(IP,Intelligence Property)提供者的身份出售知识产权,在32位RISC CPU开发领域中不断取得突破,其设计的微处理器结构已经从v3发展到现在的v7。ARMv7架构是在ARMv6架构的基础上诞生的。该架构采用了Thumb-2技术,它是在ARM的Thumb代码压缩技术的基础上发展起来的,并且保持了对现存ARM解决方案的完整的代码兼容性。Thumb-2技术比纯32位代码少使用31%的内存,减小了系统开销,同时能够提供比已有的基于
[单片机]
基于ARM v7 <font color='red'>Cortex</font> <font color='red'>A8</font>的开发平台
小广播
最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

厂商技术中心

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

 
机器人开发圈

电子工程世界版权所有 京ICP证060456号 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2026 EEWORLD.com.cn, Inc. All rights reserved