Linux驱动开发十九.SPI驱动开发

SPI是一种非常常用的串行通讯协议,今天我们过一遍如何在Linux下试下SPI驱动的编写,并且借助一个IC来实现ARM主机通过SPI和外设之间进行数据交互。

Linux下的SPI框架

SPI在Linux中也是以一种Bus呈现出来的。和前面我们做的Platform总线一样,只不过platform是一种虚拟的总线而SPI是一种实际的总线。在内核中,如果要实现SPI协议来进行数据传输,是要通过SPI的主机控制器驱动和另一个设备驱动。回顾一下我们前面写的SPI裸机驱动(好像还没发布),是有一个bsp_spi.c(.h)还有个icm20806.c。前面的bsp_spi就相当于主机控制器驱动,所有的SPI外设都要通过这个主机控制器驱动去和我们的SOC进行通讯,而后面的icm20806.c就是设备驱动,每个SPI外设就对应一个单独的设备驱动。不管是什么SPI设备,主机驱动应该都是一样的,我们要做的就是各个外设的SPI设备驱动。

SPI主机驱动

SPI的主机驱动正常来说都是由半导体厂商直接提供给我们的,是由一个结构体spi_master来描述的(include/linux/spi/spi.h)。

  1 /**
  2  * struct spi_master - interface to SPI master controller
  3  * @dev: device interface to this driver
  4  * @list: link with the global spi_master list
  5  * @bus_num: board-specific (and often SOC-specific) identifier for a
  6  *    given SPI controller.
  7  * @num_chipselect: chipselects are used to distinguish individual
  8  *    SPI slaves, and are numbered from zero to num_chipselects.
  9  *    each slave has a chipselect signal, but it's common that not
 10  *    every chipselect is connected to a slave.
 11  * @dma_alignment: SPI controller constraint on DMA buffers alignment.
 12  * @mode_bits: flags understood by this controller driver
 13  * @bits_per_word_mask: A mask indicating which values of bits_per_word are
 14  *    supported by the driver. Bit n indicates that a bits_per_word n+1 is
 15  *    supported. If set, the SPI core will reject any transfer with an
 16  *    unsupported bits_per_word. If not set, this value is simply ignored,
 17  *    and it's up to the individual driver to perform any validation.
 18  * @min_speed_hz: Lowest supported transfer speed
 19  * @max_speed_hz: Highest supported transfer speed
 20  * @flags: other constraints relevant to this driver
 21  * @bus_lock_spinlock: spinlock for SPI bus locking
 22  * @bus_lock_mutex: mutex for SPI bus locking
 23  * @bus_lock_flag: indicates that the SPI bus is locked for exclusive use
 24  * @setup: updates the device mode and clocking records used by a
 25  *    device's SPI controller; protocol code may call this.  This
 26  *    must fail if an unrecognized or unsupported mode is requested.
 27  *    It's always safe to call this unless transfers are pending on
 28  *    the device whose settings are being modified.
 29  * @transfer: adds a message to the controller's transfer queue.
 30  * @cleanup: frees controller-specific state
 31  * @can_dma: determine whether this master supports DMA
 32  * @queued: whether this master is providing an internal message queue
 33  * @kworker: thread struct for message pump
 34  * @kworker_task: pointer to task for message pump kworker thread
 35  * @pump_messages: work struct for scheduling work to the message pump
 36  * @queue_lock: spinlock to syncronise access to message queue
 37  * @queue: message queue
 38  * @idling: the device is entering idle state
 39  * @cur_msg: the currently in-flight message
 40  * @cur_msg_prepared: spi_prepare_message was called for the currently
 41  *                    in-flight message
 42  * @cur_msg_mapped: message has been mapped for DMA
 43  * @xfer_completion: used by core transfer_one_message()
 44  * @busy: message pump is busy
 45  * @running: message pump is running
 46  * @rt: whether this queue is set to run as a realtime task
 47  * @auto_runtime_pm: the core should ensure a runtime PM reference is held
 48  *                   while the hardware is prepared, using the parent
 49  *                   device for the spidev
 50  * @max_dma_len: Maximum length of a DMA transfer for the device.
 51  * @prepare_transfer_hardware: a message will soon arrive from the queue
 52  *    so the subsystem requests the driver to prepare the transfer hardware
 53  *    by issuing this call
 54  * @transfer_one_message: the subsystem calls the driver to transfer a single
 55  *    message while queuing transfers that arrive in the meantime. When the
 56  *    driver is finished with this message, it must call
 57  *    spi_finalize_current_message() so the subsystem can issue the next
 58  *    message
 59  * @unprepare_transfer_hardware: there are currently no more messages on the
 60  *    queue so the subsystem notifies the driver that it may relax the
 61  *    hardware by issuing this call
 62  * @set_cs: set the logic level of the chip select line.  May be called
 63  *          from interrupt context.
 64  * @prepare_message: set up the controller to transfer a single message,
 65  *                   for example doing DMA mapping.  Called from threaded
 66  *                   context.
 67  * @transfer_one: transfer a single spi_transfer.
 68  *                  - return 0 if the transfer is finished,
 69  *                  - return 1 if the transfer is still in progress. When
 70  *                    the driver is finished with this transfer it must
 71  *                    call spi_finalize_current_transfer() so the subsystem
 72  *                    can issue the next transfer. Note: transfer_one and
 73  *                    transfer_one_message are mutually exclusive; when both
 74  *                    are set, the generic subsystem does not call your
 75  *                    transfer_one callback.
 76  * @handle_err: the subsystem calls the driver to handle an error that occurs
 77  *        in the generic implementation of transfer_one_message().
 78  * @unprepare_message: undo any work done by prepare_message().
 79  * @cs_gpios: Array of GPIOs to use as chip select lines; one per CS
 80  *    number. Any individual value may be -ENOENT for CS lines that
 81  *    are not GPIOs (driven by the SPI controller itself).
 82  * @dma_tx: DMA transmit channel
 83  * @dma_rx: DMA receive channel
 84  * @dummy_rx: dummy receive buffer for full-duplex devices
 85  * @dummy_tx: dummy transmit buffer for full-duplex devices
 86  *
 87  * Each SPI master controller can communicate with one or more @spi_device
 88  * children.  These make a small bus, sharing MOSI, MISO and SCK signals
 89  * but not chip select signals.  Each device may be configured to use a
 90  * different clock rate, since those shared signals are ignored unless
 91  * the chip is selected.
 92  *
 93  * The driver for an SPI controller manages access to those devices through
 94  * a queue of spi_message transactions, copying data between CPU memory and
 95  * an SPI slave device.  For each such message it queues, it calls the
 96  * message's completion function when the transaction completes.
 97  */
 98 struct spi_master {
 99     struct device    dev;
100 
101     struct list_head list;
102 
103     /* other than negative (== assign one dynamically), bus_num is fully
104      * board-specific.  usually that simplifies to being SOC-specific.
105      * example:  one SOC has three SPI controllers, numbered 0..2,
106      * and one board's schematics might show it using SPI-2.  software
107      * would normally use bus_num=2 for that controller.
108      */
109     s16            bus_num;
110 
111     /* chipselects will be integral to many controllers; some others
112      * might use board-specific GPIOs.
113      */
114     u16            num_chipselect;
115 
116     /* some SPI controllers pose alignment requirements on DMAable
117      * buffers; let protocol drivers know about these requirements.
118      */
119     u16            dma_alignment;
120 
121     /* spi_device.mode flags understood by this controller driver */
122     u16            mode_bits;
123 
124     /* bitmask of supported bits_per_word for transfers */
125     u32            bits_per_word_mask;
126 #define SPI_BPW_MASK(bits) BIT((bits) - 1)
127 #define SPI_BIT_MASK(bits) (((bits) == 32) ? ~0U : (BIT(bits) - 1))
128 #define SPI_BPW_RANGE_MASK(min, max) (SPI_BIT_MASK(max) - SPI_BIT_MASK(min - 1))
129 
130     /* limits on transfer speed */
131     u32            min_speed_hz;
132     u32            max_speed_hz;
133 
134     /* other constraints relevant to this driver */
135     u16            flags;
136 #define SPI_MASTER_HALF_DUPLEX    BIT(0)        /* can't do full duplex */
137 #define SPI_MASTER_NO_RX    BIT(1)        /* can't do buffer read */
138 #define SPI_MASTER_NO_TX    BIT(2)        /* can't do buffer write */
139 #define SPI_MASTER_MUST_RX      BIT(3)        /* requires rx */
140 #define SPI_MASTER_MUST_TX      BIT(4)        /* requires tx */
141 
142     /* lock and mutex for SPI bus locking */
143     spinlock_t        bus_lock_spinlock;
144     struct mutex        bus_lock_mutex;
145 
146     /* flag indicating that the SPI bus is locked for exclusive use */
147     bool            bus_lock_flag;
148 
149     /* Setup mode and clock, etc (spi driver may call many times).
150      *
151      * IMPORTANT:  this may be called when transfers to another
152      * device are active.  DO NOT UPDATE SHARED REGISTERS in ways
153      * which could break those transfers.
154      */
155     int            (*setup)(struct spi_device *spi);
156 
157     /* bidirectional bulk transfers
158      *
159      * + The transfer() method may not sleep; its main role is
160      *   just to add the message to the queue.
161      * + For now there's no remove-from-queue operation, or
162      *   any other request management
163      * + To a given spi_device, message queueing is pure fifo
164      *
165      * + The master's main job is to process its message queue,
166      *   selecting a chip then transferring data
167      * + If there are multiple spi_device children, the i/o queue
168      *   arbitration algorithm is unspecified (round robin, fifo,
169      *   priority, reservations, preemption, etc)
170      *
171      * + Chipselect stays active during the entire message
172      *   (unless modified by spi_transfer.cs_change != 0).
173      * + The message transfers use clock and SPI mode parameters
174      *   previously established by setup() for this device
175      */
176     int            (*transfer)(struct spi_device *spi,
177                         struct spi_message *mesg);
178 
179     /* called on release() to free memory provided by spi_master */
180     void            (*cleanup)(struct spi_device *spi);
181 
182     /*
183      * Used to enable core support for DMA handling, if can_dma()
184      * exists and returns true then the transfer will be mapped
185      * prior to transfer_one() being called.  The driver should
186      * not modify or store xfer and dma_tx and dma_rx must be set
187      * while the device is prepared.
188      */
189     bool            (*can_dma)(struct spi_master *master,
190                        struct spi_device *spi,
191                        struct spi_transfer *xfer);
192 
193     /*
194      * These hooks are for drivers that want to use the generic
195      * master transfer queueing mechanism. If these are used, the
196      * transfer() function above must NOT be specified by the driver.
197      * Over time we expect SPI drivers to be phased over to this API.
198      */
199     bool                queued;
200     struct kthread_worker        kworker;
201     struct task_struct        *kworker_task;
202     struct kthread_work        pump_messages;
203     spinlock_t            queue_lock;
204     struct list_head        queue;
205     struct spi_message        *cur_msg;
206     bool                idling;
207     bool                busy;
208     bool                running;
209     bool                rt;
210     bool                auto_runtime_pm;
211     bool                            cur_msg_prepared;
212     bool                cur_msg_mapped;
213     struct completion               xfer_completion;
214     size_t                max_dma_len;
215 
216     int (*prepare_transfer_hardware)(struct spi_master *master);
217     int (*transfer_one_message)(struct spi_master *master,
218                     struct spi_message *mesg);
219     int (*unprepare_transfer_hardware)(struct spi_master *master);
220     int (*prepare_message)(struct spi_master *master,
221                    struct spi_message *message);
222     int (*unprepare_message)(struct spi_master *master,
223                  struct spi_message *message);
224 
225     /*
226      * These hooks are for drivers that use a generic implementation
227      * of transfer_one_message() provied by the core.
228      */
229     void (*set_cs)(struct spi_device *spi, bool enable);
230     int (*transfer_one)(struct spi_master *master, struct spi_device *spi,
231                 struct spi_transfer *transfer);
232     void (*handle_err)(struct spi_master *master,
233                struct spi_message *message);
234 
235     /* gpio chip select */
236     int            *cs_gpios;
237 
238     /* DMA channels for use with core dmaengine helpers */
239     struct dma_chan        *dma_tx;
240     struct dma_chan        *dma_rx;
241 
242     /* dummy data for full duplex devices */
243     void            *dummy_rx;
244     void            *dummy_tx;
245 };
spi_master

