More embedded original articles , Please pay attention to the official account. : A bite of Linux
 official account

1. SPI Bus

1.1. SPI Bus Overview

SPI, It's English Serial Peripheral interface Abbreviation , As the name implies, it is serial peripheral interface . yes Motorola First of all MC68HCXX Defined on the family processor .SPI The interface is mainly used in EEPROM,FLASH, Real time clock ,AD converter , And between the DSP and the DSP .SPI, It's a high speed , full duplex , Synchronous communication bus , And only four wires are used on the chip pins , Saved the chip pins , Also for PCB Save space on layout of , Convenient , It is out of this simple and easy-to-use feature , Now more and more chips integrate this communication protocol .
SPI The composition and signal type of the bus are shown in the figure 1-1 Shown :

  • MOSI – Main device data output , From device data input Corresponding MOSI master output slave input
  • MISO – Main equipment data input , Output data from device Corresponding MISO master input slave output
  • CLK – Clock signal , Generated by the main equipment
  • nCS – Slave enable signal , Controlled by the main equipment

 chart 1-1 SPI Bus model

1.2. SPI Bus timing

SPI Interface in Master Slave enable signal and clock signal generated under control , Two bidirectional shift registers exchange data bit by bit , The high bit of the transmitted data comes first (MSB first), Low in the back . As shown in the figure below , stay CLK The data changes on the falling edge of , One bit of data on the rising edge is stored in the shift register .

 chart 1-2 spi Transmission sequence diagram

In a SPI Within the clock cycle , You will do the following :
(1)Master adopt MOSI Wire transmission 1 Bit data , meanwhile Slave adopt MOSI The line reads this 1 Bit data ;
(2)Slave adopt MISO Wire transmission 1 Bit data , meanwhile Master adopt MISO The line reads this 1 Bit data .
Master and Slave Each has a shift register , Pictured 1-3 Shown , And the two shift registers are connected in a ring . according to CLK The change of , The data to MSB first In turn Master Registers and Slave register , And move it in turn Slave Registers and Master register . When all the contents of the register are moved out , It is equivalent to the exchange of the contents of two registers .
 chart 1-3 spi Transmission diagram

1.3. SPI Bus transfer mode

SPI There are a total of 4 Middle mode , this 4 The two modes are controlled by clock polarity (CPOL,Clock Polarity) And clock phase (CPHA,Clock Phase) To define , among CPOL The parameters specify SCK The level of the idle state of the clock signal ,CPHA It specifies that the data is in SCK Whether the rising edge of the clock is sampled or the falling edge of the clock is sampled . The sequence diagram of these four modes is shown in the figure below 1-4 Shown :
 chart 1-4SPI Bus transmission sequence diagram

  • Pattern 0:CPOL= 0,CPHA=0.CLK Serial clock line idle is low level , The data is in SCK The rising edge of the clock is sampled , The data is in CLK The falling edge of the clock switches
  • Pattern 1:CPOL= 0,CPHA=1.CLK Serial clock line idle is low level , The data is in SCK The falling edge of the clock is sampled , The data is in CLK The rising edge of the clock switches
  • Pattern 2:CPOL= 1,CPHA=0.CLK Serial clock line idle is high level , The data is in SCK The falling edge of the clock is sampled , The data is in CLK The rising edge of the clock switches
  • Pattern 3:CPOL= 1,CPHA=1.CLK Serial clock line idle is high level , The data is in SCK The rising edge of the clock is sampled , The data is in CLK The falling edge of the clock switches
    One of the more common patterns is pattern 0 And pattern 3. For a clearer description SPI The timing of the bus , Here's the pattern 0 Under the SPI Sequence diagram 1-5:

 chart 1-5 mode0 Under the SPI Sequence diagram

1.4. SPI Advantages and disadvantages of bus

(1) In point-to-point communication ,SPI The interface does not need to be addressed , And full duplex communication , It's simple and efficient .
(2) SPI Interface has no flow control specified , There is no reply mechanism to confirm whether data has been received .

2. Linux SPI frame

2.1. Software architecture

Linux System pair spi The device has good support ,linux Under the system spi Drivers can be logically divided into 3 Parts of :

  1. spi The core (SPI Core):SPI Core yes Linux The kernel is used to maintain and manage spi The core of ,SPI Core Provide operation interface function , Allows a spi master,spi driver and spi device At the time of initialization SPI Core Register in , And write off at launch .
  2. spi Controller drive (SPI Master Driver):SPI Master For different types of spi Controller hardware , Realization spi Bus hardware access operation .SPI Master Through the interface function to SPI Core Register a controller .
  3. spi Device drivers (SPI Device Driver):SPI Driver Is corresponding to spi Device driver , Through the interface function to SPI Core To register ,SPI Driver The role of the spi The device is attached to spi On the bus ;
    Linux The software architecture of is shown in the figure 2-1 Shown :

 chart 2-1 spi Software architecture

