前言
前面我们对SPI控制器驱动进行了分析,接下来来分析SPI设备驱动。我们以DS1302驱动作为分析对象。DS1302是一款RTC芯片,估计很多人在学单片机时用到过。RTC芯片算是比较简单的,也方便分析理解。
SPI设备驱动分析
内核:4.20
芯片:DS1302 RTC
下面的代码分析主要都在注释中,会按照驱动中函数的执行顺序分析。我们不需要去关心RTC的具体内容,因为它主要是一些读写寄存器的过程。应主要关注SPI的通信。
(1) 装载和卸载函数
//dts匹配表staticconststructof_device_idds1302_dt_ids[] = { { .compatible="maxim,ds1302", }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, ds1302_dt_ids); staticstructspi_driverds1302_driver= { .driver.name="rtc-ds1302", .driver.of_match_table=of_match_ptr(ds1302_dt_ids), .probe=ds1302_probe, .remove=ds1302_remove, }; //封装了spi_register_driver和spi_unregister_drivermodule_spi_driver(ds1302_driver);
module_spi_driver宏定义在 include/linux/spi/spi.h, 具体看一下源码
所以只是对 spi_register_driver 和 spi_unregister_driver 做了封装。
(2) probe()函数
staticintds1302_probe(structspi_device*spi) { structrtc_device*rtc; u8addr; u8buf[4]; u8*bp; intstatus; //检查是不是8bit传输if (spi->bits_per_word&& (spi->bits_per_word!=8)) { dev_err(&spi->dev, "bad word length\n"); return-EINVAL; } elseif (spi->max_speed_hz>2000000) {//检查最大速率dev_err(&spi->dev, "speed is too high\n"); return-EINVAL; } elseif (spi->mode&SPI_CPHA) { dev_err(&spi->dev, "bad mode\n"); return-EINVAL; } //使用spi读写一下寄存器,检查是否可以写(DS1302有个寄存器是设置写保护的)addr=RTC_ADDR_CTRL<<1|RTC_CMD_READ; status=spi_write_then_read(spi, &addr, sizeof(addr), buf, 1); ...... spi_set_drvdata(spi, spi); //注册rtcrtc=devm_rtc_device_register(&spi->dev, "ds1302", &ds1302_rtc_ops, THIS_MODULE); return0; }
(3) RTC设置和读取函数
//读取时间staticintds1302_rtc_get_time(structdevice*dev, structrtc_time*time) { structspi_device*spi=dev_get_drvdata(dev); u8addr=RTC_CLCK_BURST<<1|RTC_CMD_READ; u8buf[RTC_CLCK_LEN-1]; intstatus; //spi读取时间status=spi_write_then_read(spi, &addr, sizeof(addr), buf, sizeof(buf)); if (status<0) returnstatus; /* Decode the registers */time->tm_sec=bcd2bin(buf[RTC_ADDR_SEC]); time->tm_min=bcd2bin(buf[RTC_ADDR_MIN]); time->tm_hour=bcd2bin(buf[RTC_ADDR_HOUR]); time->tm_wday=buf[RTC_ADDR_DAY] -1; time->tm_mday=bcd2bin(buf[RTC_ADDR_DATE]); time->tm_mon=bcd2bin(buf[RTC_ADDR_MON]) -1; time->tm_year=bcd2bin(buf[RTC_ADDR_YEAR]) +100; return0; } //设置时间staticintds1302_rtc_set_time(structdevice*dev, structrtc_time*time) { structspi_device*spi=dev_get_drvdata(dev); u8buf[1+RTC_CLCK_LEN]; u8*bp; intstatus; /* Enable writing */bp=buf; *bp++=RTC_ADDR_CTRL<<1|RTC_CMD_WRITE; *bp++=RTC_CMD_WRITE_ENABLE; //关闭写保护status=spi_write_then_read(spi, buf, 2, NULL, 0); if (status) returnstatus; /* Write registers starting at the first time/date address. */bp=buf; *bp++=RTC_CLCK_BURST<<1|RTC_CMD_WRITE; *bp++=bin2bcd(time->tm_sec); *bp++=bin2bcd(time->tm_min); *bp++=bin2bcd(time->tm_hour); *bp++=bin2bcd(time->tm_mday); *bp++=bin2bcd(time->tm_mon+1); *bp++=time->tm_wday+1; *bp++=bin2bcd(time->tm_year%100); *bp++=RTC_CMD_WRITE_DISABLE; //只有写,没有读returnspi_write_then_read(spi, buf, sizeof(buf), NULL, 0); } staticconststructrtc_class_opsds1302_rtc_ops= { .read_time=ds1302_rtc_get_time, .set_time=ds1302_rtc_set_time, };
上面读取和设置都是调用spi_write_then_read来进行Spi通信,这个是Linux帮我们封装好的接口函数。看一下具体实现:
intspi_write_then_read(structspi_device*spi, constvoid*txbuf, unsignedn_tx, void*rxbuf, unsignedn_rx) { staticDEFINE_MUTEX(lock); intstatus; structspi_messagemessage; structspi_transferx[2]; u8*local_buf; if ((n_tx+n_rx) >SPI_BUFSIZ||!mutex_trylock(&lock)) { local_buf=kmalloc(max((unsigned)SPI_BUFSIZ, n_tx+n_rx), GFP_KERNEL|GFP_DMA); if (!local_buf) return-ENOMEM; } else { local_buf=buf; } //初始化spi_messagespi_message_init(&message); //将要传的数据放到spi_transfer,然后追加到spi_messagememset(x, 0, sizeof(x)); if (n_tx) { x[0].len=n_tx; spi_message_add_tail(&x[0], &message); } if (n_rx) { x[1].len=n_rx; spi_message_add_tail(&x[1], &message); } memcpy(local_buf, txbuf, n_tx); x[0].tx_buf=local_buf; x[1].rx_buf=local_buf+n_tx; //进行SPI发送status=spi_sync(spi, &message); if (status==0) memcpy(rxbuf, x[1].rx_buf, n_rx); if (x[0].tx_buf==buf) mutex_unlock(&lock); elsekfree(local_buf); returnstatus; }
spi_sync最终会调用spi_master->transfer();传递给spi_sync函数的参数中有spi_device, 而spi_device中又包含spi_master的指针。所以就能找到了对应的spi控制器进行数据发送。
总结
大部分的SPI设备驱动框架都差不多,大家可以配合下面两篇文章一起看。这样更能理解。我们会发现,SPI设备驱动内容其实就是使用SPI控制器(spi_master)去对具体芯片设备进行读写。