1. SPI×ÜÏß
1.1. SPI×ÜÏ߸ÅÊö
SPI£¬ÊÇÓ¢ÓïSerial Peripheral interfaceµÄËõд£¬¹ËÃû˼Òå¾ÍÊÇ´®ÐÐÍâΧÉ豸½Ó¿Ú¡£ÊÇMotorolaÊ×ÏÈÔÚÆäMC68HCXXϵÁд¦ÀíÆ÷É϶¨ÒåµÄ¡£SPI½Ó¿ÚÖ÷ÒªÓ¦ÓÃÔÚ EEPROM£¬FLASH£¬ÊµÊ±Ê±ÖÓ£¬ADת»»Æ÷£¬»¹ÓÐÊý×ÖÐźŴ¦ÀíÆ÷ºÍÊý×ÖÐźŽâÂëÆ÷Ö®¼ä¡£SPI£¬ÊÇÒ»ÖÖ¸ßËٵģ¬È«Ë«¹¤£¬Í¬²½µÄͨÐÅ×ÜÏߣ¬²¢ÇÒÔÚоƬµÄ¹Ü½ÅÉÏÖ»Õ¼ÓÃËĸùÏߣ¬½ÚÔ¼ÁËоƬµÄ¹Ü½Å£¬Í¬Ê±ÎªPCBµÄ²¼¾ÖÉϽÚÊ¡¿Õ¼ä£¬Ìṩ·½±ã£¬ÕýÊdzöÓÚÕâÖÖ¼òµ¥Ò×ÓõÄÌØÐÔ£¬ÏÖÔÚÔ½À´Ô½¶àµÄоƬ¼¯³ÉÁËÕâÖÖͨÐÅÐÒé¡£SPI×ÜÏߵĹ¹³É¼°ÐźÅÀàÐÍÈçͼ1-1Ëùʾ£º
ͼ1-1 SPI×ÜÏßÄ£ÐÍ
1.2. SPI×ÜÏßʱÐò
SPI½Ó¿ÚÔÚMaster¿ØÖÆϲúÉúµÄ´ÓÉ豸ʹÄÜÐźźÍʱÖÓÐźţ¬Á½¸öË«ÏòÒÆλ¼Ä´æÆ÷°´Î»´«Êä½øÐÐÊý¾Ý½»»»£¬´«ÊäÊý¾Ý¸ßλÔÚÇ°(MSB first)£¬µÍλÔÚºó¡£ÈçÏÂͼËùʾ£¬ÔÚCLKµÄϽµÑØÉÏÊý¾Ý¸Ä±ä£¬ÉÏÉýÑØһλÊý¾Ý±»´æÈëÒÆλ¼Ä´æÆ÷¡£
ͼ1-2 spi´«ÊäʱÐòͼ
ÔÚÒ»¸öSPIʱÖÓÖÜÆÚÄÚ£¬»áÍê³ÉÈçϲÙ×÷£º(1)Masterͨ¹ýMOSIÏß·¢ËÍ1λÊý¾Ý£¬Í¬Ê±Slaveͨ¹ýMOSIÏ߶ÁÈ¡Õâ1λÊý¾Ý;(2)Slaveͨ¹ýMISOÏß·¢ËÍ1λÊý¾Ý£¬Í¬Ê±Masterͨ¹ýMISOÏ߶ÁÈ¡Õâ1λÊý¾Ý¡£MasterºÍSlave¸÷ÓÐÒ»¸öÒÆλ¼Ä´æÆ÷£¬Èçͼ1-3Ëùʾ£¬¶øÇÒÕâÁ½¸öÒÆλ¼Ä´æÆ÷Á¬½Ó³É»·×´¡£ÒÀÕÕCLKµÄ±ä»¯£¬Êý¾ÝÒÔMSB firstµÄ·½Ê½ÒÀ´ÎÒƳöMaster¼Ä´æÆ÷ºÍSlave¼Ä´æÆ÷£¬²¢ÇÒÒÀ´ÎÒÆÈëSlave¼Ä´æÆ÷ºÍMaster¼Ä´æÆ÷¡£µ±¼Ä´æÆ÷ÖеÄÄÚÈÝÈ«²¿ÒƳöʱ£¬Ï൱ÓÚÍê³ÉÁËÁ½¸ö¼Ä´æÆ÷ÄÚÈݵĽ»»»¡£
1.3. SPI×ÜÏß´«Êäģʽ
SPI×ÜÏß´«ÊäÒ»¹²ÓÐ4ÖÖģʽ£¬Õâ4ÖÖģʽ·Ö±ðÓÉʱÖÓ¼«ÐÔ(CPOL£¬Clock Polarity)ºÍʱÖÓÏàλ(CPHA£¬Clock Phase)À´¶¨Ò壬ÆäÖÐCPOL²ÎÊý¹æ¶¨ÁËSCKʱÖÓÐźſÕÏÐ״̬µÄµçƽ£¬CPHA¹æ¶¨ÁËÊý¾ÝÊÇÔÚSCKʱÖÓµÄÉÏÉýÑر»²ÉÑù»¹ÊÇϽµÑر»²ÉÑù¡£ÕâËÄÖÖģʽµÄʱÐòͼÈçÏÂͼ1-4Ëùʾ£º
ͼ1-5 mode0ϵÄSPIʱÐòͼ
1.4. SPI×ÜÏßµÄÓÅȱµã
(1) ÔÚµã¶ÔµãµÄͨÐÅÖУ¬SPI½Ó¿Ú²»ÐèÒª½øÐÐÑ°Ö·²Ù×÷£¬ÇÒΪȫ˫¹¤Í¨ÐÅ£¬ÏԵüòµ¥¸ßЧ¡£(2) SPI½Ó¿ÚûÓÐÖ¸¶¨µÄÁ÷¿ØÖÆ£¬Ã»ÓÐÓ¦´ð»úÖÆÈ·ÈÏÊÇ·ñ½ÓÊÕµ½Êý¾Ý¡£
2. Linux SPI ¿ò¼Ü
2.1. Èí¼þ¼Ü¹¹
Linuxϵͳ¶ÔspiÉ豸¾ßÓкܺõÄÖ§³Ö£¬linuxϵͳϵÄspiÇý¶¯³ÌÐò´ÓÂß¼ÉÏ¿ÉÒÔ·ÖΪ3¸ö²¿·Ö£º
ͼ2-1 spiÈí¼þ¼Ü¹¹Í¼
2.2. ³õʼ»¯¼°Í˳öÁ÷³Ì
2.2.1. ×¢²áspi¿ØÖÆÆ÷
×¢²áspi¿ØÖÆÆ÷µ½Äں˷ÖΪÁ½¸ö½×¶Î£ºµÚÒ»¸ö½×¶Î£¬Ê¹ÓÃspi_alloc_master,·ÖÅäÒ»¸öspi_masterµÄ¿Õ¼ä£¬¾ßÌåÁ÷³ÌÈçͼ2-2Ëùʾ£º
µÚ¶þ½×¶Î£¬Ê¹ÓÃspi_register_master½«µÚÒ»½×¶Î·ÖÅäµÄspi_master×¢²áµ½ÄÚºËÖУ¬¾ßÌåÁ÷³ÌÈç2-3Ëùʾ£º
2.2.2. ×¢Ïúspi¿ØÖÆÆ÷
spi¿ØÖÆÆ÷×¢ÏúµÄÁ÷³ÌÈçͼ2-4Ëùʾ£º
2.3. ¹Ø¼üÊý¾Ý½á¹¹
2.3.1. spi_device
- struct spi_device {
- struct device dev; /*spi¿ØÖÆÆ÷¶ÔÓ¦µÄdevice½á¹¹
- struct spi_master *master; /*É豸ʹÓõÄmaster½á¹¹£¬¹ÒÔÚÄĸöÖ÷¿ØÖÆÆ÷ÏÂ*/
- u32 max_speed_hz; /*ͨѶʱÖÓ×î´óƵÂÊ*/
- u8 chip_select; /*ƬѡºÅ£¬Ã¿¸ömasterÖ§³Ö¶à¸ö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; /*ÿ¸ö×Ö³¤µÄ±ÈÌØÊý£¬Ä¬ÈÏÊÇ8*/
- int irq;
- void *controller_state; /*¿ØÖÆÆ÷״̬*/
- void *controller_data; /*¿ØÖÆÆ÷Êý¾Ý*/
- char modalias[SPI_NAME_SIZE]; /* É豸Çý¶¯µÄÃû×Ö */
- 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´ú±íÒ»¸öÍâΧspiÉ豸£¬ÓÉmaster controller driver×¢²áÍê³ÉºóɨÃèBSPÖÐ×¢²áÉ豸²úÉúµÄÉ豸Á´±í²¢Ïòspi_bus×¢²á²úÉú¡£ÔÚÄÚºËÖУ¬Ã¿¸öspi_device´ú±íÒ»¸öÎïÀíµÄspiÉ豸¡£
2.3.2. spi_driver
- struct spi_driver {
- const struct spi_device_id *id_table; /*Ö§³ÖµÄspi_deviceÉ豸±í*/
- 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´ú±íÒ»¸öSPI protocol drivers£¬¼´ÍâÉèÇý¶¯
2.3.3. struct spi_master
- struct spi_master {
- struct device dev; /*spi¿ØÖÆÆ÷¶ÔÓ¦µÄdevice½á¹¹*/
- struct list_head 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; /*×ÜÏߣ¨»ò¿ØÖÆÆ÷±àºÅ£©*/
- /* chipselects will be integral to many controllers; some others
- * might use board-specific GPIOs.
- */
- u16 num_chipselect; /*ƬѡÊýÁ¿*/
- /* 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Ö§³ÖµÄÉ豸ģʽ */
- /* bitmask of supported bits_per_word for transfers */
- u32 bits_per_word_mask;
- /* other constraints relevant to this driver */
- u16 flags; /*ÓÃÓÚÏÞ¶¨Ä³Ð©ÏÞÖÆÌõ¼þµÄ±ê־λ
- #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); /*¸ù¾ÝspiÉ豸¸üÐÂÓ²¼þÅäÖá£ÉèÖÃspi¹¤×÷ģʽ¡¢Ê±ÖÓµÈ*/
- /* 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); /*Ìí¼ÓÏûÏ¢µ½¶ÓÁеķ½·¨£¬´Ëº¯Êý²»¿É˯Ãß¡£ËüµÄÖ°ÔðÊÇ°²ÅÅ·¢ÉúµÄ´«ËͲ¢ÇÒµ÷ÓÃ×¢²áµÄ»Øµ÷º¯Êýcomplete()*/
- /* called on release() to free memory provided by spi_master */
- void (*cleanup)(struct spi_device *spi);/*cleanupº¯Êý»áÔÚspidev_releaseº¯ÊýÖб»µ÷Óã¬spidev_release±»µÇ¼ÇΪspi devµÄreleaseº¯Êý¡£*/
- /*
- * 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; /*ÓÃÓÚ¹ÜÀíÊý¾Ý´«ÊäÏûÏ¢¶ÓÁеŤ×÷¶ÓÁÐÏß³Ì*/
- struct task_struct *kworker_task;
- struct kthread_work pump_messages; /*¾ßÌåʵÏÖÊý¾Ý´«Êä¶ÓÁеŤ×÷¶ÓÁÐ*/
- spinlock_t queue_lock;
- struct list_head queue; /*¸Ã¿ØÖÆÆ÷µÄÏûÏ¢¶ÓÁУ¬ËùÓеȴý´«ÊäµÄ¶ÓÁйÒÔÚ¸ÃÁ´±íÏÂ*/
- struct spi_message *cur_msg;/*µ±Ç°ÕýÔÚ´¦ÀíµÄÏûÏ¢¶ÓÁÐ*/
- bool busy; /æ״̬*/
- bool running; /*ÕýÔÚÅÜ*/
- bool rt;
- int (*prepare_transfer_hardware)(struct spi_master *master); /*»Øµ÷º¯Êý£¬Õýʽ·¢Æð´«ÊäÇ°»á±»µ÷Óã¬ÓÃÓÚ×¼±¸Ó²¼þ×ÊÔ´*/
- int (*transfer_one_message)(struct spi_master *master, struct spi_message *mesg); /*µ¥¸öÏûÏ¢µÄÔ×Ó´«Êä»Øµ÷º¯Êý£¬¶ÓÁÐÖÐÿ¸öÏûÏ¢¶¼»á»Øµ÷Ò»´Î¸Ã»Øµ÷À´Íê³É´«Ê乤×÷*/
- int (*unprepare_transfer_hardware)(struct spi_master *master); /*ÇåÀí»Øµ÷º¯Êý*/
- /* gpio chip select */
- int *cs_gpios;
- };
spi_master´ú±íÒ»¸öspi¿ØÖÆÆ÷¡£
2.3.4. struct spi_message ºÍspi_transfer
ÒªÍê³ÉºÍSPIÉ豸µÄÊý¾Ý´«Ê乤×÷£¬ÎÒÃÇ»¹ÐèÒªÁíÍâÁ½¸öÊý¾Ý½á¹¹£ºspi_messageºÍspi_transfer¡£
spi_message°üº¬ÁËÒ»¸öµÄspi_transfer½á¹¹ÐòÁУ¬Ò»µ©¿ØÖÆÆ÷½ÓÊÕÁËÒ»¸öspi_message£¬ÆäÖеÄspi_transferÓ¦¸Ã°´Ë³Ðò±»·¢ËÍ£¬²¢ÇÒ²»Äܱ»ÆäËüspi_message´ò¶Ï£¬ËùÒÔÎÒÃÇÈÏΪspi_message¾ÍÊÇÒ»´ÎSPIÊý¾Ý½»»»µÄÔ×Ó²Ù×÷¡£ÏÂÃæÎÒÃÇ¿´¿´ÕâÁ½¸öÊý¾Ý½á¹¹µÄ¶¨Ò壺
struct spi_message £º
- struct spi_message {
- struct list_head transfers; /*spi_transferÁ´±í¶ÓÁУ¬´Ë´ÎÏûÏ¢µÄ´«Êä¶Î¶ÓÁУ¬Ò»¸öÏûÏ¢¿ÉÒÔ°üº¬¶à¸ö´«Êä¶Î¡£*/
- struct spi_device *spi; /*´«ÊäµÄÄ¿µÄÉ豸*/
- unsigned is_dma_mapped:1; /*Èç¹ûΪÕ棬´Ë´Îµ÷ÓÃÌṩdmaºÍcpuÐéÄâµØÖ·¡£*/
- /* 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);/*Òì²½µ÷ÓÃÍê³ÉºóµÄ»Øµ÷º¯Êý*/
- void *context; /*»Øµ÷º¯ÊýµÄ²ÎÊý*/
- unsigned actual_length; /*ʵ¼Ê´«ÊäµÄ³¤¶È*/
- int status; /*¸ÃÏûÏ¢µÄ·¢Ëͽá¹û£¬³É¹¦±»ÖÃ0£¬·ñÔòÊÇÒ»¸ö¸ºµÄ´íÎóÂë¡£*/
- /* 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;
- };
Á´±í×Ö¶ÎqueueÓÃÓڰѸýṹ¹ÒÔÚ´ú±í¿ØÖÆÆ÷µÄspi_master½á¹¹µÄqueue×Ö¶ÎÉÏ£¬¿ØÖÆÆ÷ÉÏ¿ÉÒÔͬʱ±»¼ÓÈë¶à¸öspi_message½øÐÐÅŶӡ£ÁíÒ»¸öÁ´±í×Ö¶ÎtransfersÔòÓÃÓÚÁ´½Ó¹ÒÔÚ±¾messageϵÄspi_tranfer½á¹¹¡£complete»Øµ÷º¯ÊýÔò»áÔÚ¸ÃmessageϵÄËùÓÐspi_transfer¶¼±»´«ÊäÍê³Éʱ±»µ÷Óã¬ÒÔ±ã֪ͨÐÒéÇý¶¯´¦Àí½ÓÊÕµ½µÄÊý¾ÝÒÔ¼°×¼±¸ÏÂÒ»ÅúÐèÒª·¢Ë͵ÄÊý¾Ý¡£ÎÒÃÇÔÙÀ´¿´¿´spi_transfer½á¹¹£º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; /*·¢ËÍ»º³åÇø*/
- void *rx_buf; /*½ÓÊÕ»º³åÇø*/
- unsigned len; /*»º³åÇø³¤¶È£¬txºÍrxµÄ´óС£¨×Ö½ÚÊý£©¡£Ö¸ËüÃǸ÷×ԵĴóС*/
- dma_addr_t tx_dma; /*txµÄdmaµØÖ·*/
- dma_addr_t rx_dma; /*rxµÄdmaµØÖ·*/
- unsigned cs_change:1; /*µ±Ç°spi_transfer·¢ËÍÍê³ÉÖ®ºóÖØÐÂƬѡ*/
- u8 bits_per_word; /*ÿ¸ö×Ö³¤µÄ±ÈÌØÊý£¬0´ú±íʹÓÃspi_deviceÖеÄĬÈÏÖµ8*/
- u16 delay_usecs; /*·¢ËÍÍê³ÉÒ»¸öspi_transferºóµÄÑÓʱʱ¼ä£¬´Ë´Î´«Êä½áÊøºÍƬѡ¸Ä±äÖ®¼äµÄÑÓʱ£¬Ö®ºó¾Í»áÆô¶¯ÁíÒ»¸ö´«Êä»òÕß½áÊøÕû¸öÏûÏ¢*/
- u32 speed_hz; /*ͨÐÅʱÖÓ¡£Èç¹ûÊÇ0£¬Ê¹ÓÃĬÈÏÖµ*/
- #ifdef CONFIG_SPI_LOMBO
- struct lombo_spi_operate_para *esop;
- #endif
- struct list_head transfer_list; /*ÓÃÓÚÁ´½Óµ½spi_message£¬ÓÃÀ´Á¬½ÓµÄË«ÏòÁ´½Ó½Úµã*/
- };
Ê×ÏÈ£¬transfer_listÁ´±í×Ö¶ÎÓÃÓڰѸÃtransfer¹ÒÔÚÒ»¸öspi_message½á¹¹ÖУ¬tx_bufºÍrx_bufÌṩÁË·ÇdmaģʽϵÄÊý¾Ý»º³åÇøµØÖ·£¬lenÔòÊÇÐèÒª´«ÊäÊý¾ÝµÄ³¤¶È£¬tx_dmaºÍrx_dmaÔò¸ø³öÁËdmaģʽϵĻº³åÇøµØÖ·¡£ÔÔòÀ´½²£¬spi_transfer²ÅÊÇ´«ÊäµÄ×îСµ¥Î»£¬Ö®ËùÒÔÓÖÒý½øÁËspi_message½øÐдò°ü£¬ÎÒ¾õµÃÔÒòÊÇ£ºÓÐʱºòÏ£ÍûÍùspiÉ豸µÄ¶à¸ö²»Á¬ÐøµÄµØÖ·(»ò¼Ä´æÆ÷)Ò»´ÎÐÔдÈ룬Èç¹ûûÓÐspi_message½øÐаÑÕâÑùµÄ¶à¸öspi_transfer´ò°ü£¬ÒòΪͨ³£ÕæÕýµÄÊý¾Ý´«Ë͹¤×÷ÊÇÔÚÁíÒ»¸öÄÚºËÏß³Ì(¹¤×÷¶ÓÁÐ)ÖÐÍê³ÉµÄ£¬²»´ò°üµÄºó¹û¾ÍÊÇ»áÔì³É¸ü¶àµÄ½ø³ÌÇл»£¬Ð§ÂʽµµÍ£¬ÑÓ³ÙÔö¼Ó£¬ÓÈÆä¶ÔÓÚ¶à¸ö²»Á¬ÐøµØÖ·µÄС¹æÄ£Êý¾Ý´«ËͶøÑԾ͸üΪÃ÷ÏÔ¡£
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]; /*Ãû×Ö*/
- const void *platform_data; /*ƽ̨Êý¾Ý*/
- void *controller_data; /*¿ØÖÆÆ÷Êý¾Ý*/
- int irq;
- /* slower signaling on noisy or low voltage boards */
- u32 max_speed_hz; /*×î´óËÙÂÊ*/
- /* 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×ÜÏß±àºÅ*/
- u16 chip_select; /*Ƭѡ*/
- /* mode becomes spi_device.mode, and is essential for chips
- * where the default of SPI_CS_HIGH = 0 is wrong.
- */
- u8 mode; /*ģʽ */
- /* ... 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. Êý¾Ý´«ÊäÁ÷³Ì
ÕûÌåµÄÊý¾Ý´«ÊäÁ÷³Ì´óÖÂÉÏÊÇÕâÑùµÄ:
´«ÊäʾÒâͼÈçͼ2-5Ëùʾ£º
2.4.1. Êý¾Ý×¼±¸
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);
- }
³õʼ»¯spi_message£ºÇå¿Õmessage£¬³õʼ»¯transfersÁ´±íÍ·¡£
2.4.1.2. spi_message_add_tail
- static inline void
- spi_message_add_tail(struct spi_transfer *t, struct spi_message *m)
- {
- list_add_tail(&t->transfer_list, &m->transfers);
- }
½«spi_transfer¼ÓÈëµ½spi_messageµÄÁ´±íβ²¿¡£
2.4.2. Êý¾Ý´«Êä
SPIÊý¾Ý´«Êä¿ÉÒÔÓÐÁ½ÖÖ·½Ê½£ºÍ¬²½·½Ê½ºÍÒì²½·½Ê½¡£Ëùνͬ²½·½Ê½ÊÇÖ¸Êý¾Ý´«ÊäµÄ·¢ÆðÕß±ØÐëµÈ´ý±¾´Î´«ÊäµÄ½áÊø£¬Æڼ䲻ÄÜ×öÆäËüÊÂÇ飬ÓôúÂëÀ´½âÊ;ÍÊÇ£¬µ÷Óô«ÊäµÄº¯Êýºó£¬Ö±µ½Êý¾Ý´«ÊäÍê³É£¬º¯Êý²Å»á·µ»Ø¡£¶øÒì²½·½Ê½ÔòÕýºÃÏà·´£¬Êý¾Ý´«ÊäµÄ·¢ÆðÕßÎÞÐèµÈ´ý´«ÊäµÄ½áÊø£¬Êý¾Ý´«ÊäÆڼ仹¿ÉÒÔ×öÆäËüÊÂÇ飬ÓôúÂëÀ´½âÊ;ÍÊÇ£¬µ÷Óô«ÊäµÄº¯Êýºó£¬º¯Êý»áÁ¢¿Ì·µ»Ø¶ø²»ÓõȴýÊý¾Ý´«ÊäÍê³É£¬ÎÒÃÇÖ»ÐèÉèÖÃÒ»¸ö»Øµ÷º¯Êý£¬´«ÊäÍê³Éºó£¬¸Ã»Øµ÷º¯Êý»á±»µ÷ÓÃÒÔ֪ͨ·¢ÆðÕßÊý¾Ý´«ËÍÒѾÍê³É¡£Í¬²½·½Ê½¼òµ¥Ò×Ó㬺ÜÊʺϴ¦ÀíÄÇЩÉÙÁ¿Êý¾ÝµÄµ¥´Î´«Êä¡£µ«ÊǶÔÓÚÊý¾ÝÁ¿´ó¡¢´ÎÊý¶àµÄ´«ÊäÀ´Ëµ£¬Òì²½·½Ê½¾ÍÏԵøü¼ÓºÏÊÊ¡£¶ÔÓÚSPI¿ØÖÆÆ÷À´Ëµ£¬ÒªÖ§³ÖÒì²½·½Ê½±ØÐëÒª¿¼ÂÇÒÔÏÂÁ½ÖÖ×´¿ö£º
Æä´Î·ÖÎöspi_async_locked½Ó¿ÚµÄʵÏÖÁ÷³Ì£¬Èçͼ2-7Ëùʾ£º
spi_queued_transfer½Ó¿ÚµÄʵÏÖÁ÷³ÌÈçͼ3-8Ëùʾ£º
spi_pump_messagesº¯ÊýµÄ´¦ÀíÁ÷³ÌÈçͼ3-9Ëùʾ£º
ͼÖÐtransfer_one_messageÊÇspi¿ØÖÆÆ÷Çý¶¯ÒªÊµÏֵģ¬Ö÷Òª¹¦ÄÜÊÇ´¦Àíspi_messageÖеÄÿ¸öspi_transfer¡£
2.5. ¹Ø¼üº¯Êý½âÎö
2.5.1. spi_alloc_master
ÔÐÍ£º
- struct spi_master *spi_alloc_master(struct device *dev, unsigned size)
¹¦ÄÜ£º·ÖÅäÒ»¸öspi_master½á¹¹ÌåÖ¸Õë¡£
²ÎÊý£ºdev:spi¿ØÖÆÆ÷deviceÖ¸Õë size £º·ÖÅäµÄdriver-private data´óС
·µ»ØÖµ £º³É¹¦£¬·µ»Øspi_masterÖ¸Õë;·ñÔò·µ»ØNULL
2.5.2. spi_register_master
ÔÐÍ£º
- int spi_register_master(struct spi_master *master)
¹¦ÄÜ ×¢²áspi¿ØÖÆÆ÷Çý¶¯µ½Äںˡ£
²ÎÊý master£ºspi_masterÖ¸Õë
·µ»ØÖµ ³É¹¦£¬·µ»Ø0;·ñÔò·µ»Ø´íÎóÂë
2.5.3. spi_unregister_master
ÔÐÍ£º
- void spi_unregister_master(struct spi_master *master)
¹¦ÄÜ ×¢Ïúspi¿ØÖÆÆ÷Çý¶¯¡£
²ÎÊý master£ºspi_masterÖ¸Õë
·µ»ØÖµ ÎÞ
3. Demo
(²Î¿¼×ÔÕýµãÔ×Ó)
- #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.
- ÎļþÃû : icm20608.c
- ×÷Õß : ×ó¹¤
- °æ±¾ : V1.0
- ÃèÊö : ICM20608 SPIÇý¶¯³ÌÐò
- ÆäËû : ÎÞ
- ÂÛ̳ :
- ÈÕÖ¾ : ³õ°æV1.0 2019/9/2 ×󹤴´½¨
- ***************************************************************/
- #define ICM20608_CNT 1
- #define ICM20608_NAME "icm20608"
- struct icm20608_dev {
- dev_t devid; /* É豸ºÅ */
- struct cdev cdev; /* cdev */
- struct class *class; /* Àà */
- struct device *device; /* É豸 */
- struct device_node *nd; /* É豸½Úµã */
- int major; /* Ö÷É豸ºÅ */
- void *private_data; /* ˽ÓÐÊý¾Ý */
- int cs_gpio; /* ƬѡËùʹÓõÄGPIO±àºÅ */
- signed int gyro_x_adc; /* ÍÓÂÝÒÇXÖáÔʼֵ */
- signed int gyro_y_adc; /* ÍÓÂÝÒÇYÖáÔʼֵ */
- signed int gyro_z_adc; /* ÍÓÂÝÒÇZÖáÔʼֵ */
- signed int accel_x_adc; /* ¼ÓËٶȼÆXÖáÔʼֵ */
- signed int accel_y_adc; /* ¼ÓËٶȼÆYÖáÔʼֵ */
- signed int accel_z_adc; /* ¼ÓËٶȼÆZÖáÔʼֵ */
- signed int temp_adc; /* ζÈÔʼֵ */
- };
- static struct icm20608_dev icm20608dev;
- /*
- * @description : ´Óicm20608¶ÁÈ¡¶à¸ö¼Ä´æÆ÷Êý¾Ý
- * @param - dev: icm20608É豸
- * @param - reg: Òª¶ÁÈ¡µÄ¼Ä´æÆ÷Ê×µØÖ·
- * @param - val: ¶ÁÈ¡µ½µÄÊý¾Ý
- * @param - len: Òª¶ÁÈ¡µÄÊý¾Ý³¤¶È
- * @return : ²Ù×÷½á¹û
- */
- 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); /* ƬѡÀµÍ£¬Ñ¡ÖÐICM20608 */
- t = kzalloc(sizeof(struct spi_transfer), GFP_KERNEL); /* ÉêÇëÄÚ´æ */
- /* µÚ1´Î£¬·¢ËÍÒª¶ÁÈ¡µÄ¼Ä´æµØÖ· */
- txdata[0] = reg | 0x80; /* дÊý¾ÝµÄʱºò¼Ä´æÆ÷µØÖ·bit8ÒªÖÃ1 */
- t->tx_buf = txdata; /* Òª·¢Ë͵ÄÊý¾Ý */
- t->len = 1; /* 1¸ö×Ö½Ú */
- spi_message_init(&m); /* ³õʼ»¯spi_message */
- spi_message_add_tail(t, &m);/* ½«spi_transferÌí¼Óµ½spi_message¶ÓÁÐ */
- ret = spi_sync(spi, &m); /* ͬ²½·¢ËÍ */
- /* µÚ2´Î£¬¶ÁÈ¡Êý¾Ý */
- txdata[0] = 0xff; /* Ëæ±ãÒ»¸öÖµ£¬´Ë´¦ÎÞÒâÒå */
- t->rx_buf = buf; /* ¶ÁÈ¡µ½µÄÊý¾Ý */
- t->len = len; /* Òª¶ÁÈ¡µÄÊý¾Ý³¤¶È */
- spi_message_init(&m); /* ³õʼ»¯spi_message */
- spi_message_add_tail(t, &m);/* ½«spi_transferÌí¼Óµ½spi_message¶ÓÁÐ */
- ret = spi_sync(spi, &m); /* ͬ²½·¢ËÍ */
- kfree(t); /* ÊÍ·ÅÄÚ´æ */
- gpio_set_value(dev->cs_gpio, 1); /* ƬѡÀ¸ß£¬ÊÍ·ÅICM20608 */
- return ret;
- }
- /*
- * @description : Ïòicm20608¶à¸ö¼Ä´æÆ÷дÈëÊý¾Ý
- * @param - dev: icm20608É豸
- * @param - reg: ҪдÈëµÄ¼Ä´æÆ÷Ê×µØÖ·
- * @param - val: ҪдÈëµÄÊý¾Ý»º³åÇø
- * @param - len: ҪдÈëµÄÊý¾Ý³¤¶È
- * @return : ²Ù×÷½á¹û
- */
- 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); /* ÉêÇëÄÚ´æ */
- gpio_set_value(dev->cs_gpio, 0); /* ƬѡÀµÍ */
- /* µÚ1´Î£¬·¢ËÍÒª¶ÁÈ¡µÄ¼Ä´æµØÖ· */
- txdata[0] = reg & ~0x80; /* дÊý¾ÝµÄʱºò¼Ä´æÆ÷µØÖ·bit8ÒªÇåÁã */
- t->tx_buf = txdata; /* Òª·¢Ë͵ÄÊý¾Ý */
- t->len = 1; /* 1¸ö×Ö½Ú */
- spi_message_init(&m); /* ³õʼ»¯spi_message */
- spi_message_add_tail(t, &m);/* ½«spi_transferÌí¼Óµ½spi_message¶ÓÁÐ */
- ret = spi_sync(spi, &m); /* ͬ²½·¢ËÍ */
- /* µÚ2´Î£¬·¢ËÍҪдÈëµÄÊý¾Ý */
- t->tx_buf = buf; /* ҪдÈëµÄÊý¾Ý */
- t->len = len; /* дÈëµÄ×Ö½ÚÊý */
- spi_message_init(&m); /* ³õʼ»¯spi_message */
- spi_message_add_tail(t, &m);/* ½«spi_transferÌí¼Óµ½spi_message¶ÓÁÐ */
- ret = spi_sync(spi, &m); /* ͬ²½·¢ËÍ */
- kfree(t); /* ÊÍ·ÅÄÚ´æ */
- gpio_set_value(dev->cs_gpio, 1);/* ƬѡÀ¸ß£¬ÊÍ·ÅICM20608 */
- return ret;
- }
- /*
- * @description : ¶ÁÈ¡icm20608Ö¸¶¨¼Ä´æÆ÷Öµ£¬¶ÁÈ¡Ò»¸ö¼Ä´æÆ÷
- * @param - dev: icm20608É豸
- * @param - reg: Òª¶ÁÈ¡µÄ¼Ä´æÆ÷
- * @return : ¶ÁÈ¡µ½µÄ¼Ä´æÆ÷Öµ
- */
- 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 : Ïòicm20608Ö¸¶¨¼Ä´æÆ÷дÈëÖ¸¶¨µÄÖµ£¬Ð´Ò»¸ö¼Ä´æÆ÷
- * @param - dev: icm20608É豸
- * @param - reg: ҪдµÄ¼Ä´æÆ÷
- * @param - data: ҪдÈëµÄÖµ
- * @return : ÎÞ
- */
- static void icm20608_write_onereg(struct icm20608_dev *dev, u8 reg, u8 value)
- {
- u8 buf = value;
- icm20608_write_regs(dev, reg, &buf, 1);
- }
- /*
- * @description : ¶ÁÈ¡ICM20608µÄÊý¾Ý£¬¶ÁÈ¡ÔʼÊý¾Ý£¬°üÀ¨ÈýÖáÍÓÂÝÒÇ¡¢
- * : ÈýÖá¼ÓËٶȼƺÍÄÚ²¿Î¶ȡ£
- * @param - dev : ICM20608É豸
- * @return : ÎÞ¡£
- */
- 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 : ´ò¿ªÉ豸
- * @param - inode : ´«µÝ¸øÇý¶¯µÄinode
- * @param - filp : É豸Îļþ£¬file½á¹¹ÌåÓиö½Ð×öprivateate_dataµÄ³ÉÔ±±äÁ¿
- * Ò»°ãÔÚopenµÄʱºò½«private_dataËÆÓÐÏòÉ豸½á¹¹Ìå¡£
- * @return : 0 ³É¹¦;ÆäËû ʧ°Ü
- */
- static int icm20608_open(struct inode *inode, struct file *filp)
- {
- filp->private_data = &icm20608dev; /* ÉèÖÃ˽ÓÐÊý¾Ý */
- return 0;
- }
- /*
- * @description : ´ÓÉ豸¶ÁÈ¡Êý¾Ý
- * @param - filp : Òª´ò¿ªµÄÉ豸Îļþ(ÎļþÃèÊö·û)
- * @param - buf : ·µ»Ø¸øÓû§¿Õ¼äµÄÊý¾Ý»º³åÇø
- * @param - cnt : Òª¶ÁÈ¡µÄÊý¾Ý³¤¶È
- * @param - offt : Ïà¶ÔÓÚÎļþÊ×µØÖ·µÄÆ«ÒÆ
- * @return : ¶ÁÈ¡µÄ×Ö½ÚÊý£¬Èç¹ûΪ¸ºÖµ£¬±íʾ¶Áȡʧ°Ü
- */
- 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 : ¹Ø±Õ/ÊÍ·ÅÉ豸
- * @param - filp : Òª¹Ø±ÕµÄÉ豸Îļþ(ÎļþÃèÊö·û)
- * @return : 0 ³É¹¦;ÆäËû ʧ°Ü
- */
- static int icm20608_release(struct inode *inode, struct file *filp)
- {
- return 0;
- }
- /* icm20608²Ù×÷º¯Êý */
- static const struct file_operations icm20608_ops = {
- .owner = THIS_MODULE,
- .open = icm20608_open,
- .read = icm20608_read,
- .release = icm20608_release,
- };
- /*
- * ICM20608ÄÚ²¿¼Ä´æÆ÷³õʼ»¯º¯Êý
- * @param : ÎÞ
- * @return : ÎÞ
- */
- 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); /* Êä³öËÙÂÊÊÇÄÚ²¿²ÉÑùÂÊ */
- icm20608_write_onereg(&icm20608dev, ICM20_GYRO_CONFIG, 0x18); /* ÍÓÂÝÒDZ2000dpsÁ¿³Ì */
- icm20608_write_onereg(&icm20608dev, ICM20_ACCEL_CONFIG, 0x18); /* ¼ÓËٶȼƱ16GÁ¿³Ì */
- icm20608_write_onereg(&icm20608dev, ICM20_CONFIG, 0x04); /* ÍÓÂÝÒǵÍͨÂ˲¨BW=20Hz */
- icm20608_write_onereg(&icm20608dev, ICM20_ACCEL_CONFIG2, 0x04); /* ¼ÓËٶȼƵÍͨÂ˲¨BW=21.2Hz */
- icm20608_write_onereg(&icm20608dev, ICM20_PWR_MGMT_2, 0x00); /* ´ò¿ª¼ÓËٶȼƺÍÍÓÂÝÒÇËùÓÐÖá */
- icm20608_write_onereg(&icm20608dev, ICM20_LP_MODE_CFG, 0x00); /* ¹Ø±ÕµÍ¹¦ºÄ */
- icm20608_write_onereg(&icm20608dev, ICM20_FIFO_EN, 0x00); /* ¹Ø±ÕFIFO */
- }
- /*
- * @description : spiÇý¶¯µÄprobeº¯Êý£¬µ±Çý¶¯Óë
- * É豸ƥÅäÒÔºó´Ëº¯Êý¾Í»áÖ´ÐÐ
- * @param - client : spiÉ豸
- * @param - id : spiÉ豸ID
- *
- */
- static int icm20608_probe(struct spi_device *spi)
- {
- int ret = 0;
- /* 1¡¢¹¹½¨É豸ºÅ */
- 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¡¢×¢²áÉ豸 */
- cdev_init(&icm20608dev.cdev, &icm20608_ops);
- cdev_add(&icm20608dev.cdev, icm20608dev.devid, ICM20608_CNT);
- /* 3¡¢´´½¨Àà */
- icm20608dev.class = class_create(THIS_MODULE, ICM20608_NAME);
- if (IS_ERR(icm20608dev.class)) {
- return PTR_ERR(icm20608dev.class);
- }
- /* 4¡¢´´½¨É豸 */
- icm20608dev.device = device_create(icm20608dev.class, NULL, icm20608dev.devid, NULL, ICM20608_NAME);
- if (IS_ERR(icm20608dev.device)) {
- return PTR_ERR(icm20608dev.device);
- }
- /* »ñÈ¡É豸Ê÷ÖÐcsƬѡÐźŠ*/
- icm20608dev.nd = of_find_node_by_path("/soc/aips-bus@02000000/spba-bus@02000000/ecspi@02010000");
- if(icm20608dev.nd == NULL) {
- printk("ecspi3 node not find!\r\n");
- return -EINVAL;
- }
- /* 2¡¢ »ñÈ¡É豸Ê÷ÖеÄgpioÊôÐÔ£¬µÃµ½BEEPËùʹÓõÄBEEP±àºÅ */
- 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¡¢ÉèÖÃGPIO1_IO20ΪÊä³ö£¬²¢ÇÒÊä³ö¸ßµçƽ */
- ret = gpio_direction_output(icm20608dev.cs_gpio, 1);
- if(ret < 0) {
- printk("can't set gpio!\r\n");
- }
- /*³õʼ»¯spi_device */
- spi->mode = SPI_MODE_0; /*MODE0£¬CPOL=0£¬CPHA=0*/
- spi_setup(spi);
- icm20608dev.private_data = spi; /* ÉèÖÃ˽ÓÐÊý¾Ý */
- /* ³õʼ»¯ICM20608ÄÚ²¿¼Ä´æÆ÷ */
- icm20608_reginit();
- return 0;
- }
- /*
- * @description : spiÇý¶¯µÄremoveº¯Êý£¬ÒƳýspiÇý¶¯µÄʱºò´Ëº¯Êý»áÖ´ÐÐ
- * @param - client : spiÉ豸
- * @return : 0£¬³É¹¦;ÆäËû¸ºÖµ,ʧ°Ü
- */
- static int icm20608_remove(struct spi_device *spi)
- {
- /* ɾ³ýÉ豸 */
- cdev_del(&icm20608dev.cdev);
- unregister_chrdev_region(icm20608dev.devid, ICM20608_CNT);
- /* ×¢ÏúµôÀàºÍÉ豸 */
- device_destroy(icm20608dev.class, icm20608dev.devid);
- class_destroy(icm20608dev.class);
- return 0;
- }
- /* ´«Í³Æ¥Å䷽ʽIDÁбí */
- static const struct spi_device_id icm20608_id[] = {
- {"alientek,icm20608", 0},
- {}
- };
- /* É豸Ê÷Æ¥ÅäÁбí */
- static const struct of_device_id icm20608_of_match[] = {
- { .compatible = "alientek,icm20608" },
- { /* Sentinel */ }
- };
- /* SPIÇý¶¯½á¹¹Ìå */
- 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 : Çý¶¯Èë¿Úº¯Êý
- * @param : ÎÞ
- * @return : ÎÞ
- */
- static int __init icm20608_init(void)
- {
- return spi_register_driver(&icm20608_driver);
- }
- /*
- * @description : Çý¶¯³ö¿Úº¯Êý
- * @param : ÎÞ
- * @return : ÎÞ
- */
- 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");
×î½üÔÚѧϰjQueryʱ½Ó´¥µ½ÁËshow()¡¢hide()¡¢toggle()º¯Êý£¬ÓÚÊÇÀûÓÃÕ⼸¸öº¯Êý...
Ïê½âSpring Controller autowired Request±äÁ¿ springµÄDI´ó¼Ò±È½ÏÊìϤÁË£¬¶ÔÓÚ...
¶àÄêÒÔºó£¬Ãæ¶Ǫ̂ÏÂÎå²Ê°ßìµµÄJetbrainºÍVscodeÓû§£¬ÕâλÔø¾µÄ×ÊÉîµÄvim×·ËæÕß...
ÔÚеÄMySQL 8.0.23ÖУ¬ÒýÈëÁËеÄÓÐȤ¹¦ÄÜ£º²»¿É¼ûÁС£ ÕâÊǵÚһƪ¹ØÓÚÕâ¸öй¦...
ÐèҪעÒâµÄÊÇ£¬µ÷Óõķâ×°µÄÊý¾Ý¿â£¬ºÍjQueryµÄ±£´æµØÖ· Ò»¡¢×¢²á £¨1£©Ð´Îı¾¿ò...
Ò»¸ö³£¼ûµÄ³¡¾°£¬»ñÈ¡£º±êÇ©±³¾°Í¼Æ¬Á´½Ó£º Èç×Ö·û´®£ºvar bgImg = "url (\" htt...
git cloneÖ§³ÖhttpsºÍgit£¨¼´ssh£©Á½ÖÖ·½Ê½ÏÂÔØÔ´Â룺 µ±Ê¹ÓÃgit·½Ê½ÏÂÔØʱ£¬Èç...
1 ¸ÅÊö ÔÚ½ÓÏÂÀ´µÄʱ¼äÀ½«»áÈëÊÖASP.NET MVCÕâһרÌ⣬¾¡Á¿ÓÃ×î¿ìµÄʱ¼ä£¬×î...
´ó¼ÒºÃÎÒÊÇ°®¾°ÌðµÄÍø¹¤ÎÒÊÇÒ»¸ö˼¿Æ³öÉíרעÓÚ»ªÎªµÄÍø¹¤ ºÃÁË»°²»¶à˵½øÈëÕýÌâ...
ÔÚAsp.net Core֮ǰËùÓеÄAction·µ»ØÖµ¶¼ÊÇActionResult£¬Json(),File()µÈ·½·¨·µ...