2.2. Initialization and exit process

2.2.1. register spi controller

register spi There are two stages from controller to kernel :
First stage , Use spi_alloc_master, Allocate one spi_master Space , The specific process is shown in the figure 2-2 Shown :
 chart 2-2 spi Distribute master flow chart

The second stage , Use spi_register_master What will be allocated in the first phase spi_master Register in the kernel , The specific process is as follows 2-3 Shown :
 chart 2-3 spi register master flow chart

2.2.2. Cancellation spi controller

spi The process of controller logout is shown in the figure 2-4 Shown :
 chart 2-4 spi Controller logout flowchart

2.3. Key data structure

2.3.1. spi_device

struct spi_device {struct device         dev;      /*spi The controller corresponds to device structure
        struct spi_master   *master;      /* Used by the equipment master structure , Which main controller is it attached to */u32                     max_speed_hz;   /* Communication clock maximum frequency */u8                      chip_select;     /* Selection number , Every master Support multiple spi_device  */ 
        u8                      mode;#define SPI_CPHA        0x01                    /* clock phase */#define SPI_CPOL        0x02                    /* clock polarity */#define SPI_MODE_0      (0|0)                   /* (original MicroWire) */#define SPI_MODE_1      (0|SPI_CPHA)#define SPI_MODE_2      (SPI_CPOL|0)#define SPI_MODE_3      (SPI_CPOL|SPI_CPHA)#define SPI_CS_HIGH     0x04                    /* chipselect active high? */#define SPI_LSB_FIRST   0x08                    /* per-word bits-on-wire */#define SPI_3WIRE       0x10                    /* SI/SO signals shared */#define SPI_LOOP        0x20                    /* loopback mode */#define SPI_NO_CS       0x40                    /* 1 dev/bus, no chipselect */#define SPI_READY       0x80                    /* slave pulls low to pause */u8                      bits_per_word;    /* The number of bits per word length , The default is 8*/int                     irq;void                    *controller_state; /* Controller status */void                    *controller_data;   /* Controller data */char                    modalias[SPI_NAME_SIZE]; /*  The name of the device driver  */int                     cs_gpio;        /* chip select gpio *//*
         * likely need more hooks for more protocol options affecting how
         * the controller talks to each chip, like:
         *  - memory packing (12 bit samples into low bits, others zeroed)
         *  - priority
         *  - drop chipselect after each word
         *  - chipselect delays
         *  - ...
         */};

spi_device It represents a periphery spi equipment , from master controller driver Scan after registration BSP Register the device linked list generated by the device in and send it to spi_bus Registration produces . In kernel , Every spi_device Represents a physical spi equipment .

2.3.2. spi_driver

struct spi_driver {const struct spi_device_id *id_table; /* Supported by spi_device Equipment list */int                     (*probe)(struct spi_device *spi);int                     (*remove)(struct spi_device *spi);void                    (*shutdown)(struct spi_device *spi);int                     (*suspend)(struct spi_device *spi, pm_message_t mesg);int                     (*resume)(struct spi_device *spi);struct device_driver    driver;};

spi_driver Representing one SPI protocol drivers, That is peripheral driver

2.3.3. struct spi_master

