SPI驱动程序设计
一、SPI驱动子系统架构
m25p80.c:
- static int __devinit m25p_probe(struct spi_device *spi)
- {
- struct flash_platform_data *data;
- struct m25p *flash;
- struct flash_info *info;
- unsigned i;
- data = spi->dev.platform_data;
- if (data && data->type) { //配对设备,在m25p_data中找是否有对应的ID
- for (i = 0, info = m25p_data;
- i < ARRAY_SIZE(m25p_data);
- i++, info++) {
- if (strcmp(data->type, info->name) == 0)
- break;
- }
- /* unrecognized chip? */ //芯片没有被认出
- if (i == ARRAY_SIZE(m25p_data)) {
- DEBUG(MTD_DEBUG_LEVEL0, "%s: unrecognized id %s\n",
- dev_name(&spi->dev), data->type);
- info = NULL;
- /* recognized; is that chip really what's there? */ //找到芯片
- } else if (info->jedec_id) {
- struct flash_info *chip = jedec_probe(spi);
- if (!chip || chip != info) {
- dev_warn(&spi->dev, "found %s, expected %s\n",
- chip ? chip->name : "UNKNOWN",
- info->name);
- info = NULL;
- }
- }
- } else
- info = jedec_probe(spi);
- if (!info)
- return -ENODEV;
- flash = kzalloc(sizeof *flash, GFP_KERNEL); //分配空间
- if (!flash)
- return -ENOMEM;
- flash->spi = spi;
- mutex_init(&flash->lock);
- dev_set_drvdata(&spi->dev, flash);
- /*
- * Atmel serial flash tend to power up
- * with the software protection bits set
- */
- if (info->jedec_id >> 16 == 0x1f) {
- write_enable(flash);
- write_sr(flash, 0);
- }
- if (data && data->name)
- flash->mtd.name = data->name;
- else
- flash->mtd.name = dev_name(&spi->dev);
- flash->mtd.type = MTD_NORFLASH; //初始化操作集
- flash->mtd.writesize = 1;
- flash->mtd.flags = MTD_CAP_NORFLASH;
- flash->mtd.size = info->sector_size * info->n_sectors;
- flash->mtd.erase = m25p80_erase;
- flash->mtd.read = m25p80_read;
- flash->mtd.write = m25p80_write;
- /* prefer "small sector" erase if possible */
- if (info->flags & SECT_4K) {
- flash->erase_opcode = OPCODE_BE_4K;
- flash->mtd.erasesize = 4096;
- } else {
- flash->erase_opcode = OPCODE_SE;
- flash->mtd.erasesize = info->sector_size;
- }
- flash->mtd.dev.parent = &spi->dev;
- dev_info(&spi->dev, "%s (%lld Kbytes)\n", info->name,
- (long long)flash->mtd.size >> 10);
- DEBUG(MTD_DEBUG_LEVEL2,
- "mtd .name = %s, .size = 0x%llx (%lldMiB) "
- ".erasesize = 0x%.8x (%uKiB) .numeraseregions = %d\n",
- flash->mtd.name,
- (long long)flash->mtd.size, (long long)(flash->mtd.size >> 20),
- flash->mtd.erasesize, flash->mtd.erasesize / 1024,
- flash->mtd.numeraseregions);
- if (flash->mtd.numeraseregions)
- for (i = 0; i < flash->mtd.numeraseregions; i++)
- DEBUG(MTD_DEBUG_LEVEL2,
- "mtd.eraseregions[%d] = { .offset = 0x%llx, "
- ".erasesize = 0x%.8x (%uKiB), "
- ".numblocks = %d }\n",
- i, (long long)flash->mtd.eraseregions[i].offset,
- flash->mtd.eraseregions[i].erasesize,
- flash->mtd.eraseregions[i].erasesize / 1024,
- flash->mtd.eraseregions[i].numblocks);
- /* partitions should match sector boundaries; and it may be good to
- * use readonly partitions for writeprotected sectors (BP2..BP0).
- */
- if (mtd_has_partitions()) {
- struct mtd_partition *parts = NULL;
- int nr_parts = 0;
- if (mtd_has_cmdlinepart()) {
- static const char *part_probes[]
- = { "cmdlinepart", NULL, };
- nr_parts = parse_mtd_partitions(&flash->mtd,
- part_probes, &parts, 0);
- }
- if (nr_parts <= 0 && data && data->parts) {
- parts = data->parts;
- nr_parts = data->nr_parts;
- }
- if (nr_parts > 0) {
- for (i = 0; i < nr_parts; i++) {
- DEBUG(MTD_DEBUG_LEVEL2, "partitions[%d] = "
- "{.name = %s, .offset = 0x%llx, "
- ".size = 0x%llx (%lldKiB) }\n",
- i, parts[i].name,
- (long long)parts[i].offset,
- (long long)parts[i].size,
- (long long)(parts[i].size >> 10));
- }
- flash->partitioned = 1;
- return add_mtd_partitions(&flash->mtd, parts, nr_parts);
- }
- } else if (data->nr_parts)
- dev_warn(&spi->dev, "ignoring %d default partitions on %s\n",
- data->nr_parts, data->name);
- return add_mtd_device(&flash->mtd) == 1 ? -ENODEV : 0;
- }
m25p_write:
- static int m25p80_write(struct mtd_info *mtd, loff_t to, size_t len,
- size_t *retlen, const u_char *buf)
- {
- struct m25p *flash = mtd_to_m25p(mtd);
- u32 page_offset, page_size;
- struct spi_transfer t[2]; //重要结构,一次发送
- struct spi_message m; //重要结构,消息结构
- DEBUG(MTD_DEBUG_LEVEL2, "%s: %s %s 0x%08x, len %zd\n",
- dev_name(&flash->spi->dev), __func__, "to",
- (u32)to, len);
- if (retlen)
- *retlen = 0;
- /* sanity checks */
- if (!len)
- return(0);
- if (to + len > flash->mtd.size)
- return -EINVAL;
- spi_message_init(&m); //这边进行了初始化
- memset(t, 0, (sizeof t)); //清空
- t[0].tx_buf = flash->command;
- t[0].len = CMD_SIZE;
- spi_message_add_tail(&t[0], &m); //把数据挂汲取
- t[1].tx_buf = buf;
- spi_message_add_tail(&t[1], &m);
- mutex_lock(&flash->lock);
- /* Wait until finished previous write command. */
- if (wait_till_ready(flash)) {
- mutex_unlock(&flash->lock);
- return 1;
- }
- write_enable(flash);
- /* Set up the opcode in the write buffer. */ //数据发送
- flash->command[0] = OPCODE_PP;
- flash->command[1] = to >> 16;
- flash->command[2] = to >> 8;
- flash->command[3] = to;
- /* what page do we start with? */
- page_offset = to % FLASH_PAGESIZE;
- /* do all the bytes fit onto one page? */
- if (page_offset + len <= FLASH_PAGESIZE) {
- t[1].len = len;
- spi_sync(flash->spi, &m); //把message提交给控制器
- *retlen = m.actual_length - CMD_SIZE;
- } else {
- u32 i;
- /* the size of data remaining on the first page */
- page_size = FLASH_PAGESIZE - page_offset;
- t[1].len = page_size;
- spi_sync(flash->spi, &m);
- *retlen = m.actual_length - CMD_SIZE;
- /* write everything in PAGESIZE chunks */
- for (i = page_size; i < len; i += page_size) {
- page_size = len - i;
- if (page_size > FLASH_PAGESIZE)
- page_size = FLASH_PAGESIZE;
- /* write the next page to flash */
- flash->command[1] = (to + i) >> 16;
- flash->command[2] = (to + i) >> 8;
- flash->command[3] = (to + i);
- t[1].tx_buf = buf + i;
- t[1].len = page_size;
- wait_till_ready(flash);
- write_enable(flash);
- spi_sync(flash->spi, &m);
- if (retlen)
- *retlen += m.actual_length - CMD_SIZE;
- }
- }
- mutex_unlock(&flash->lock);
- return 0;
- }