整个结构体太大了,这里就不展开了。这里我们先不讲SPI主机驱动的流程了,主要就是通过封装了SPI数据的spi_message结构体通过transfer_one_message函数发送数据。的如果以后有时间我们在把这里展开讲一下。但是不妨碍我们使用SPI的使用,因为SPI协议我们主要是要完成设备驱动。

SPI设备驱动

spi的设备驱动也是由一个结构体来描述的(spi_driver,路径也是include/linux/spi/spi.h)

 1 /**
 2  * struct spi_driver - Host side "protocol" driver
 3  * @id_table: List of SPI devices supported by this driver
 4  * @probe: Binds this driver to the spi device.  Drivers can verify
 5  *    that the device is actually present, and may need to configure
 6  *    characteristics (such as bits_per_word) which weren't needed for
 7  *    the initial configuration done during system setup.
 8  * @remove: Unbinds this driver from the spi device
 9  * @shutdown: Standard shutdown callback used during system state
10  *    transitions such as powerdown/halt and kexec
11  * @driver: SPI device drivers should initialize the name and owner
12  *    field of this structure.
13  *
14  * This represents the kind of device driver that uses SPI messages to
15  * interact with the hardware at the other end of a SPI link.  It's called
16  * a "protocol" driver because it works through messages rather than talking
17  * directly to SPI hardware (which is what the underlying SPI controller
18  * driver does to pass those messages).  These protocols are defined in the
19  * specification for the device(s) supported by the driver.
20  *
21  * As a rule, those device protocols represent the lowest level interface
22  * supported by a driver, and it will support upper level interfaces too.
23  * Examples of such upper levels include frameworks like MTD, networking,
24  * MMC, RTC, filesystem character device nodes, and hardware monitoring.
25  */
26 struct spi_driver {
27     const struct spi_device_id *id_table;
28     int            (*probe)(struct spi_device *spi);
29     int            (*remove)(struct spi_device *spi);
30     void            (*shutdown)(struct spi_device *spi);
31     struct device_driver    driver;
32 };

