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添加 ...
随机推荐
- 各大漏洞平台及SRC的区别和如何批量刷漏洞
批量刷漏洞: 01刷指纹->02刷原始漏洞->03刷CMS->04刷指定政府.教育->05刷众测平台->06刷SRC->07刷国内外.活动 搜索引擎: 百度.goo ...
- 使用DMA方式发送串口数据
一.初始化部分代码 //串口接收DMA缓存 uint8_t Uart_Rx[UART_RX_LEN] = {}; uint32_t Uart_Send_Buffer[] = {}; void USAR ...
- ADSL(Asymmetric Digital Subscriber Loop)技术
上行带宽,下行带宽 宽带上行下行是指一般ADSL上网方式上行与下行速率,上行就是从电脑上传的速度,下行就是从网络上的主机下载速度,一般下行速率比较高! ADSL(Asymmetric Digital ...
- java高并发解决思路
一个小型的网站,比如个人网站,可以使用最简单的html静态页面就实现了,配合一些图片达到美化效果,所有的页面均存放在一个目录下,这样的网站对系统架构.性能的要求都很简单,随着互联网业务的不断丰富,网站 ...
- 查看SQL Server被锁的表以及如何解锁【转】
锁定数据库的一个表的区别 SELECT * FROM table WITH (HOLDLOCK) 其他事务可以读取表,但不能更新删除 SELECT * FROM table WITH (TABLO ...
- docker 部署netcore 的关键语句
网站容器:docker run -it --name myTestWeb -p 8080:80 -v /mnt/hgfs/my_share/core/website/:/website microso ...
- leetcode-解题记录 206. 反转链表
题目 反转一个单链表. 示例: 输入: 1->2->3->4->5->NULL 输出: 5->4->3->2->1->NULL 进阶: 你可 ...
- 全面了解python中的类,对象,方法,属性
全面了解python中的类,对象,方法,属性 python中一切皆为对象,所谓对象:我自己就是一个对象,我玩的电脑就是对象,坐着的椅子就是对象,家里养的小狗也是一个对象...... 我们通过描述属性( ...
- .net 运行原理
刚学习那会,感觉.net运行原理是很复杂的,也去了解过相关的东西,但是很晦涩,难于理解.感觉有些难了,也就放弃了解了.今天回头想想,也是当时有些毛躁了,不管怎么说,时至今日是有些明白运行原理. 从头开 ...
- row_number() over partition by 分组聚合
分组聚合,就是先分组再排序,可以的话顺手标个排名:如果不想分组也可以排名:如果不想分组同时再去重排名也可以 ROW_NUMBER() OVER( [PARTITION BY column_1, col ...