spi_master是SPI控制器,其中的queue把spi_message串成链表。这是一次SPI事务,而一个事务又分成一个一个的读写发送操作(spi_transfer)。

SPI驱动程序设计的更多相关文章
- [国嵌攻略][160][SPI驱动程序设计]
SPI Flash驱动 1.打开/drivers/mtd/devices/m25p80.c驱动文件.找到初始化m25p80_init函数,其中通过spi_register_driver来注册spi设备 ...
- 基于μC/OS—III的CC1120驱动程序设计
基于μC/OS—III的CC1120驱动程序设计 时间:2014-01-21 来源:电子设计工程 作者:张绍游,张贻雄,石江宏 关键字:CC1120 嵌入式操作系统 STM32F103ZE ...
- 基于TQ2440的SPI驱动学习(OLED)
平台简介 开发板:TQ2440 (NandFlash:256M 内存:64M) u-boot版本:u-boot-2015.04 内核版本:Linux-3.14 作者:彭东林 邮箱:pengdongl ...
- SPI 驱动分析
断更博客两个月后我又回来了,眯着躺倒就能睡熟的小眼睛,在这儿敲键盘.这篇文章给你快乐,你有没有爱上我! SPI驱动由三部分组成,分别为drivers.core.device.通过bus总线连接.困了不 ...
- 20145316&20145229实验四:驱动程序设计
20145316&20145229实验四:驱动程序设计 结对伙伴:20145316 许心远 博客链接:http://www.cnblogs.com/xxy745214935/p/6130871 ...
- linux驱动程序设计的硬件基础,王明学learn
linux驱动程序设计的硬件基础(一) 本章讲总结学习linux设备程序设计的硬件基础. 一.处理器 1.1通用处理器 通用处理器(GPP)并不针对特定的应用领域进行体系结构和指令集的优化,它们具有一 ...
- 信息安全系统设计基础实验四:外设驱动程序设计 20135211李行之&20135216刘蔚然
北京电子科技学院(BESTI) 实 验 报 告 封面 课程:信息安全系统设计基础 班级:1352 ...
- 2017-2018-1 20155214&20155216 实验四:外设驱动程序设计
2017-2018-1 20155214&20155216 实验四:外设驱动程序设计 实验四外设驱动程序设计-1 实验要求: 学习资源中全课中的"hqyj.嵌入式Linux应用程序开 ...
- Linux Spi驱动移植小结
2012-01-07 22:21:29 效果图: 理论学习后,主要是linux中spi子系统设备框架的了解后,主控制器与设备分离的思想,那么我要开始动手了. 1, make menuconfig添加 ...
随机推荐
- python学习笔记(四)字符串及字符串操作
字符串 字符串可以存任意类型的字符串,比如名字,一句话等等. 字符串还有很多内置方法,对字符串进行操作,常用的方法如下: name1='hello world' print(name.capitali ...
- spring-boot整合Mybatis多数据源案例
1.运行环境 开发工具:intellij idea JDK版本:1.8 项目管理工具:Maven 4.0.0 2.GITHUB地址 https://github.com/nbfujx/springBo ...
- Git中的分支
具体请参考:https://git-scm.com/book/zh/v1/Git-%E5%88%86%E6%94%AF-%E4%BD%95%E8%B0%93%E5%88%86%E6%94%AF Git ...
- 初识 ❤ TensorFlow |【一见倾心】
说明
- 颁发不受浏览器信任的SSL证书
xshell登录服务器,使用openssl生成RSA密钥及证书 # 生成一个RSA密钥 $ openssl genrsa -des3 -out tfjybj.key 1024 # 生成一个证书请求$ ...
- MySQL查询缓存详解(总结)
MySQL查询缓存详解(总结) 一.总结 一句话总结: mysql查询缓存还是可以用用试一试,但是更推荐分布式,比如redis/memcache之流,将数据库中查询的数据和查询语句以键值对的方式存进分 ...
- feignClient传参(参数为对象类型)的一个坑
客户端 @RequestMapping(value = "/friendCircleComment/comment",method = RequestMethod.POST) R ...
- ActiveReports报表数据源:运行时数据源
ActiveReports 支持在设计时和运行时设置报表数据源,本文将重点介绍如何在运行时为区域报表和RDL/页面报表设置报表数据源. ·第一步: 在工程中创建一个名为 SectionReport_D ...
- Python中多线程的阻塞问题
在使用Queue模块+多线程模拟生产者+消费者问题时,遇到了一个小问题,现在记录下来.供可能会遇到类似问题的初学者们参考. 该问题的完整参考代码如下.主要实现了以下的功能:在一个线程中,开启生产者模式 ...
- LeetCode 实现 Trie (前缀树)
题目链接:https://leetcode-cn.com/problems/implement-trie-prefix-tree/ 题目大意: 略. 分析: 字典树模板. 代码如下: class Tr ...