二十一、spi驱动框架及驱动代码分析
一、spi驱动框架简介
- spi核心层
提供spi控制器驱动和设备驱动的注册方法、注销方法、spi通信硬件无关接口
- spi主机驱动
主要包含spi硬件体系结构中适配器(spi控制器)的控制,用于产生spi读写时序。
- spi设备驱动
通过spi主机驱动与CPU交换数据。
二、驱动源码分析
1、spidev.c
(1)初始化
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | static int __init spidev_init( void ) { int status; /* Claim our 256 reserved device numbers. Then register a class * that will key udev/mdev to add/remove /dev nodes. Last, register * the driver which manages those device numbers. */ BUILD_BUG_ON(N_SPI_MINORS > 256); status = register_chrdev(SPIDEV_MAJOR, "spi" , &spidev_fops); //注册字符设备 if (status < 0) return status; spidev_class = class_create(THIS_MODULE, "spidev" ); //创建设备类 if (IS_ERR(spidev_class)) { unregister_chrdev(SPIDEV_MAJOR, spidev_spi_driver.driver.name); return PTR_ERR(spidev_class); } status = spi_register_driver(&spidev_spi_driver); //注册spi驱动 if (status < 0) { class_destroy(spidev_class); unregister_chrdev(SPIDEV_MAJOR, spidev_spi_driver.driver.name); } return status; } |
(2)spi驱动定义
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | static struct spi_driver spidev_spi_driver = { .driver = { .name = "spidev" , .of_match_table = of_match_ptr(spidev_dt_ids), .acpi_match_table = ACPI_PTR(spidev_acpi_ids), }, .probe = spidev_probe, . remove = spidev_remove, /* NOTE: suspend/resume methods are not necessary here. * We don't do anything except pass the requests to/from * the underlying controller. The refrigerator handles * most issues; the controller driver handles the rest. */ }; |
(3)spidev_probe
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 | static int spidev_probe( struct spi_device *spi) { struct spidev_data *spidev; int status; unsigned long minor; /* * spidev should never be referenced in DT without a specific * compatible string, it is a Linux implementation thing * rather than a description of the hardware. */ WARN(spi->dev.of_node && of_device_is_compatible(spi->dev.of_node, "spidev" ), "%pOF: buggy DT: spidev listed directly in DT\n" , spi->dev.of_node); spidev_probe_acpi(spi); /* Allocate driver data */ spidev = kzalloc( sizeof (*spidev), GFP_KERNEL); if (!spidev) return -ENOMEM; /* Initialize the driver data */ spidev->spi = spi; spin_lock_init(&spidev->spi_lock); mutex_init(&spidev->buf_lock); INIT_LIST_HEAD(&spidev->device_entry); /* If we can allocate a minor number, hook up this device. * Reusing minors is fine so long as udev or mdev is working. */ mutex_lock(&device_list_lock); minor = find_first_zero_bit(minors, N_SPI_MINORS); if (minor < N_SPI_MINORS) { //次设备号小于32 struct device *dev; spidev->devt = MKDEV(SPIDEV_MAJOR, minor); //创建spi设备 dev = device_create(spidev_class, &spi->dev, spidev->devt, spidev, "spidev%d.%d" , spi->master->bus_num, spi->chip_select); status = PTR_ERR_OR_ZERO(dev); } else { dev_dbg(&spi->dev, "no minor number available!\n" ); status = -ENODEV; } if (status == 0) { set_bit(minor, minors); list_add(&spidev->device_entry, &device_list); } mutex_unlock(&device_list_lock); spidev->speed_hz = spi->max_speed_hz; //设置最大速率 if (status == 0) spi_set_drvdata(spi, spidev); //设置设备驱动数据 else kfree(spidev); return status; } |
(4)spidev_remove
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | static int spidev_remove( struct spi_device *spi) { struct spidev_data *spidev = spi_get_drvdata(spi); /* prevent new opens */ mutex_lock(&device_list_lock); /* make sure ops on existing fds can abort cleanly */ spin_lock_irq(&spidev->spi_lock); spidev->spi = NULL; spin_unlock_irq(&spidev->spi_lock); list_del(&spidev->device_entry); device_destroy(spidev_class, spidev->devt); clear_bit(MINOR(spidev->devt), minors); if (spidev->users == 0) kfree(spidev); mutex_unlock(&device_list_lock); return 0; } |
(5)spi设备文件操作集合
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | static const struct file_operations spidev_fops = { .owner = THIS_MODULE, /* REVISIT switch to aio primitives, so that userspace * gets more complete API coverage. It'll simplify things * too, except for the locking. */ .write = spidev_write, .read = spidev_read, .unlocked_ioctl = spidev_ioctl, .compat_ioctl = spidev_compat_ioctl, .open = spidev_open, .release = spidev_release, .llseek = no_llseek, }; |
(6)读操作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | /* Read-only message with current device setup */ static ssize_t spidev_read( struct file *filp, char __user *buf, size_t count, loff_t *f_pos) { struct spidev_data *spidev; ssize_t status = 0; /* chipselect only toggles at start or end of operation */ if (count > bufsiz) return -EMSGSIZE; spidev = filp->private_data; mutex_lock(&spidev->buf_lock); status = spidev_sync_read(spidev, count); if (status > 0) { unsigned long missing; missing = copy_to_user(buf, spidev->rx_buffer, status); if (missing == status) status = -EFAULT; else status = status - missing; } mutex_unlock(&spidev->buf_lock); return status; } |
1 | spidev_sync_read: |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | static inline ssize_t spidev_sync_read( struct spidev_data *spidev, size_t len) { struct spi_transfer t = { .rx_buf = spidev->rx_buffer, .len = len, .speed_hz = spidev->speed_hz, }; struct spi_message m; spi_message_init(&m); spi_message_add_tail(&t, &m); return spidev_sync(spidev, &m); } |
(7)写操作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | /* Write-only message with current device setup */ static ssize_t spidev_write( struct file *filp, const char __user *buf, size_t count, loff_t *f_pos) { struct spidev_data *spidev; ssize_t status = 0; unsigned long missing; /* chipselect only toggles at start or end of operation */ if (count > bufsiz) return -EMSGSIZE; spidev = filp->private_data; mutex_lock(&spidev->buf_lock); missing = copy_from_user(spidev->tx_buffer, buf, count); if (missing == 0) status = spidev_sync_write(spidev, count); else status = -EFAULT; mutex_unlock(&spidev->buf_lock); return status; } |
1 | spidev_sync_write: |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | static inline ssize_t spidev_sync_write( struct spidev_data *spidev, size_t len) { struct spi_transfer t = { .tx_buf = spidev->tx_buffer, .len = len, .speed_hz = spidev->speed_hz, }; struct spi_message m; spi_message_init(&m); spi_message_add_tail(&t, &m); return spidev_sync(spidev, &m); } |
(8)spidev_ioctl
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 | static long spidev_ioctl( struct file *filp, unsigned int cmd, unsigned long arg) { int retval = 0; struct spidev_data *spidev; struct spi_device *spi; u32 tmp; unsigned n_ioc; struct spi_ioc_transfer *ioc; /* Check type and command number */ if (_IOC_TYPE(cmd) != SPI_IOC_MAGIC) return -ENOTTY; /* guard against device removal before, or while, * we issue this ioctl. */ spidev = filp->private_data; spin_lock_irq(&spidev->spi_lock); spi = spi_dev_get(spidev->spi); spin_unlock_irq(&spidev->spi_lock); if (spi == NULL) return -ESHUTDOWN; /* use the buffer lock here for triple duty: * - prevent I/O (from us) so calling spi_setup() is safe; * - prevent concurrent SPI_IOC_WR_* from morphing * data fields while SPI_IOC_RD_* reads them; * - SPI_IOC_MESSAGE needs the buffer locked "normally". */ mutex_lock(&spidev->buf_lock); switch (cmd) { /* read requests */ 读取spi的属性 case SPI_IOC_RD_MODE: //读取spi mode retval = put_user(spi->mode & SPI_MODE_MASK, (__u8 __user *)arg); break ; case SPI_IOC_RD_MODE32: retval = put_user(spi->mode & SPI_MODE_MASK, (__u32 __user *)arg); break ; case SPI_IOC_RD_LSB_FIRST: //读取spi是低位优先还是高位优先 retval = put_user((spi->mode & SPI_LSB_FIRST) ? 1 : 0, (__u8 __user *)arg); break ; case SPI_IOC_RD_BITS_PER_WORD: //读取每个字 的bit位数 retval = put_user(spi->bits_per_word, (__u8 __user *)arg); break ; case SPI_IOC_RD_MAX_SPEED_HZ: //最大速率 retval = put_user(spidev->speed_hz, (__u32 __user *)arg); break ; /* write requests */ 这是对应的写,用于设置spi属性 case SPI_IOC_WR_MODE: case SPI_IOC_WR_MODE32: if (cmd == SPI_IOC_WR_MODE) retval = get_user(tmp, (u8 __user *)arg); else retval = get_user(tmp, (u32 __user *)arg); if (retval == 0) { struct spi_controller *ctlr = spi->controller; u32 save = spi->mode; if (tmp & ~SPI_MODE_MASK) { retval = -EINVAL; break ; } if (ctlr->use_gpio_descriptors && ctlr->cs_gpiods && ctlr->cs_gpiods[spi->chip_select]) tmp |= SPI_CS_HIGH; tmp |= spi->mode & ~SPI_MODE_MASK; spi->mode = (u16)tmp; retval = spi_setup(spi); if (retval < 0) spi->mode = save; else dev_dbg(&spi->dev, "spi mode %x\n" , tmp); } break ; case SPI_IOC_WR_LSB_FIRST: retval = get_user(tmp, (__u8 __user *)arg); if (retval == 0) { u32 save = spi->mode; if (tmp) spi->mode |= SPI_LSB_FIRST; else spi->mode &= ~SPI_LSB_FIRST; retval = spi_setup(spi); if (retval < 0) spi->mode = save; else dev_dbg(&spi->dev, "%csb first\n" , tmp ? 'l' : 'm' ); } break ; case SPI_IOC_WR_BITS_PER_WORD: retval = get_user(tmp, (__u8 __user *)arg); if (retval == 0) { u8 save = spi->bits_per_word; spi->bits_per_word = tmp; retval = spi_setup(spi); if (retval < 0) spi->bits_per_word = save; else dev_dbg(&spi->dev, "%d bits per word\n" , tmp); } break ; case SPI_IOC_WR_MAX_SPEED_HZ: retval = get_user(tmp, (__u32 __user *)arg); if (retval == 0) { u32 save = spi->max_speed_hz; spi->max_speed_hz = tmp; retval = spi_setup(spi); if (retval >= 0) spidev->speed_hz = tmp; else dev_dbg(&spi->dev, "%d Hz (max)\n" , tmp); spi->max_speed_hz = save; } break ; default : /* segmented and/or full-duplex I/O request */ /* Check message and copy into scratch area */ ioc = spidev_get_ioc_message(cmd, ( struct spi_ioc_transfer __user *)arg, &n_ioc); if (IS_ERR(ioc)) { retval = PTR_ERR(ioc); break ; } if (!ioc) break ; /* n_ioc is also 0 */ /* translate to spi_message, execute */ retval = spidev_message(spidev, ioc, n_ioc); kfree(ioc); break ; } mutex_unlock(&spidev->buf_lock); spi_dev_put(spi); return retval; } |
2、spi平台驱动程序分析:spi_sun6i.c
(1)平台驱动定义
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | static const struct dev_pm_ops sun6i_spi_pm_ops = { .runtime_resume = sun6i_spi_runtime_resume, .runtime_suspend = sun6i_spi_runtime_suspend, }; static struct platform_driver sun6i_spi_driver = { .probe = sun6i_spi_probe, . remove = sun6i_spi_remove, .driver = { .name = "sun6i-spi" , .of_match_table = sun6i_spi_match, .pm = &sun6i_spi_pm_ops, }, } |
(2)sun6i_spi_probe
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 | static int sun6i_spi_probe( struct platform_device *pdev) { struct spi_master *master; struct sun6i_spi *sspi; int ret = 0, irq; master = spi_alloc_master(&pdev->dev, sizeof ( struct sun6i_spi)); if (!master) { dev_err(&pdev->dev, "Unable to allocate SPI Master\n" ); return -ENOMEM; } platform_set_drvdata(pdev, master); sspi = spi_master_get_devdata(master); //获取平台设备的io资源 sspi->base_addr = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(sspi->base_addr)) { ret = PTR_ERR(sspi->base_addr); goto err_free_master; } //获取irq资源 irq = platform_get_irq(pdev, 0); if (irq < 0) { ret = -ENXIO; goto err_free_master; } //申请中断 ret = devm_request_irq(&pdev->dev, irq, sun6i_spi_handler, 0, "sun6i-spi" , sspi); if (ret) { dev_err(&pdev->dev, "Cannot request IRQ\n" ); goto err_free_master; } sspi->master = master; sspi->fifo_depth = (unsigned long )of_device_get_match_data(&pdev->dev); //spi主机的初始化 master->max_speed_hz = 100 * 1000 * 1000; //spi主机最大速率 100Mhz master->min_speed_hz = 3 * 1000; //spi主机最小速率3000hz master->set_cs = sun6i_spi_set_cs; //设置片选引脚信号 master->transfer_one = sun6i_spi_transfer_one; //发送一个spi消息 master->num_chipselect = 4; //有4个片选信号 master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH | SPI_LSB_FIRST; //spi模式 master->bits_per_word_mask = SPI_BPW_MASK(8); //发送一个字8个字节 master->dev.of_node = pdev->dev.of_node; //设备节点 master->auto_runtime_pm = true ; master->max_transfer_size = sun6i_spi_max_transfer_size; //最多发送的字节个数 0xffffff-1 sspi->hclk = devm_clk_get(&pdev->dev, "ahb" ); //获取AHB总线的clk if (IS_ERR(sspi->hclk)) { dev_err(&pdev->dev, "Unable to acquire AHB clock\n" ); ret = PTR_ERR(sspi->hclk); goto err_free_master; } sspi->mclk = devm_clk_get(&pdev->dev, "mod" ); //获取模块clock if (IS_ERR(sspi->mclk)) { dev_err(&pdev->dev, "Unable to acquire module clock\n" ); ret = PTR_ERR(sspi->mclk); goto err_free_master; } init_completion(&sspi->done); //获取复位控制器 sspi->rstc = devm_reset_control_get_exclusive(&pdev->dev, NULL); if (IS_ERR(sspi->rstc)) { dev_err(&pdev->dev, "Couldn't get reset controller\n" ); ret = PTR_ERR(sspi->rstc); goto err_free_master; } /* * This wake-up/shutdown pattern is to be able to have the * device woken up, even if runtime_pm is disabled */ //恢复spi设备 ret = sun6i_spi_runtime_resume(&pdev->dev); if (ret) { dev_err(&pdev->dev, "Couldn't resume the device\n" ); goto err_free_master; } pm_runtime_set_active(&pdev->dev); pm_runtime_enable(&pdev->dev); pm_runtime_idle(&pdev->dev); //注册spi主机 ret = devm_spi_register_master(&pdev->dev, master); if (ret) { dev_err(&pdev->dev, "cannot register SPI master\n" ); goto err_pm_disable; } return 0; err_pm_disable: pm_runtime_disable(&pdev->dev); sun6i_spi_runtime_suspend(&pdev->dev); err_free_master: spi_master_put(master); return ret; } |
- sun6i_spi_remove
1 2 3 4 5 6 7 | static int sun6i_spi_remove( struct platform_device *pdev) { //强制挂机 pm_runtime_force_suspend(&pdev->dev); return 0; } |
- sun6i_spi_match
1 2 3 4 5 | static const struct of_device_id sun6i_spi_match[] = { { .compatible = "allwinner,sun6i-a31-spi" , .data = ( void *)SUN6I_FIFO_DEPTH }, { .compatible = "allwinner,sun8i-h3-spi" , .data = ( void *)SUN8I_FIFO_DEPTH }, {} }; |
(3)spi_device结构体
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 | //spi.h struct spi_device { struct device dev; //spi设备 struct spi_controller *controller; //spi控制器 struct spi_controller *master; /* compatibility layer */ spi的 复制,为了向后兼容 u32 max_speed_hz; u8 chip_select; //Chipselect, distinguishing chips u8 bits_per_word; //定义每个字的bits位数,由spi协议决定 bool rt; //使pump线程实时优先 u32 mode; //spi模式 #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 */ #define SPI_TX_DUAL 0x100 /* transmit with 2 wires */ #define SPI_TX_QUAD 0x200 /* transmit with 4 wires */ #define SPI_RX_DUAL 0x400 /* receive with 2 wires */ #define SPI_RX_QUAD 0x800 /* receive with 4 wires */ #define SPI_CS_WORD 0x1000 /* toggle cs after each word */ #define SPI_TX_OCTAL 0x2000 /* transmit with 8 wires */ #define SPI_RX_OCTAL 0x4000 /* receive with 8 wires */ #define SPI_3WIRE_HIZ 0x8000 /* high impedance turnaround */ int irq; //中断号 void *controller_state; //控制器的实时状态 void *controller_data; char modalias[SPI_NAME_SIZE]; //驱动别名 const char *driver_override; int cs_gpio; /* LEGACY: chip select gpio */ struct gpio_desc *cs_gpiod; /* chip select gpio desc */ uint8_t word_delay_usecs; //每个字数据之间的微秒延时 /* the statistics */ struct spi_statistics statistics; //spi传输的统计信息,包括传输的数据信息 /* * 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 * - chipselect delays * - ... */ }; |
2、spi.c
(1)spi_init
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | //spi.c static int __init spi_init( void ) { int status; buf = kmalloc(SPI_BUFSIZ, GFP_KERNEL); if (!buf) { status = -ENOMEM; goto err0; } status = bus_register(&spi_bus_type); //总线注册,注册后,可以在/sys/bus目录下看到注册的总线 if (status < 0) goto err1; //注册spi主机类 status = class_register(&spi_master_class); // if (status < 0) goto err2; //注册从机类 if (IS_ENABLED(CONFIG_SPI_SLAVE)) { status = class_register(&spi_slave_class); if (status < 0) goto err3; } if (IS_ENABLED(CONFIG_OF_DYNAMIC)) WARN_ON(of_reconfig_notifier_register(&spi_of_notifier)); if (IS_ENABLED(CONFIG_ACPI)) WARN_ON(acpi_reconfig_notifier_register(&spi_acpi_notifier)); return 0; err3: class_unregister(&spi_master_class); err2: bus_unregister(&spi_bus_type); err1: kfree(buf); buf = NULL; err0: return status; } |
(2)spi_bus_type定义:
1 2 3 4 5 6 | struct bus_type spi_bus_type = { .name = "spi" , //总线名 .dev_groups = spi_dev_groups, //设备组 .match = spi_match_device, //spi匹配 .uevent = spi_uevent, }; |
- spi_match_device :spi设备和驱动的匹配方式,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | static int spi_match_device( struct device *dev, struct device_driver *drv) { const struct spi_device *spi = to_spi_device(dev); const struct spi_driver *sdrv = to_spi_driver(drv); //通过spi设备名和驱动名进行匹配 /* Check override first, and if set, only use the named driver */ if (spi->driver_override) return strcmp (spi->driver_override, drv->name) == 0; /* Attempt an OF style match */ if (of_driver_match_device(dev, drv)) //设备树中compatiable属性和驱动名进行匹配 return 1; /* Then try ACPI */ if (acpi_driver_match_device(dev, drv)) //acpi匹配 return 1; if (sdrv->id_table) return !!spi_match_id(sdrv->id_table, spi); //通过驱动别名来进行匹配 return strcmp (spi->modalias, drv->name) == 0; } |
- spi_uevent
1 2 3 4 5 6 7 8 9 10 11 | static int spi_uevent( struct device *dev, struct kobj_uevent_env *env) { const struct spi_device *spi = to_spi_device(dev); int rc; rc = acpi_device_uevent_modalias(dev, env); if (rc != -ENODEV) return rc; return add_uevent_var(env, "MODALIAS=%s%s" , SPI_MODULE_PREFIX, spi->modalias); } |
(3)spi_master_class定义:
1 2 3 4 5 6 | static struct class spi_master_class = { .name = "spi_master" , .owner = THIS_MODULE, .dev_release = spi_controller_release, .dev_groups = spi_master_groups, }; |
(4)spi驱动注册
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | /** * __spi_register_driver - register a SPI driver * @owner: owner module of the driver to register * @sdrv: the driver to register * Context: can sleep * * Return: zero on success, else a negative error code. */ int __spi_register_driver( struct module *owner, struct spi_driver *sdrv) { sdrv->driver.owner = owner; sdrv->driver.bus = &spi_bus_type; sdrv->driver.probe = spi_drv_probe; sdrv->driver. remove = spi_drv_remove; if (sdrv->shutdown) sdrv->driver.shutdown = spi_drv_shutdown; return driver_register(&sdrv->driver); } EXPORT_SYMBOL_GPL(__spi_register_driver); |
- spi_drv_probe
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | static int spi_drv_probe( struct device *dev) { const struct spi_driver *sdrv = to_spi_driver(dev->driver); struct spi_device *spi = to_spi_device(dev); int ret; ret = of_clk_set_defaults(dev->of_node, false ); //获取设备树中的clock属性,并设置clock if (ret) return ret; if (dev->of_node) { spi->irq = of_irq_get(dev->of_node, 0); //获取spi终端 if (spi->irq == -EPROBE_DEFER) return -EPROBE_DEFER; if (spi->irq < 0) spi->irq = 0; } ret = dev_pm_domain_attach(dev, true ); //将spi设备加进PM域,用于节省电源能耗 if (ret) return ret; if (sdrv->probe) { ret = sdrv->probe(spi); if (ret) dev_pm_domain_detach(dev, true ); } return ret; } |
- spi_remove
1 2 3 4 5 6 7 8 9 10 11 | static int spi_drv_remove( struct device *dev) { const struct spi_driver *sdrv = to_spi_driver(dev->driver); int ret = 0; if (sdrv-> remove ) ret = sdrv-> remove (to_spi_device(dev)); dev_pm_domain_detach(dev, true ); //spi驱动移除后,要将spi设备从pm域中去掉 return ret; } |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)