struct spi_master {struct device   dev;  /*spi The controller corresponds to device structure */struct list_head list;  /* Linked list
        /* other than negative (== assign one dynamically), bus_num is fully
         * board-specific.  usually that simplifies to being SOC-specific.
         * example:  one SOC has three SPI controllers, numbered 0..2,
         * and one board's schematics might show it using SPI-2.  software
         * would normally use bus_num=2 for that controller.
         */s16                     bus_num; /* Bus ( Or controller number )*//* chipselects will be integral to many controllers; some others
         * might use board-specific GPIOs.
         */u16                     num_chipselect; /* The number of selections *//* some SPI controllers pose alignment requirements on DMAable
         * buffers; let protocol drivers know about these requirements.
         */u16                     dma_alignment;/* spi_device.mode flags understood by this controller driver */u16                     mode_bits;   /* master Supported device modes  *//* bitmask of supported bits_per_word for transfers */u32                     bits_per_word_mask;/* other constraints relevant to this driver */u16                     flags; /* Flag bits used to limit certain restrictions
#define SPI_MASTER_HALF_DUPLEX  BIT(0)          /* can't do full duplex */#define SPI_MASTER_NO_RX        BIT(1)          /* can't do buffer read */#define SPI_MASTER_NO_TX        BIT(2)          /* can't do buffer write *//* lock and mutex for SPI bus locking */spinlock_t              bus_lock_spinlock;struct mutex            bus_lock_mutex;/* flag indicating that the SPI bus is locked for exclusive use */bool                    bus_lock_flag;/* Setup mode and clock, etc (spi driver may call many times).
         *
         * IMPORTANT:  this may be called when transfers to another
         * device are active.  DO NOT UPDATE SHARED REGISTERS in ways
         * which could break those transfers.
         */int                     (*setup)(struct spi_device *spi); /* according to spi Device update hardware configuration . Set up spi Working mode 、 Clock, etc *//* bidirectional bulk transfers
         *
         * + The transfer() method may not sleep; its main role is
         *   just to add the message to the queue.
         * + For now there's no remove-from-queue operation, or
         *   any other request management
         * + To a given spi_device, message queueing is pure fifo
         *
* + The master's main job is to process its message queue,
         *   selecting a chip then transferring data
         * + If there are multiple spi_device children, the i/o queue
         *   arbitration algorithm is unspecified (round robin, fifo,
         *   priority, reservations, preemption, etc)
         *
         * + Chipselect stays active during the entire message
         *   (unless modified by spi_transfer.cs_change != 0).
         * + The message transfers use clock and SPI mode parameters
         *   previously established by setup() for this device
         */int                     (*transfer)(struct spi_device *spi,struct spi_message *mesg);   /* Methods to add messages to the queue , This function cannot sleep . Its job is to schedule the delivery and call the registered callback function complete()*//* called on release() to free memory provided by spi_master */void                    (*cleanup)(struct spi_device *spi);/*cleanup The function will be in the spidev_release Called in function ,spidev_release Registered as spi dev Of release function .*//*
         * These hooks are for drivers that want to use the generic
         * master transfer queueing mechanism. If these are used, the
         * transfer() function above must NOT be specified by the driver.
         * Over time we expect SPI drivers to be phased over to this API.
         */bool                      queued; struct kthread_worker  kworker; /* The work queue thread used to manage the data transfer message queue */struct task_struct     *kworker_task;struct kthread_work    pump_messages; /* Specific implementation of data transmission queue work queue */spinlock_t               queue_lock;struct list_head        queue; /* Message queuing for the controller , All queues waiting for transmission are hung under the linked list */struct spi_message     *cur_msg;/* The message queue currently being processed */bool                       busy; / Busy state */bool                       running; /* Running */bool                       rt; int (*prepare_transfer_hardware)(struct spi_master *master); /* Callback function , It will be called before the transmission is officially initiated , For preparing hardware resources */int (*transfer_one_message)(struct spi_master *master, struct spi_message *mesg); /* Atomic transport callback function for a single message , Each message in the queue is called back once to complete the transmission */  int (*unprepare_transfer_hardware)(struct spi_master *master); /* Clean up callback functions *//* gpio chip select */int                     *cs_gpios;};

spi_master Representing one spi controller .

2.3.4. struct spi_message and spi_transfer

To complete and SPI Data transmission of equipment , We need two more data structures :spi_message and spi_transfer.

spi_message It contains a spi_transfer Structural sequence , Once the controller receives a spi_message, Among them spi_transfer Should be sent in order , And it can't be used by other spi_message interrupt , So we think spi_message Just once SPI Atomic operation of data exchange . Let's look at the definitions of these two data structures :

struct spi_message :

struct spi_message {struct list_head        transfers;  /*spi_transfer List queue , The transmission segment queue of this message , A message can contain multiple transport segments .*/struct spi_device       *spi; /* The destination device of the transmission */unsigned                is_dma_mapped:1;  /* If it is true , This call provides dma and cpu Virtual address .*//* REVISIT:  we might want a flag affecting the behavior of the
         * last transfer ... allowing things like "read 16 bit length L"
         * immediately followed by "read L bytes".  Basically imposing
         * a specific message scheduling algorithm.
         *
         * Some controller drivers (message-at-a-time queue processing)
         * could provide that as their default scheduling algorithm.  But
         * others (with multi-message pipelines) could need a flag to
         * tell them about such special cases.
         *//* completion is reported through a callback */void                    (*complete)(void *context);/* Call the callback function after the completion of asynchronous call */void                    *context; /* The parameters of the callback function */unsigned                actual_length; /* The length of the actual transmission */int                     status; /* The result of sending the message , Success is set 0, Otherwise, it's a negative error code .*//* for optional use by whatever driver currently owns the
         * spi_message ...  between calls to spi_async and then later
         * complete(), that's the spi_master controller driver.
         */struct list_head        queue;void                    *state;};