相较于前面的spi_master来说,这个spi_driver要简单的多。这个driver结构体和我们的platform_driver结构体基本一样,都是在匹配好设备后执行probe函数。所以就是说在驱动入口里我们只需要对这个spi_driver进行加载就可以了。整个基础框架可以参考前面的platform驱动框架

 1 /**
 2  * @file spi框架.c
 3  * @author your name (you@domain.com)
 4  * @brief SPI设备驱动基础框架
 5  * @version 0.1
 6  * @date 2022-09-24
 7  * 
 8  * @copyright Copyright (c) 2022
 9  * 
10  */
11 #include <linux/module.h>
12 #include <linux/kernel.h>
13 #include <linux/init.h>
14 #include <linux/fs.h>
15 #include <linux/uaccess.h>
16 #include <linux/io.h>
17 #include <linux/types.h>
18 #include <linux/cdev.h>
19 #include <linux/device.h>
20 #include <linux/of.h>
21 #include <linux/of_address.h>
22 #include <linux/of_irq.h>
23 #include <linux/gpio.h>
24 #include <linux/of_gpio.h>
25 #include <linux/irq.h>
26 #include <linux/interrupt.h>
27 #include <linux/input.h>
28 #include <linux/spi/spi.h>
29 
30 static int icm20608_probe(struct spi_device *spi)
31 {   int ret = 0;
32 
33     printk("icm20608 probe\r\n");
34     return 0;
35 }
36 
37 static int icm20608_remove(struct spi_device *spi)
38 {
39     int ret = 0;
40     return 0;
41 }
42 
43 /*设备树匹配*/
44 static const struct of_device_id icm20608_of_match[] = {
45     {.compatible = "alientek,icm20608"},
46     {}
47 };
48 
49 /*传统匹配*/
50 static const struct spi_device_id icm20608_id[] = {
51     {"alientek,icm20608",0},
52     {}
53 };
54 
55 struct spi_driver icm20608_driver = {
56     .driver = {
57         .name = "icm20608",
58         .owner = THIS_MODULE,
59         .of_match_table = icm20608_of_match,
60     },
61     .probe = icm20608_probe,
62     .remove = icm20608_remove,
63     .id_table = icm20608_id,
64 };
65 
66 static int __init icm20608_init(void)
67 {
68     int ret = 0;
69     ret = spi_register_driver(&icm20608_driver);
70     printk("ret = %d\r\n",ret);
71     return ret;
72 }
73 
74 static void __exit icm20608_exit(void)
75 {
76     spi_unregister_driver(&icm20608_driver);
77 }
78 
79 module_init(icm20608_init);
80 module_exit(icm20608_exit);
81 MODULE_LICENSE("GPL");
82 MODULE_AUTHOR("AC");

