当前位置: 首页 > news >正文

linux 主机驱动(SPI)与外设驱动分离的设计思想

一、        主机驱动与外设驱动分离

        Linux中的SPI、I2c、USB等子系统都利用了典型的把主机驱动和外设驱动分离的想法,让主机端负责产生总线上的传输波形,而外设端只是通过标准的API来让主机端以适当的波形访问自身。因此这里涉及了4个软件模块:

        1)主机端的驱动。根据具体的I2c、SPI、USB等控制器的硬件的手册,操作具体的i2c、SPI、USB等控制器,产生总线的各种波形。

        2)连接主机和外设的纽带。外设不直接调用主机端的驱动来产生波形,而是调一个标准的API。由这个标准的API把这个波形的传输请求间接“转发”给具体的主机端驱动。当然,在这里,最好把关于波形的描述也以某种数据结构标准化。

        3)外设端的驱动。外设接在I2c、SPI、USB这样的总线上,但是它们本身可以是触摸屏、网卡、声卡或者任意一种类型的设备。我们在相关的i2c_driver、spi_driver、usb_driver这种xxx_driver的probe()函数中注册具体的类型。当这些外设要去i2c、spi、usb等去访问它的时候,它调用“连接主机和外设的纽带”模块的标准API。

        4)板级逻辑。板级逻辑用来描述主机和外设是如何互联,它相当于一个“路由表”。假设板子上有多个SPI控制器和多个SPI外设,那究竟谁接在谁上面?管理互联关系,既不是主机端的责任,也不是外设端的责任,这属于板级逻辑的责任。这部分通常在arch/arm/mach-xx或者arch/arm/boot/dts下面。

        什么叫良好的软件设计?一言以蔽之,让正确的代码出现在正确的位置。不要在错误的时间、错误的地点,编写一段错位的代码。在LKML中,关于代码出现在错误的位置,常见的台词是代码“out of place”。

        