Linked list fields queue It is used to hang the structure on the spi_master Structural queue Field , Multiple... Can be added to the controller at the same time spi_message Queuing . Another linked list field transfers It is used to link to this message Under the spi_tranfer structure .complete The callback function will be in the message All under spi_transfer All are called when the transfer is complete , In order to inform the protocol driver to process the received data and prepare the next batch of data to be sent . Let's see spi_transfer structure :
spi_transfer

struct spi_transfer {/* it's ok if tx_buf == rx_buf (right?)
         * for MicroWire, one buffer must be null
         * buffers must work with dma_*map_single() calls, unless
         *   spi_message.is_dma_mapped reports a pre-existing mapping
         */const void     *tx_buf; /* Send buffer */void            *rx_buf; /* Receive buffer */unsigned        len; /* Buffer length ,tx and rx Size ( Number of bytes ). Of their respective sizes */dma_addr_t      tx_dma; /*tx Of dma Address */dma_addr_t      rx_dma;  /*rx Of dma Address */
 unsigned        cs_change:1; /* At present spi_transfer Re select after sending */u8              bits_per_word; /* The number of bits per word length ,0 On behalf of the use of spi_device Default in 8*/u16             delay_usecs; /* Send to complete a spi_transfer The delay time after , The delay between the end of the transmission and the change of the selection , After that, another transmission will start or the whole message will end */u32             speed_hz; /* Communication clock . If it is 0, Use the default value */#ifdef CONFIG_SPI_LOMBOstruct lombo_spi_operate_para *esop;#endifstruct list_head transfer_list; /* Used to link to spi_message, Two way link node used to connect */};

First ,transfer_list The linked list field is used to put the transfer Hang on a spi_message In structure ,tx_buf and rx_buf Provide non dma Data buffer address in mode ,len Is the length of the data to be transmitted ,tx_dma and rx_dma It gives dma Buffer address in mode . In principle ,spi_transfer It's the smallest unit of transmission , The reason is that spi_message package , I think the reason is : Sometimes I hope to spi Multiple discontinuous addresses of the device ( Or register ) Write once , without spi_message Carry out such a number of spi_transfer pack , Because usually the real data transfer work is in another kernel thread ( Work queue ) Done in , The consequence of not packaging is that it will cause more process switching , Low efficiency , Delay increases , Especially for small-scale data transmission with multiple discontinuous addresses .

2.3.5. spi_board_info

struct spi_board_info {/* the device name and module name are coupled, like platform_bus;
         * "modalias" is normally the driver name.
         *
         * platform_data goes to spi_device.dev.platform_data,
         * controller_data goes to spi_device.controller_data,
         * irq is copied too
         */char            modalias[SPI_NAME_SIZE]; /* name */const void      *platform_data; /* Platform data */void            *controller_data; /* Controller data */int             irq;/* slower signaling on noisy or low voltage boards */u32             max_speed_hz; /* Maximum speed *//* bus_num is board specific and matches the bus_num of some
         * spi_master that will probably be registered later.
         *
         * chip_select reflects how this chip is wired to that master;
         * it's less than num_chipselect.
         */u16             bus_num; /*spi Bus number */u16             chip_select; /* Chip selection *//* mode becomes spi_device.mode, and is essential for chips
         * where the default of SPI_CS_HIGH = 0 is wrong.
         */u8              mode; /* Pattern  *//* ... may need additional spi_device chip config data here.
         * avoid stuff protocol drivers can set; but include stuff
         * needed to behave without being bound to a driver:
         *  - quirks like clock rate mattering when not selected
         */};

2.4. Data transfer process

The overall data transmission process is basically like this :