这里就用到了两个函数,一个加载SPI设备驱动,一个卸载SPI设备驱动。

extern int spi_register_driver(struct spi_driver *sdrv);
static inline void spi_unregister_driver(struct spi_driver *sdrv)
{
    if (sdrv)
        driver_unregister(&sdrv->driver);
}

上面这段代码整个过程没什么可说的,下面就是要根据程序中的匹配信息构建设备树信了。

设备树修改

对于SPI设备来说,设备树信息要考虑到两部分,首先是主机的信息配置,主要是pinctrl的配置,第二是设备的信息。可以先回顾一下硬件图

 

 

 

其中IC的中断引脚我们不使用,只使用SPI通讯的4条线(时钟、片选、MISO以及MOSI),对应的SOC上的ECSPI3这组SPI接口(I.MX6UL系列主控上有4组SPI接口)。我们要对这4组信号的GPIO进行配置。

&iomuxc {
    pinctrl-names = "default";
    pinctrl-0 = <&pinctrl_hog_1>;

        /*
        省略信息
        */

        /*SPI3pinctrl配置*/
        pinctrl_ecspi3: ecspi3grp{
            fsl,pins = <
                /*片选信号使用GPIO,在程序里通过软件片选*/
                MX6UL_PAD_UART2_TX_DATA__GPIO1_IO20        0x10B0
                MX6UL_PAD_UART2_RX_DATA__ECSPI3_SCLK       0x10B1
                MX6UL_PAD_UART2_CTS_B__ECSPI3_MOSI         0x10B1
                MX6UL_PAD_UART2_RTS_B__ECSPI3_MISO         0x10b1    
            >;
        };

        /*
        省略信息
        */

和前面的配置一样,ECSPI3的GPIO是属于iomuxc组管理的,所以就把这组gpio的配置完成在iomuxc组里。

SPI外设信息

整个ecspi3的节点是在imx6ul.dtsi这个文件中的,可以看一下定义

 1 ecspi3: ecspi@02010000 {
 2     #address-cells = <1>;
 3     #size-cells = <0>;
 4     compatible = "fsl,imx6ul-ecspi", "fsl,imx51-ecspi";
 5     reg = <0x02010000 0x4000>;
 6     interrupts = <GIC_SPI 33 IRQ_TYPE_LEVEL_HIGH>;
 7     clocks = <&clks IMX6UL_CLK_ECSPI3>,
 8             <&clks IMX6UL_CLK_ECSPI3>;
 9     clock-names = "ipg", "per";
10     dmas = <&sdma 7 7 1>, <&sdma 8 7 2>;
11     dma-names = "rx", "tx";
12     status = "disabled";
13 };

其中寄存器地址0x02010000就是ECSPI3组的首地址。

 

 我们要在开发板对应的设备树文件下添加新外设的信息

 1 /*新添加spi设备信息*/
 2 &ecspi3 {
 3     fsl,spi-num-chipselects = <1>;                                /*1个片选*/
 4     cs-gpio = <&gpio1 20 GPIO_ACTIVE_LOW>;                        /*软件片选 设备树要求是cs-gpios*/
 5     pinctrl-names = "default";
 6     pinctrl-0 = <&pinctrl_ecspi3>;
 7     status = "okay";
 8 
 9     spidev0:icm20608@0 { /*后面@0的0表示SPI接到哪个硬件片选*/
10         reg= <0>;
11         compatible = "alientek,icm20608";
12         spi-max-frequency = <8000000>;    /*时钟频率,单位为Hz*/
13     }; 
14 };

新的外设一定要是在ecspi3这个节点下面的(有可能开发板的设备树文件里没有用到ecspi3,要自己手动添加)。然后新的设备就是spidev0,最后的@0是因为我们的ECSPI接口在硬件定义上可以接4个外设,通过硬件的片选信号去选择,0就表示第1个外设。其他就没什么可说的了其中我们可以参考文档中的信息对设备树文件进行编辑(Documentation/devicetree/bindings/spi/spi-bus.txt)。主要就是匹配的原则一定要和驱动文件中的内容一致。第4行的cs-gpio和文档里的说明是有区别的,文档中说的是cs-gpios,是配合spi_write和spi_read两个函数使用的,这里有一些问题我们回来再说。

修改好设备树文件以后可以启动开发板,可以看到/sys路径下的spi下有了新的设备

 

打印一下of_node里面的compatible,可以看到相关属性,说明SPI设备加载正常。再加载一下驱动基础框架

 

probe函数运行正常,说明驱动框架匹配的也没问题。下面我们主要完成字符设备的添加等功能。

字符设备添加

这个过程没什么可讲的,但是由于我们一直使用platform来构建驱动(特别是MISC杂项驱动,都是内核帮我们完成这个步骤),可能都忘的差不多了。所以要回顾一下

  1 /**
  2  * @file SPI框架.c
  3  * @author your name (you@domain.com)
  4  * @brief SPI设备完整框架
  5  * @version 0.1
  6  * @date 2022-09-24
  7  * 
  8  * @copyright Copyright (c) 2022
  9  * 
 10  */
 11 #include <linux/module.h>
 12 #include <linux/kernel.h>
 13 #include <linux/init.h>
 14 #include <linux/fs.h>
 15 #include <linux/uaccess.h>
 16 #include <linux/io.h>
 17 #include <linux/types.h>
 18 #include <linux/cdev.h>
 19 #include <linux/device.h>
 20 #include <linux/of.h>
 21 #include <linux/of_address.h>
 22 #include <linux/of_irq.h>
 23 #include <linux/gpio.h>
 24 #include <linux/of_gpio.h>
 25 #include <linux/irq.h>
 26 #include <linux/interrupt.h>
 27 #include <linux/input.h>
 28 #include <linux/spi/spi.h>
 29 
 30 
 31 #define ICM20608_CNT            1
 32 #define ICM20608_NAME           "icm20608"
 33 
 34 /*设备结构体*/
 35 struct icm20608_device
 36 {
 37     int major;
 38     int minor;
 39     dev_t dev_id;
 40     struct cdev cdev;
 41     struct class *class;
 42     struct device *device;
 43     int cs_gpio;
 44 
 45     void *private_data;
 46 };
 47 
 48 struct icm20608_device icm20608_dev;
 49 
 50 static int icm20608_open(struct inode *inode, struct file *file)
 51 {
 52     int ret = 0;
 53     return ret;
 54 }
 55 
 56 /*读文件*/
 57 ssize_t icm20608_read(struct file *file, char __user *buf, size_t cnt, loff_t *off)
 58 {
 59     int ret = 0;
 60     return ret;
 61 }
 62 
 63 /*关闭文件*/
 64 static int icm20608_release(struct inode *inode, struct file *file)
 65 {
 66     int ret = 0;
 67     return ret;
 68 }
 69 
 70 /*文件操作集合*/
 71 struct file_operations icm20608_fops = {
 72     .owner = THIS_MODULE,
 73     .open = icm20608_open,
 74     .read = icm20608_read,
 75     .release = icm20608_release,
 76 };
 77 
 78 
 79 /*读多个寄存器*/
 80 static int icm20608_read_regs(struct icm20608_device *dev, u8 reg, u8 *buf, int len)
 81 {
 82     int ret = 0;
 83     return ret;
 84 
 85 }
 86 
 87 /*写多个寄存器*/
 88 static int icm20608_write_regs(struct icm20608_device *dev, u8 reg, u8 *buf,int len)
 89 {
 90     int ret = 0;
 91     return ret;
 92 }
 93 
 94 /*读取单个寄存器*/
 95 static unsigned char icm20608_read_onereg(struct icm20608_device *dev, u8 reg)
 96 {
 97     u8 data = 0;
 98     return data;
 99 }
100 /*写单个寄存器*/
101 static void icm20608_write_onereg(struct icm20608_device *dev, u8 reg, u8 value)
102 {
103 
104 }
105 
106 /*icm20608外设初始化*/
107 void icm20608_device_init(struct icm20608_device *dev)
108 {
109 
110 }
111 
112 
113 static int icm20608_probe(struct spi_device *spi)
114 {   int ret = 0;
115 
116     /*申请字符设备号*/
117     icm20608_dev.major = 0;
118     if(icm20608_dev.major){
119         icm20608_dev.dev_id = MKDEV(icm20608_dev.major,0);
120         ret = register_chrdev_region(icm20608_dev.dev_id,ICM20608_CNT,ICM20608_NAME);
121     }
122     else{
123         ret = alloc_chrdev_region(&icm20608_dev.dev_id,0,ICM20608_CNT,ICM20608_NAME);
124         icm20608_dev.major = MAJOR(icm20608_dev.dev_id);
125         icm20608_dev.minor = MINOR(icm20608_dev.dev_id);
126     }
127 
128     if(ret<0)
129     {
130         printk("icm20608 chrdev_region err\r\n");
131         goto fail_devid;
132     }
133     printk("icm20608 major=%d,minor=%d",icm20608_dev.major,icm20608_dev.minor);
134      
135     /*添加字符设备*/
136     icm20608_dev.cdev.owner = THIS_MODULE;
137     cdev_init(&icm20608_dev.cdev,&icm20608_fops);
138     ret = cdev_add(&icm20608_dev.cdev,icm20608_dev.dev_id,ICM20608_CNT);
139     if(ret<0)
140     {
141         goto fail_cdev;
142     }
143     /*添加类*/
144     icm20608_dev.class = class_create(THIS_MODULE,ICM20608_NAME);
145     if(IS_ERR(icm20608_dev.class))
146     {
147         ret = PTR_ERR(icm20608_dev.class);
148         goto fail_class;
149     }
150 
151     /*添加字符设备*/
152     icm20608_dev.device = device_create(icm20608_dev.class,NULL,
153                                         icm20608_dev.dev_id,NULL,ICM20608_NAME);
154     if(IS_ERR(icm20608_dev.device))
155     {
156         ret = PTR_ERR(icm20608_dev.device);
157         goto fail_device;
158     }
159 
160 
161     /*私有数据设置*/
162     icm20608_dev.private_data = spi;        //spi就是probe函数传进来的spi_device参数
163     return 0;
164 
165 fail_device:
166     class_destroy(icm20608_dev.class);
167 fail_class:
168     cdev_del(&icm20608_dev.cdev);
169 fail_cdev:
170     unregister_chrdev_region(icm20608_dev.dev_id,ICM20608_CNT);
171 fail_devid:
172     return 0;
173 }
174 
175 static int icm20608_remove(struct spi_device *spi)
176 {
177     int ret = 0;
178     /*释放资源*/
179     cdev_del(&icm20608_dev.cdev);
180     unregister_chrdev_region(icm20608_dev.dev_id,ICM20608_CNT);
181     device_destroy(icm20608_dev.class,icm20608_dev.dev_id);
182     class_destroy(icm20608_dev.class);
183     gpio_free(icm20608_dev.cs_gpio);
184     return 0;
185 }
186 
187 static const struct of_device_id icm20608_of_match[] = {
188     {.compatible = "alientek,icm20608"},
189     {}
190 };
191 
192 /*传统匹配*/
193 static const struct spi_device_id icm20608_id[] = {
194     {"alientek,icm20608",0},
195     {}
196 };
197 
198 struct spi_driver icm20608_driver = {
199     .driver = {
200         .name = "icm20608",
201         .owner = THIS_MODULE,
202         .of_match_table = icm20608_of_match,
203     },
204     .probe = icm20608_probe,
205     .remove = icm20608_remove,
206     .id_table = icm20608_id,
207 };
208 
209 
210 static int __init icm20608_init(void)
211 {
212     int ret = 0;
213     ret = spi_register_driver(&icm20608_driver);
214     printk("ret = %d\r\n",ret);
215     return ret;
216 }
217 
218 static void __exit icm20608_exit(void)
219 {
220     spi_unregister_driver(&icm20608_driver);
221 }
222 
223 module_init(icm20608_init);
224 module_exit(icm20608_exit);
225 MODULE_LICENSE("GPL");
226 MODULE_AUTHOR("AC");
SPI设备完整框架

并且在上面的代码中还预留了SPI的读写、设备初始化等代码,在后面我们只需要完善各个功能对应的函数就可以了。可以把完成的框架make以后加载一下,可以看到/dev下的设备节点

 

截止到现在为止,我们几乎没有涉及到新的知识点,但是下面就要开始做SPI的读写函数了。

SPI读写函数

SPI的读写函数其实在spi/spi.h文件里是有内核已经完成的,但是我们先自己写一个能更好的知道这个通讯的流程。因为每个外设的SPI写或读的流程可能会有一些差异,对icm20608来说,因为该IC同时支持I2C和SPI,如果使用SPI读写寄存器是需要吧寄存器的地址位的最高位置一,写的话寄存器地址最高位置零,剩下的7个bit才是寄存器的实际地址。所以这里要注意一下处理方法。

SPI设备数据收发流程

前面说了,SPI设备驱动的核心是spi_driver,在向Linux内核注册spi_driver成功以后,我们就可以使用SPI核心层的API来进行SPI外设的读写操作了。这里要用到一个spi_transfer的结构体

 1 struct spi_transfer {
 2     /* it's ok if tx_buf == rx_buf (right?)
 3      * for MicroWire, one buffer must be null
 4      * buffers must work with dma_*map_single() calls, unless
 5      *   spi_message.is_dma_mapped reports a pre-existing mapping
 6      */
 7     const void    *tx_buf;
 8     void        *rx_buf;
 9     unsigned    len;
10 
11     dma_addr_t    tx_dma;
12     dma_addr_t    rx_dma;
13     struct sg_table tx_sg;
14     struct sg_table rx_sg;
15 
16     unsigned    cs_change:1;
17     unsigned    tx_nbits:3;
18     unsigned    rx_nbits:3;
19 #define    SPI_NBITS_SINGLE    0x01 /* 1bit transfer */
20 #define    SPI_NBITS_DUAL        0x02 /* 2bits transfer */
21 #define    SPI_NBITS_QUAD        0x04 /* 4bits transfer */
22     u8        bits_per_word;
23     u16        delay_usecs;
24     u32        speed_hz;
25 
26     struct list_head transfer_list;
27 };

其中tx_buf里保存的是要发送的数据,而rx_buf里是要接受的数据,len时要进行传输的数据长度。和I2C不同,SPI是一种全双工的通讯,所以在没测通讯的过程中收发的字节数应该是一样的。所以这个len就没有接收或发送的区别。在设置好这个spi_transfer对象以后,要把这个spi_transfer对象封装到另外一个spi_message结构体里

 1 struct spi_message {
 2     struct list_head    transfers;
 3 
 4     struct spi_device    *spi;
 5 
 6     unsigned        is_dma_mapped:1;
 7 
 8     /* REVISIT:  we might want a flag affecting the behavior of the
 9      * last transfer ... allowing things like "read 16 bit length L"
10      * immediately followed by "read L bytes".  Basically imposing
11      * a specific message scheduling algorithm.
12      *
13      * Some controller drivers (message-at-a-time queue processing)
14      * could provide that as their default scheduling algorithm.  But
15      * others (with multi-message pipelines) could need a flag to
16      * tell them about such special cases.
17      */
18 
19     /* completion is reported through a callback */
20     void            (*complete)(void *context);
21     void            *context;
22     unsigned        frame_length;
23     unsigned        actual_length;
24     int            status;
25 
26     /* for optional use by whatever driver currently owns the
27      * spi_message ...  between calls to spi_async and then later
28      * complete(), that's the spi_master controller driver.
29      */
30     struct list_head    queue;
31     void            *state;
32 };

我们要用到的就是spi_message里的transfer成员。但是spi_message在使用前需要初始化,并且封装spi_transfer的过程以及发送数据的过程需要用到几个函数。下面我们看下这几个函数

spi_message初始化

spi_message在声明以后需要进行初始化

1 static inline void spi_message_init(struct spi_message *m)
2 {
3     memset(m, 0, sizeof *m);
4     INIT_LIST_HEAD(&m->transfers);
5 }

直接把声明好的message传进来就行了。

spi_transfer封装

在完成了spi_transfer的构建和message初始化以后就可以进行封装了。所用函数如下

1 static inline void
2 spi_message_add_tail(struct spi_transfer *t, struct spi_message *m)
3 {
4     list_add_tail(&t->transfer_list, &m->transfers);
5 }

函数的参数分别是transfer对象和message对象。

传输数据

数据传输分同步和异步,使用异步传输时程序不会阻塞在SPI通讯时,但是需要设置spi_message里的complete成员(上面代码第20行),complete成员是一个回调函数,在完成了SPI传输以后会直接调用这个函数,异步传输所用的函数如下

extern int spi_async(struct spi_device *spi, struct spi_message *message);

函数参数直接就能看明白。

同步传输时程序会阻塞在通讯的过程,但是由于我们这里通讯的过程很短暂,可以先不考虑这个阻塞导致的消耗,在这个SPI驱动的架构我们就是用同步传输的模式,需要用到下面的函数

extern int spi_sync(struct spi_device *spi, struct spi_message *message);

上面就是SPI进行数据传输主要使用的几个函数,下面我们来完成SPI的读写函数

读函数

首先是读函数

 1 /**
 2  * @brief SPI读连续寄存器
 3  * 
 4  * @param dev SPI外设结构体
 5  * @param reg 读取首个寄存器地址
 6  * @param buf 读取数据存放缓存
 7  * @param len 读取寄存器长度
 8  * @return int 
 9  */
10 static int icm20608_read_regs(struct icm20608_device *dev, u8 reg, void *buf, int len)
11 {
12     int ret = 0;
13     struct spi_device *spi = (struct spi_device *)dev->private_data;    //私有数据
14 
15     unsigned char txdata[1];                       //发送数据              
16     unsigned char * rxdata;                        //接收数据缓存
17     struct spi_message msg;                        //spi_message
18     struct spi_transfer *t;                        //spi_transfer
19 
20     /*拉低片选*/
21     gpio_set_value(dev->cs_gpio,0);
22 
23     /*申请内存*/
24     t = kzalloc(sizeof(struct spi_transfer), GFP_KERNEL);
25     rxdata = kzalloc(sizeof(char) * len, GFP_KERNEL);
26 
27     txdata[0] = reg | 0x80;          //地址最高位置一
28     t->tx_buf = txdata;              //发送数据(寄存器地址)
29     t->rx_buf = rxdata;              //接收数据
30     t->len = len + 1;                //数据长度,第一帧为地址,所以要加1
31 
32     spi_message_init(&msg);          //message初始化
33     spi_message_add_tail(t,&msg);    //transfer封装
34     ret = spi_sync(spi,&msg);        //传输
35 
36     /*将读取到的数据复制给buf*/
37     memcpy(buf , rxdata+1, len);    //rxdata为指针,+1是只要读取的数据
38 
39     /*释放内存*/
40     kfree(t);                      
41     kfree(rxdata);
42     /*拉高片选*/
43     gpio_set_value(dev->cs_gpio,1);
44     return ret;
45 }

在上面的的操作中,我们使用了手动拉低GPIO的方式来控制片选信号。首先先声明了message和transfer,还有发送和接受的缓存txdata和rxdata。因为最终我们要的是这个rxdata中的一部分(除去第一个元素),所以要对其申请一段内存;还有transfer也要有一段内存与其对应,所以要用到kzalloc申请内存。因为是全双工通讯,首先向外设发送写入数据寄存器的地址,读取数据的时候要把读取到的第一个byte的数据删掉。所以我们定义的接收数据的缓存是个指针变量,在第37行从内存复制数的时候指针加了1,自动删除了第一个元素。同理,由于第一帧数据时发送的地址,我们读n个字节数据时其实实际上是通讯录n+1个字节的数据。

写数据

 

posted @ 2023-11-19 23:09  银色的音色  阅读(427)  评论(0编辑  收藏  举报