二、        linux SPI主机和设备驱动

        在linux中,用代码的spi_maste结构体来描述一个spi主机控制器驱动,其主要成员是主机控制器的序号(系统中可能存在多个SPI主机控制器)、片选数量、SPI模式、时钟设置用到的和数据传输用到的函数等。

        

 521 struct spi_controller {522     struct device   dev;523 524     struct list_head list;525 526     /*527      * Other than negative (== assign one dynamically), bus_num is fully528      * board-specific. Usually that simplifies to being SoC-specific.529      * example: one SoC has three SPI controllers, numbered 0..2,530      * and one board's schematics might show it using SPI-2. Software531      * would normally use bus_num=2 for that controller.532      */533     s16         bus_num;534 535     /*536      * Chipselects will be integral to many controllers; some others537      * might use board-specific GPIOs.538      */539     u16         num_chipselect;540 541     /* Some SPI controllers pose alignment requirements on DMAable542      * buffers; let protocol drivers know about these requirements.543      */544     u16         dma_alignment;545 546     /* spi_device.mode flags understood by this controller driver */547     u32         mode_bits;548 549     /* spi_device.mode flags override flags for this controller */550     u32         buswidth_override_bits;551 552     /* Bitmask of supported bits_per_word for transfers */553     u32         bits_per_word_mask;554 #define SPI_BPW_MASK(bits) BIT((bits) - 1)555 #define SPI_BPW_RANGE_MASK(min, max) GENMASK((max) - 1, (min) - 1)556 557     /* Limits on transfer speed */558     u32         min_speed_hz;559     u32         max_speed_hz;560 561     /* Other constraints relevant to this driver */562     u16         flags;563 #define SPI_CONTROLLER_HALF_DUPLEX  BIT(0)  /* Can't do full duplex */564 #define SPI_CONTROLLER_NO_RX        BIT(1)  /* Can't do buffer read */565 #define SPI_CONTROLLER_NO_TX        BIT(2)  /* Can't do buffer write */566 #define SPI_CONTROLLER_MUST_RX      BIT(3)  /* Requires rx */567 #define SPI_CONTROLLER_MUST_TX      BIT(4)  /* Requires tx */568 #define SPI_CONTROLLER_GPIO_SS      BIT(5)  /* GPIO CS must select slave */569 #define SPI_CONTROLLER_SUSPENDED    BIT(6)  /* Currently suspended */570 571     /* Flag indicating if the allocation of this struct is devres-managed */572     bool            devm_allocated;573 574     union {575         /* Flag indicating this is an SPI slave controller */576         bool            slave;577         /* Flag indicating this is an SPI target controller */578         bool            target;579     };580 581     /*582      * On some hardware transfer / message size may be constrained583      * the limit may depend on device transfer settings.584      */585     size_t (*max_transfer_size)(struct spi_device *spi);586     size_t (*max_message_size)(struct spi_device *spi);587 588     /* I/O mutex */589     struct mutex        io_mutex;590 591     /* Used to avoid adding the same CS twice */592     struct mutex        add_lock;593 594     /* Lock and mutex for SPI bus locking */595     spinlock_t      bus_lock_spinlock;596     struct mutex        bus_lock_mutex;597 598     /* Flag indicating that the SPI bus is locked for exclusive use */599     bool            bus_lock_flag;600 601     /*602      * Setup mode and clock, etc (SPI driver may call many times).603      *604      * IMPORTANT:  this may be called when transfers to another605      * device are active.  DO NOT UPDATE SHARED REGISTERS in ways606      * which could break those transfers.607      */608     int         (*setup)(struct spi_device *spi);609 610     /*611      * set_cs_timing() method is for SPI controllers that supports612      * configuring CS timing.613      *614      * This hook allows SPI client drivers to request SPI controllers615      * to configure specific CS timing through spi_set_cs_timing() after616      * spi_setup().617      */618     int (*set_cs_timing)(struct spi_device *spi);619 620     /*621      * Bidirectional bulk transfers622      *623      * + The transfer() method may not sleep; its main role is624      *   just to add the message to the queue.625      * + For now there's no remove-from-queue operation, or626      *   any other request management627      * + To a given spi_device, message queueing is pure FIFO628      *629      * + The controller's main job is to process its message queue,630      *   selecting a chip (for masters), then transferring data631      * + If there are multiple spi_device children, the i/o queue632      *   arbitration algorithm is unspecified (round robin, FIFO,633      *   priority, reservations, preemption, etc)634      *635      * + Chipselect stays active during the entire message636      *   (unless modified by spi_transfer.cs_change != 0).637      * + The message transfers use clock and SPI mode parameters638      *   previously established by setup() for this device639      */640     int         (*transfer)(struct spi_device *spi,641                         struct spi_message *mesg);642 643     /* Called on release() to free memory provided by spi_controller */644     void            (*cleanup)(struct spi_device *spi);645 646     /*647      * Used to enable core support for DMA handling, if can_dma()648      * exists and returns true then the transfer will be mapped649      * prior to transfer_one() being called.  The driver should650      * not modify or store xfer and dma_tx and dma_rx must be set651      * while the device is prepared.652      */653     bool            (*can_dma)(struct spi_controller *ctlr,654                        struct spi_device *spi,655                        struct spi_transfer *xfer);656     struct device *dma_map_dev;657     struct device *cur_rx_dma_dev;658     struct device *cur_tx_dma_dev;659 660     /*661      * These hooks are for drivers that want to use the generic662      * controller transfer queueing mechanism. If these are used, the663      * transfer() function above must NOT be specified by the driver.664      * Over time we expect SPI drivers to be phased over to this API.665      */666     bool                queued;667     struct kthread_worker       *kworker;668     struct kthread_work     pump_messages;669     spinlock_t          queue_lock;670     struct list_head        queue;671     struct spi_message      *cur_msg;672     struct completion               cur_msg_completion;673     bool                cur_msg_incomplete;674     bool                cur_msg_need_completion;675     bool                busy;676     bool                running;677     bool                rt;678     bool                auto_runtime_pm;679     bool                cur_msg_mapped;680     char                last_cs;681     bool                last_cs_mode_high;682     bool                            fallback;683     struct completion               xfer_completion;684     size_t              max_dma_len;685 686     int (*prepare_transfer_hardware)(struct spi_controller *ctlr);687     int (*transfer_one_message)(struct spi_controller *ctlr,688                     struct spi_message *mesg);689     int (*unprepare_transfer_hardware)(struct spi_controller *ctlr);690     int (*prepare_message)(struct spi_controller *ctlr,691                    struct spi_message *message);692     int (*unprepare_message)(struct spi_controller *ctlr,693                  struct spi_message *message);694     union {695         int (*slave_abort)(struct spi_controller *ctlr);696         int (*target_abort)(struct spi_controller *ctlr);697     };698 699     /*700      * These hooks are for drivers that use a generic implementation701      * of transfer_one_message() provided by the core.702      */703     void (*set_cs)(struct spi_device *spi, bool enable);704     int (*transfer_one)(struct spi_controller *ctlr, struct spi_device *spi,705                 struct spi_transfer *transfer);706     void (*handle_err)(struct spi_controller *ctlr,707                struct spi_message *message);708 709     /* Optimized handlers for SPI memory-like operations. */710     const struct spi_controller_mem_ops *mem_ops;711     const struct spi_controller_mem_caps *mem_caps;712 713     /* GPIO chip select */714     struct gpio_desc    **cs_gpiods;715     bool            use_gpio_descriptors;716     s8          unused_native_cs;717     s8          max_native_cs;718 719     /* Statistics */720     struct spi_statistics __percpu  *pcpu_statistics;721 722     /* DMA channels for use with core dmaengine helpers */723     struct dma_chan     *dma_tx;724     struct dma_chan     *dma_rx;725 726     /* Dummy data for full duplex devices */727     void            *dummy_rx;728     void            *dummy_tx;729 730     int (*fw_translate_cs)(struct spi_controller *ctlr, unsigned cs);731 732     /*733      * Driver sets this field to indicate it is able to snapshot SPI734      * transfers (needed e.g. for reading the time of POSIX clocks)735      */736     bool            ptp_sts_supported;737 738     /* Interrupt enable state during PTP system timestamping */739     unsigned long       irq_flags;740 741     /* Flag for enabling opportunistic skipping of the queue in spi_sync */742     bool            queue_empty;743     bool            must_async;744 };

        分配、注册和注销SPI主机的API由SPI核心提供:

     

        在linux中,用代码清单的spi_driver结构体来描述一个SPI外设驱动,这个外设驱动可以认为是spi_master的客户端驱动。

 325 struct spi_driver {326     const struct spi_device_id *id_table;327     int         (*probe)(struct spi_device *spi);328     void            (*remove)(struct spi_device *spi);329     void            (*shutdown)(struct spi_device *spi);330     struct device_driver    driver;331 };

        可以看出,spi_driver结构体和platform_driver结构体有极大的相似性,都有probe()、remove()、suspend()、resume()这样都接口和device_driver都实例。这几乎是一切客户端驱动常模板。 

        在SPI外设驱动中,当通过SPI总线进行数据传输都时候,使用了一套与CPU无关的统一的接口。这套接口的第1个关键数据结构就是spi_transfer,它用于描述SPI传输,如下所示:

1030 struct spi_transfer {
1031     /*
1032      * It's okay if tx_buf == rx_buf (right?).
1033      * For MicroWire, one buffer must be NULL.
1034      * Buffers must work with dma_*map_single() calls, unless
1035      * spi_message.is_dma_mapped reports a pre-existing mapping.
1036      */
1037     const void  *tx_buf;
1038     void        *rx_buf;
1039     unsigned    len;
1040 
1041 #define SPI_TRANS_FAIL_NO_START BIT(0)
1042     u16     error;
1043 
1044     dma_addr_t  tx_dma;
1045     dma_addr_t  rx_dma;
1046     struct sg_table tx_sg;
1047     struct sg_table rx_sg;
1048 
1049     unsigned    dummy_data:1;
1050     unsigned    cs_off:1;
1051     unsigned    cs_change:1;
1052     unsigned    tx_nbits:3;
1053     unsigned    rx_nbits:3;
1054     unsigned    timestamped:1;
1055 #define SPI_NBITS_SINGLE    0x01 /* 1-bit transfer */
1056 #define SPI_NBITS_DUAL      0x02 /* 2-bit transfer */
1057 #define SPI_NBITS_QUAD      0x04 /* 4-bit transfer */
1058     u8      bits_per_word;
1059     struct spi_delay    delay;
1060     struct spi_delay    cs_change_delay;
1061     struct spi_delay    word_delay;
1062     u32     speed_hz;
1063 
1064     u32     effective_speed_hz;
1065 
1066     unsigned int    ptp_sts_word_pre;
1067     unsigned int    ptp_sts_word_post;
1068 
1069     struct ptp_system_timestamp *ptp_sts;
1070 
1071     struct list_head transfer_list;
1072 };

        而一次完整的SPI传输流程可能不是只包含spi_transfer,它可能包含一个或多个spi_transfer,这些spi_transfer最终通过spi_message组织在一起。

1107 struct spi_message {
1108     struct list_head    transfers;
1109 
1110     struct spi_device   *spi;
1111 
1112     unsigned        is_dma_mapped:1;
1113 
1114     /* spi_prepare_message() was called for this message */
1115     bool            prepared;
1116 
1117     /*
1118      * REVISIT: we might want a flag affecting the behavior of the
1119      * last transfer ... allowing things like "read 16 bit length L"
1120      * immediately followed by "read L bytes".  Basically imposing
1121      * a specific message scheduling algorithm.
1122      *
1123      * Some controller drivers (message-at-a-time queue processing)
1124      * could provide that as their default scheduling algorithm.  But
1125      * others (with multi-message pipelines) could need a flag to
1126      * tell them about such special cases.
1127      */
1128 
1129     /* Completion is reported through a callback */
1130     int         status;
1131     void            (*complete)(void *context);
1132     void            *context;
1133     unsigned        frame_length;
1134     unsigned        actual_length;
1135 
1136     /*
1137      * For optional use by whatever driver currently owns the
1138      * spi_message ...  between calls to spi_async and then later
1139      * complete(), that's the spi_controller controller driver.
1140      */
1141     struct list_head    queue;
1142     void            *state;
1143 
1144     /* List of spi_res resources when the SPI message is processed */
1145     struct list_head        resources;
1146 
1147     /* For embedding transfers into the memory of the message */
1148     struct spi_transfer t[];
1149 };

        通过spi_message_init()可以初始化spi_message,而将spi_transfer添加到spi_message队列的方法则是:

     

        发起一次spi_message的传输有同步和异步两种方式,使用同步API时,会阻塞等待这个消息被处理完。同步操作时使用的API是:
 

        使用异步API时,不会阻塞等待这个消息被处理完,但是可以在spi_message的complete字段挂接一个回调函数,当消息被处理完成后,该函数会被调用。在异步操作时使用的API是:

       

        下图是非常典型的初始化spi_transfer、spi_message并进行spi数据传输的例子,同时spi_write()、spi_read()也是spi核心层的两个通用快捷API,在SPI外设驱动中可以直接调用它们进行简单的纯写和纯读操作。

         

        SPI主机控制器驱动位于driver/spi/,这些驱动的主体是实现了spi_master的transfer()、transfer_one()、setup()这样的成员函数,当然,也可能是实现spi_bitbang的txrx_bufs()、setup_transfer()、chipselect()这样的成员函数。 下图SPI主机端驱动完成的波形传输

摘取了./drivers/spi/spi-pl022.c的部分代码。

1576 static int pl022_transfer_one_message(struct spi_controller *host,
1577                       struct spi_message *msg)
1578 {
1579     struct pl022 *pl022 = spi_controller_get_devdata(host);
1580 
1581     /* Initial message state */
1582     pl022->cur_msg = msg;
1583     msg->state = STATE_START;
1584 
1585     pl022->cur_transfer = list_entry(msg->transfers.next,
1586                      struct spi_transfer, transfer_list);
1587 
1588     /* Setup the SPI using the per chip configuration */
1589     pl022->cur_chip = spi_get_ctldata(msg->spi);
1590     pl022->cur_cs = spi_get_chipselect(msg->spi, 0);
1591     /* This is always available but may be set to -ENOENT */
1592     pl022->cur_gpiod = spi_get_csgpiod(msg->spi, 0);
1593 
1594     restore_state(pl022);
1595     flush(pl022);
1596 
1597     if (pl022->cur_chip->xfer_type == POLLING_TRANSFER)
1598         do_polling_transfer(pl022);
1599     else
1600         do_interrupt_dma_transfer(pl022);
1601 
1602     return 0;
1603 }1840 static int pl022_setup(struct spi_device *spi)
1841 {
1842     struct pl022_config_chip const *chip_info;
1843     struct pl022_config_chip chip_info_dt;
1844     struct chip_data *chip;
1845     struct ssp_clock_params clk_freq = { .cpsdvsr = 0, .scr = 0};
1846     int status = 0;
1847     struct pl022 *pl022 = spi_controller_get_devdata(spi->controller);
1848     unsigned int bits = spi->bits_per_word;
1849     u32 tmp;
1850     struct device_node *np = spi->dev.of_node;
..........
..........
2027     /* Stuff that is common for all versions */
2028     if (spi->mode & SPI_CPOL)
2029         tmp = SSP_CLK_POL_IDLE_HIGH;
2030     else
2031         tmp = SSP_CLK_POL_IDLE_LOW;
2032     SSP_WRITE_BITS(chip->cr0, tmp, SSP_CR0_MASK_SPO, 6);
2033 
2034     if (spi->mode & SPI_CPHA)
2035         tmp = SSP_CLK_SECOND_EDGE;
2036     else
2037         tmp = SSP_CLK_FIRST_EDGE;
2038     SSP_WRITE_BITS(chip->cr0, tmp, SSP_CR0_MASK_SPH, 7);
2039 
2040     SSP_WRITE_BITS(chip->cr0, clk_freq.scr, SSP_CR0_MASK_SCR, 8);
...........
...........
2057  err_config_params:
2058     spi_set_ctldata(spi, NULL);
2059     kfree(chip);
2060     return status;
2061 }2101 static int pl022_probe(struct amba_device *adev, const struct amba_id *id)
2102 {
2103     struct device *dev = &adev->dev;
2104     struct pl022_ssp_controller *platform_info =
2105             dev_get_platdata(&adev->dev);
2106     struct spi_controller *host;
2107     struct pl022 *pl022 = NULL; /*Data for this driver */
2108     int status = 0;
..........
..........
2133     /*
2134      * Bus Number Which has been Assigned to this SSP controller
2135      * on this board
2136      */
2137     host->bus_num = platform_info->bus_id;
2138     host->cleanup = pl022_cleanup;
2139     host->setup = pl022_setup;
2140     host->auto_runtime_pm = true;
2141     host->transfer_one_message = pl022_transfer_one_message;
2142     host->unprepare_transfer_hardware = pl022_unprepare_transfer_hardware;
2143     host->rt = platform_info->rt;
2144     host->dev.of_node = dev->of_node;
2145     host->use_gpio_descriptors = true;
2146 
2147     /*
..........
..........
2239  err_spi_register:
2240     if (platform_info->enable_dma)
2241         pl022_dma_remove(pl022);
2242  err_no_irq:
2243     clk_disable_unprepare(pl022->clk);
2244  err_no_clk_en:
2245  err_no_clk:
2246  err_no_ioremap:
2247     amba_release_regions(adev);
2248  err_no_ioregion:
2249     spi_controller_put(host);
2250     return status;
2251 }

        SPI外设驱动遍布内核的drivers、sound的各个子目录之下,SPI只是一种总线,spi_driver的作用只是将SPI外设挂接在该总线上,因此在spi_driver的probe()函数中,将注册SPI外设本身所属设备驱动的类型。

        和platform_driver对应一个platform_device一样,spi_driver也对应一个spi_device,platform_device需要在BSP的板文件中添加板信息数据,而spi_device也同样需要。spi_device的板信息用spi_board_info结构体描述,该结构体记录着spi外设使用的主机控制器序号、片选序号、数据比特率、SPI传输模式(即CPOL、CPHA)等。诺基亚770上的两个SPI设备的板信息数据如下图所示,位于板文件的arch/arm/mach-omap1/board-nokia770.c中。

151 static struct spi_board_info nokia770_spi_board_info[] __initdata = {
152     [0] = {
153         .modalias       = "lcd_mipid",
154         .bus_num        = 2,
155         .chip_select    = 3,
156         .max_speed_hz   = 12000000,
157         .platform_data  = &nokia770_mipid_platform_data,
158         .swnode         = &nokia770_mipid_swnode,
159     },
160     [1] = {
161         .modalias       = "ads7846",
162         .bus_num        = 2,
163         .chip_select    = 0,
164         .max_speed_hz   = 2500000,
165         .swnode         = &nokia770_ads7846_swnode,
166     },
167 };

        在linux启动过程中,在机器的init_machine()函数中,会通过如下语句注册这些spi_board_info:

        这一点和启动时通过platform_add_device()添加platform_device非常相似。

        ARM Linux 3.x之后的内核在改为设备树,不再需要在arch/arm/mach-xxx中编码SPI的板级信息了。而倾向于在SPI控制器节点下填写子节点,如下图通过设备树添加SPI外设。

        

         

        

http://www.dtcms.com/a/331940.html

相关文章:

  • 把大模型当“温度计”——基于 LLM 的分布式系统异常根因定位实战
  • 企业可商用的conda:「Miniforge」+「conda-forge」
  • Data Augmentation数据增强
  • 快速部署一个鉴黄服务
  • Android 项目:画图白板APP开发(二)——历史点、数学方式推导点
  • SQL详细语法教程(三)mysql的函数知识
  • 区块链 + 域名Web3时代域名投资的新风口(上)
  • Gemma 3 多模态推理 通过vllm运行Gemma-3-27B-IT模型的推理服务
  • 【系统安装】虚拟机中安装win10IOT企业版系统记录
  • 解决安装 scikit-learn==1.3.1 时出现的版本匹配错误
  • PHP 开发全解析:从基础到实战的进阶之路
  • sFlow原理与配置
  • Java面试场景题大全精简版
  • MySql——聚簇索引(主键索引)和非聚簇索索引(非主键索引)引区别(即聚集索引和非聚集索引区别)
  • MyBatis学习总结(六)
  • 【面板数据】各省及市省级非物质文化遗产数据合集(2005-2024年)
  • 《嵌入式 C 语言编码规范与工程实践个人笔记》参考华为C语言规范标准
  • 解锁 Docker:一场从入门到源码的趣味解谜之旅
  • 卸载python遇到msi文件权限不足
  • Python闭包详解:理解闭包与可变类型和不可变类型的关系
  • 新手如何高效运营亚马逊跨境电商:从传统SP广告到DeepBI智能策略
  • docker 容器管理入门教程
  • 身份全景图
  • Encoder-Decoder Model编码器-解码器模型
  • 【学习笔记】Java并发编程的艺术——第4章 Java并发编程基础
  • CMake笔记:Alias Target在哪些地方可以使用
  • 傅里叶变换+attention机制,深耕深度学习领域
  • shellgpt
  • Linux计划任务
  • 当GitHub宕机时,我们如何协作?