  1. Define a spi_message structure ;
  2. use spi_message_init Function initialization spi_message;
  3. Define one or more spi_transfer structure , Initialize and prepare a buffer for the data and assign it to spi_transfer Corresponding fields (tx_buf,rx_buf etc. );
  4. adopt spi_message_init Function takes these spi_transfer Hang on spi_message Under the structure ;
  5. If you use synchronization , call spi_sync(), If you use asynchronous , call spi_async();( When I debug peripherals , Only used spi_sync

The transmission diagram is shown in the figure 2-5 Shown :
 chart 2-5 spi Data transmission diagram

2.4.1. Data preparation

2.4.1.1. spi_message_init

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

initialization spi_message: Empty message, initialization transfers Chain head .

2.4.1.2. spi_message_add_tail

static inline voidspi_message_add_tail(struct spi_transfer *t, struct spi_message *m){list_add_tail(&t->transfer_list, &m->transfers);}

take spi_transfer Add to spi_message At the end of the list .

2.4.2. The data transfer

SPI There are two ways of data transmission : Synchronous mode and asynchronous mode . The so-called synchronous mode means that the initiator of data transmission must wait for the end of this transmission , You can't do anything else in the meantime , To explain it in code is , After calling the transferred function , Until the data transfer is complete , Function will return . Asynchronous mode is the opposite , The initiator of the data transmission does not have to wait for the end of the transmission , There are other things that can be done during data transmission , To explain it in code is , After calling the transferred function , The function returns immediately without waiting for the data transfer to complete , We just need to set up a callback function , After transmission , The callback function is called to notify the initiator that the data transfer has completed . Synchronization is easy to use , It is very suitable for single transmission of small amount of data . But for large amounts of data 、 For multiple transmissions , Asynchronous mode is more suitable .
about SPI For the controller , To support asynchronous mode, we must consider the following two situations :

  • For the initiator of the same data transmission , Since asynchronous mode does not need to wait for data transmission to complete to return , After the return , The initiator can immediately launch another message, And then the last one message It's not finished yet .
  • For a different sponsor , It's also possible to launch at the same time message Transmission request
    First analysis spi_sync() The implementation process of the interface , Pictured 2-6:
     chart 2-6 spi_sync Transmission flow chart

Secondly, it analyzes spi_async_locked The implementation process of the interface , Pictured 2-7 Shown :
 chart 2-7 spi_async_locked Processing flow chart

spi_queued_transfer The implementation process of the interface is shown in the figure 3-8 Shown :
 chart 3-8 spi_queued_transfer Processing flow chart

spi_pump_messages The processing flow of the function is shown in the figure 3-9 Shown :
 chart 3-9 spi_pump_messages Processing flow chart

In the figure transfer_one_message yes spi The controller drives what is to be achieved , The main function is to process spi_message Each of the spi_transfer.

2.5. Key function analysis

2.5.1. spi_alloc_master

Prototype :

struct spi_master *spi_alloc_master(struct device *dev, unsigned size)

function :
Allocate one spi_master Structure pointer .

Parameters :
dev:spi controller device The pointer
size : The distribution of driver-private data size

Return value :
success , return spi_master The pointer ; Otherwise return to NULL

2.5.2. spi_register_master

Prototype :

int spi_register_master(struct spi_master *master)

function
register spi The controller drives to the kernel .

Parameters
master:spi_master The pointer

Return value
success , return 0; Otherwise, an error code is returned

2.5.3. spi_unregister_master

Prototype :

void spi_unregister_master(struct spi_master *master)

function
Cancellation spi Controller drive .

Parameters
master:spi_master The pointer

Return value nothing

3. Demo

( Reference self punctual atom )

#include <linux/types.h>#include <linux/kernel.h>#include <linux/delay.h>#include <linux/ide.h>#include <linux/init.h>#include <linux/module.h>#include <linux/errno.h>#include <linux/gpio.h>#include <linux/cdev.h>#include <linux/device.h>#include <linux/of_gpio.h>#include <linux/semaphore.h>#include <linux/timer.h>#include <linux/i2c.h>#include <linux/spi/spi.h>#include <linux/of.h>#include <linux/of_address.h>#include <linux/of_gpio.h>#include <linux/platform_device.h>#include <asm/mach/map.h>#include <asm/uaccess.h>#include <asm/io.h>#include "icm20608reg.h"/***************************************************************
Copyright  ALIENTEK Co., Ltd. 1998-2029. All rights reserved.
file name : icm20608.c
author    :  Zuo Gong
edition     : V1.0
describe     : ICM20608 SPI The driver
other     :  nothing
Forum   : 
journal     :  First edition V1.0 2019/9/2  Zuo Gong created
***************************************************************/#define ICM20608_CNT 1#define ICM20608_NAME "icm20608"struct icm20608_dev {
dev_t devid;/*  Device number    */struct cdev cdev;/* cdev  */struct class *class;/*  class   */struct device *device;/*  equipment    */struct device_node *nd;  /*  Device node  */int major;/*  The main equipment,  */void *private_data;/*  Private data   */int cs_gpio;/*  The selection uses GPIO Number */signed int gyro_x_adc;/*  gyroscope X Axis original value    */signed int gyro_y_adc;/*  gyroscope Y Axis original value */signed int gyro_z_adc;/*  gyroscope Z Axis original value   */signed int accel_x_adc;/*  Accelerometer X Axis original value   */signed int accel_y_adc;/*  Accelerometer Y Axis original value */signed int accel_z_adc;/*  Accelerometer Z Axis original value   */signed int temp_adc;/*  The original temperature value   */};static struct icm20608_dev icm20608dev;/*
 * @description :  from icm20608 Read multiple register data
 * @param - dev:  icm20608 equipment
 * @param - reg:   The first address of the register to be read
 * @param - val:   Data read
 * @param - len:   The length of data to read
 * @return  :  Operating results
 */static int icm20608_read_regs(struct icm20608_dev *dev, u8 reg, void *buf, int len){int ret;unsigned char txdata[len];struct spi_message m;struct spi_transfer *t;struct spi_device *spi = (struct spi_device *)dev->private_data;gpio_set_value(dev->cs_gpio, 0);/*  The selection is down , Choose ICM20608 */
t = kzalloc(sizeof(struct spi_transfer), GFP_KERNEL);/*  Application memory  *//*  The first 1 Time , Send the register address to read  */
txdata[0] = reg | 0x80;/*  Register address when writing data bit8 To set up 1 */
t->tx_buf = txdata;/*  Data to send  */
t->len = 1;/* 1 Bytes  */spi_message_init(&m);/*  initialization spi_message */spi_message_add_tail(t, &m);/*  take spi_transfer Add to spi_message queue  */
ret = spi_sync(spi, &m);/*  The synchronous  *//*  The first 2 Time , Reading data  */
txdata[0] = 0xff;/*  Any value , It doesn't make sense here  */
t->rx_buf = buf;/*  Data read  */
t->len = len;/*  The length of data to read  */spi_message_init(&m);/*  initialization spi_message */spi_message_add_tail(t, &m);/*  take spi_transfer Add to spi_message queue  */
ret = spi_sync(spi, &m);/*  The synchronous  */kfree(t);/*  Free memory  */gpio_set_value(dev->cs_gpio, 1);/*  The selection of films is going up , Release ICM20608 */return ret;}/*
 * @description :  towards icm20608 Multiple registers write data
 * @param - dev:  icm20608 equipment
 * @param - reg:   The first address of the register to be written
 * @param - val:   Data buffer to write to
 * @param - len:   The length of data to write
 * @return    :    Operating results
 */static s32 icm20608_write_regs(struct icm20608_dev *dev, u8 reg, u8 *buf, u8 len){int ret;unsigned char txdata[len];struct spi_message m;struct spi_transfer *t;struct spi_device *spi = (struct spi_device *)dev->private_data;
t = kzalloc(sizeof(struct spi_transfer), GFP_KERNEL);/*  Application memory  */gpio_set_value(dev->cs_gpio, 0);/*  The selection is down  *//*  The first 1 Time , Send the register address to read  */
txdata[0] = reg & ~0x80;/*  Register address when writing data bit8 To clear  */
t->tx_buf = txdata;/*  Data to send  */
t->len = 1;/* 1 Bytes  */spi_message_init(&m);/*  initialization spi_message */spi_message_add_tail(t, &m);/*  take spi_transfer Add to spi_message queue  */
ret = spi_sync(spi, &m);/*  The synchronous  *//*  The first 2 Time , Send data to be written  */
t->tx_buf = buf;/*  The data to be written  */
t->len = len;/*  Bytes written  */spi_message_init(&m);/*  initialization spi_message */spi_message_add_tail(t, &m);/*  take spi_transfer Add to spi_message queue  */
ret = spi_sync(spi, &m);/*  The synchronous  */kfree(t);/*  Free memory  */gpio_set_value(dev->cs_gpio, 1);/*  The selection of films is going up , Release ICM20608 */return ret;}/*
 * @description :  Read icm20608 Specify the register value , Read a register
 * @param - dev:  icm20608 equipment
 * @param - reg:   Register to read
 * @return    :    Read register value
 */static unsigned char icm20608_read_onereg(struct icm20608_dev *dev, u8 reg){
u8 data = 0;icm20608_read_regs(dev, reg, &data, 1);return data;}/*
 * @description :  towards icm20608 The specified register writes the specified value , Write a register
 * @param - dev:  icm20608 equipment
 * @param - reg:   Register to write
 * @param - data:  Value to write
 * @return   :     nothing
 */static void icm20608_write_onereg(struct icm20608_dev *dev, u8 reg, u8 value){
u8 buf = value;icm20608_write_regs(dev, reg, &buf, 1);}/*
 * @description :  Read ICM20608 The data of , Read raw data , Including three-axis gyroscopes 、
 *  :  Three axis accelerometer and internal temperature .
 * @param - dev : ICM20608 equipment
 * @return  :  nothing .
 */void icm20608_readdata(struct icm20608_dev *dev){unsigned char data[14];icm20608_read_regs(dev, ICM20_ACCEL_XOUT_H, data, 14);
dev->accel_x_adc = (signed short)((data[0] << 8) | data[1]); 
dev->accel_y_adc = (signed short)((data[2] << 8) | data[3]); 
dev->accel_z_adc = (signed short)((data[4] << 8) | data[5]); 
dev->temp_adc    = (signed short)((data[6] << 8) | data[7]); 
dev->gyro_x_adc  = (signed short)((data[8] << 8) | data[9]); 
dev->gyro_y_adc  = (signed short)((data[10] << 8) | data[11]);
dev->gyro_z_adc  = (signed short)((data[12] << 8) | data[13]);}/*
 * @description :  Turn on the device
 * @param - inode  :  To the driver inode
 * @param - filp  :  Device file ,file There's a structure called privateate_data Member variables of
 *     Generally in open Will be private_data It's like a directed device structure .
 * @return  : 0  success ; other   Failure
 */static int icm20608_open(struct inode *inode, struct file *filp){
filp->private_data = &icm20608dev; /*  Set up private data  */return 0;}/*
 * @description :  Read data from device  
 * @param - filp  :  Device file to open ( File descriptor )
 * @param - buf  :  Data buffer returned to user space
 * @param - cnt  :  The length of data to read
 * @param - offt  :  Offset from the first address of the file
 * @return  :  Number of bytes read , If it's negative , Indicates a read failure
 */static ssize_t icm20608_read(struct file *filp, char __user *buf, size_t cnt, loff_t *off){signed int data[7];long err = 0;struct icm20608_dev *dev = (struct icm20608_dev *)filp->private_data;icm20608_readdata(dev);
data[0] = dev->gyro_x_adc;
data[1] = dev->gyro_y_adc;
data[2] = dev->gyro_z_adc;
data[3] = dev->accel_x_adc;
data[4] = dev->accel_y_adc;
data[5] = dev->accel_z_adc;
data[6] = dev->temp_adc;
err = copy_to_user(buf, data, sizeof(data));return 0;}/*
 * @description :  close / release device
 * @param - filp  :  Device file to close ( File descriptor )
 * @return  : 0  success ; other   Failure
 */static int icm20608_release(struct inode *inode, struct file *filp){return 0;}/* icm20608 Operation function  */static const struct file_operations icm20608_ops = {.owner = THIS_MODULE,.open = icm20608_open,.read = icm20608_read,.release = icm20608_release,};/*
 * ICM20608 Internal register initialization function  
 * @param   :  nothing
 * @return  :  nothing
 */void icm20608_reginit(void){
u8 value = 0;icm20608_write_onereg(&icm20608dev, ICM20_PWR_MGMT_1, 0x80);mdelay(50);icm20608_write_onereg(&icm20608dev, ICM20_PWR_MGMT_1, 0x01);mdelay(50);
value = icm20608_read_onereg(&icm20608dev, ICM20_WHO_AM_I);printk("ICM20608 ID = %#X\r\n", value);icm20608_write_onereg(&icm20608dev, ICM20_SMPLRT_DIV, 0x00);  /*  The output rate is the internal sample rate */icm20608_write_onereg(&icm20608dev, ICM20_GYRO_CONFIG, 0x18);  /*  gyroscope ±2000dps range   */icm20608_write_onereg(&icm20608dev, ICM20_ACCEL_CONFIG, 0x18);  /*  Accelerometer ±16G range   */icm20608_write_onereg(&icm20608dev, ICM20_CONFIG, 0x04);  /*  Gyroscope low pass filtering BW=20Hz  */icm20608_write_onereg(&icm20608dev, ICM20_ACCEL_CONFIG2, 0x04); /*  Accelerometer low pass filtering BW=21.2Hz  */icm20608_write_onereg(&icm20608dev, ICM20_PWR_MGMT_2, 0x00);  /*  Turn on all axes of accelerometer and gyroscope   */icm20608_write_onereg(&icm20608dev, ICM20_LP_MODE_CFG, 0x00);  /*  Turn off low power consumption   */icm20608_write_onereg(&icm20608dev, ICM20_FIFO_EN, 0x00);/*  close FIFO */}
 /*
  * @description     : spi Driven probe function , When driving with
  *                     After the device matches, this function will execute
  * @param - client  : spi equipment
  * @param - id      : spi equipment ID
  * 
  */static int icm20608_probe(struct spi_device *spi){int ret = 0;/* 1、 Build device number  */if (icm20608dev.major) {
icm20608dev.devid = MKDEV(icm20608dev.major, 0);register_chrdev_region(icm20608dev.devid, ICM20608_CNT, ICM20608_NAME);} else {alloc_chrdev_region(&icm20608dev.devid, 0, ICM20608_CNT, ICM20608_NAME);
icm20608dev.major = MAJOR(icm20608dev.devid);}/* 2、 Register device  */cdev_init(&icm20608dev.cdev, &icm20608_ops);cdev_add(&icm20608dev.cdev, icm20608dev.devid, ICM20608_CNT);/* 3、 Create a class  */
icm20608dev.class = class_create(THIS_MODULE, ICM20608_NAME);if (IS_ERR(icm20608dev.class)) {return PTR_ERR(icm20608dev.class);}/* 4、 Create device  */
icm20608dev.device = device_create(icm20608dev.class, NULL, icm20608dev.devid, NULL, ICM20608_NAME);if (IS_ERR(icm20608dev.device)) {return PTR_ERR(icm20608dev.device);}/*  In the device tree cs Piece of optional signal  */
icm20608dev.nd = of_find_node_by_path("/soc/[email protected]/[email protected]/[email protected]");if(icm20608dev.nd == NULL) {printk("ecspi3 node not find!\r\n");return -EINVAL;} 
/* 2、  Get... In the device tree gpio attribute , obtain BEEP What is used BEEP Number  */
icm20608dev.cs_gpio = of_get_named_gpio(icm20608dev.nd, "cs-gpio", 0);if(icm20608dev.cs_gpio < 0) {printk("can't get cs-gpio");return -EINVAL;}/* 3、 Set up GPIO1_IO20 For export , And output high level  */
ret = gpio_direction_output(icm20608dev.cs_gpio, 1);if(ret < 0) {printk("can't set gpio!\r\n");}/* initialization spi_device */
spi->mode = SPI_MODE_0;/*MODE0,CPOL=0,CPHA=0*/spi_setup(spi);
icm20608dev.private_data = spi; /*  Set up private data  *//*  initialization ICM20608 Internal register  */icm20608_reginit();return 0;}/*
 * @description     : spi Driven remove function , remove spi When driving, this function will execute
 * @param - client  : spi equipment
 * @return          : 0, success ; Other negative values , Failure
 */static int icm20608_remove(struct spi_device *spi){/*  Delete device  */cdev_del(&icm20608dev.cdev);unregister_chrdev_region(icm20608dev.devid, ICM20608_CNT);/*  Log off classes and devices  */device_destroy(icm20608dev.class, icm20608dev.devid);class_destroy(icm20608dev.class);return 0;}/*  Traditional matching ID list  */static const struct spi_device_id icm20608_id[] = {{"alientek,icm20608", 0},  
{}};/*  Device tree matching list  */static const struct of_device_id icm20608_of_match[] = {{ .compatible = "alientek,icm20608" },{ /* Sentinel */ }};/* SPI Drive structure  */static struct spi_driver icm20608_driver = {.probe = icm20608_probe,.remove = icm20608_remove,.driver = {.owner = THIS_MODULE,
    .name = "icm20608",
    .of_match_table = icm20608_of_match, 
   },.id_table = icm20608_id,};
   /*
 * @description :  Drive entry function
 * @param  :  nothing
 * @return  :  nothing
 */static int __init icm20608_init(void){return spi_register_driver(&icm20608_driver);}/*
 * @description :  Driving the exit function
 * @param  :  nothing
 * @return  :  nothing
 */static void __exit icm20608_exit(void){spi_unregister_driver(&icm20608_driver);}module_init(icm20608_init);module_exit(icm20608_exit);MODULE_LICENSE("GPL");MODULE_AUTHOR(yikoulinux");