TXT

SPI Flash

By Dana Ford,2014-07-21 21:48
5 views 0
SPI Flash

读写SPI FLASH--应用程序部分收藏

     驱动部分见前一篇文章:

    http://blog.csdn.net/bripengandre/archive/2009/03/20/4009615.aspx

     应用程序部分很简单:读取新的BIOS文件 ,以及flash中的当前内容,如果两者有差

    异,则将相关扇区先擦掉,然后再将新的BIOS内容写进去.即程序的擦写策略为:只有有差

    异时,才擦写,这能减小程序在擦写时,突然断电带来的坏作用.

     不说什么了,直接看源码.

/****************************RefreshBIOS.h**************************************

    ***/

#ifndef _REFRESH_BIOS_H_

    #define _REFRESH_BIOS_H_

#define SPI_IOCTL_BASE 0xDE

    #define IOCTL_ERASE_CHIP _IOWR(SPI_IOCTL_BASE, 0, ioctl_arg_t)

    #define IOCTL_ERASE_BLOCK _IOWR(SPI_IOCTL_BASE, 1, ioctl_arg_t)

#define SPI_NAME "/dev/spi_name"

    #define MAJOR_ID 252

    #define MINOR_ID 0

    #define CMD_SIZE 250

    #define BUF_SIZE 500

    #define SPI_FLASH_SIZE (2048*1024)

    #define SPI_SECTOR_SIZE 0x1000

typedef struct _erase_block

    {

     int start;

     int end;

    }erase_block_t;

typedef struct _ioctl_arg

    {

     unsigned int type;

     union

     {

     struct

     {

     unsigned int start;

     unsigned int end;

     }erase_range;

     }data;

    }ioctl_arg_t;

#endif /* _REFRESH_BIOS_H_ */

/**************************RefreshBIOS.c****************************************

    */

#include "RefreshBIOS.h"

    #include

    #include

    #include

    #include

    #include

    #include

    #include

    static int write_range(int start, int end); static int check_block(erase_block_t *block_old, erase_block_t *block_new);

    static ssize_t readn(int fd, void *buf, size_t n); static ssize_t writen(int fd, const void *buf, size_t n); static void dbg_print(char *buf_s, char *buf_d, int cnt);

static int fd_s, fd_d;

    static char buf_s[BUF_SIZE], buf_d[BUF_SIZE];

int main(int argc, char *argv[])

    {

     int total, cnt;

     ioctl_arg_t arg;

     erase_block_t block_old, block_new;

     char cmd[CMD_SIZE];

     if(argc != 2)

     {

     fprintf(stderr, "Usage: %s \n", argv[0]);

     return (1);

     }

     snprintf(cmd, sizeof(cmd), "rm -f %s ", SPI_NAME);

     if(system(cmd) < 0)

     {

     fprintf(stderr, "%s error: %s\n", cmd, strerror(errno));

     return (1);

     }

     snprintf(cmd, sizeof(cmd), "mknod %s c %d %d ", SPI_NAME, MAJOR_ID, MINOR_ID);

     if(system(cmd) < 0)

     {

     fprintf(stderr, "%s error: %s\n", cmd, strerror(errno));

     return (1);

     }

     if( (fd_d = open(SPI_NAME, O_RDWR)) < 0)

     {

     fprintf(stderr, "open %s error: %s\n", SPI_NAME, strerror(errno));

     return (1);

     }

     if( (fd_s = open(argv[1], O_RDONLY)) < 0)

     {

     fprintf(stderr, "open %s error: %s\n", argv[0], strerror(errno));

     return (1);

     }

     block_old.start = -1;

     block_old.end = -1;

     total = 0;

     while(total < SPI_FLASH_SIZE)

     {

     cnt = (total + BUF_SIZE > SPI_FLASH_SIZE) ? SPI_FLASH_SIZE-total: BUF_SIZE;

     if((readn(fd_d, buf_d, cnt)) < cnt)

     {

     fprintf(stderr, "%s::readn error: %s\n", argv[0], strerror(errno));

     return (1);

     }

     if((readn(fd_s, buf_s, cnt)) < cnt)

     {

     fprintf(stderr, "%s::readn error: %s\n", argv[0], strerror(errno));

     return (1);

     }

     if(memcmp(buf_s, buf_d, cnt) == 0)

     {

     total += cnt;

     continue;

     }

     //dbg_print(buf_s, buf_d, cnt);

     block_new.start = total;

     block_new.end = total+cnt-1;

     if(check_block(&block_old, &block_new))

     {

     arg.data.erase_range.start = block_new.start;

     arg.data.erase_range.end = block_new.end;

     ioctl(fd_d, IOCTL_ERASE_BLOCK, &arg);

     //lseek(fd_s, block_new.start, SEEK_SET);

     //lseek(fd_d, block_new.start, SEEK_SET);

     if(write_range(block_new.start, block_new.end) < 0)

     {

     fprintf(stderr, "write_range error. \n");

     return (1);

     }

     total = block_new.end+1;

     }

     else

     {

     total += cnt;

     }

     }

     return (0);

}

static int write_range(int start, int end)

    {

     int cnt, total;

     int len = end-start+1;

     lseek(fd_s, start, SEEK_SET);

     lseek(fd_d, start, SEEK_SET);

     total = 0;

     while(total < len)

     {

     cnt = (total + BUF_SIZE > len) ? len-total: BUF_SIZE;

     if((readn(fd_s, buf_s, cnt)) < cnt)

     {

     fprintf(stderr, "write_range::readn error: %s\n", strerror(errno));

     return (-1);

     }

     if((writen(fd_d, buf_s, cnt)) < cnt)

     {

     fprintf(stderr, "write_range::readn error: %s\n", strerror(errno));

     return (-1);

     }

     total += cnt;

     }

     return (total);

    }

static int check_block(erase_block_t *block_old, erase_block_t *block_new)

    {

     int start_old, end_old, start_new, end_new;

     if(block_old->start == -1 && block_old->end == -1) /* at the beginning */

     {

     start_old = -1;

     end_old = -1;

     }

     else

     {

     start_old = block_old->start/SPI_SECTOR_SIZE;

     end_old = block_old->end/SPI_SECTOR_SIZE;

     }

     start_new = block_new->start/SPI_SECTOR_SIZE;

     end_new = block_new->end/SPI_SECTOR_SIZE;

     if(end_new <= end_old)

     {

     return 0;

     }

     if(start_new <= end_old)

     {

     start_new = end_old+1;

     }

     block_new->start = start_new*SPI_SECTOR_SIZE;

     block_new->end = (end_new+1)*SPI_SECTOR_SIZE - 1;

     block_old->start = block_new->start;

     block_old->end = block_new->end;

     return (1);

    }

ssize_t readn(int fd, void *buf, size_t n)

    {

     size_t nleft;

     size_t nread;

     char *ptr;

     ptr = buf;

     nleft = n;

     while(nleft > 0)

     {

     if( (nread = read(fd, ptr, nleft)) < 0)

     {

     if(errno == EINTR)

     {

     nread = 0; /* and call read() again */

     }

     else

     {

     return (-1);

     }

     }

     else if(nread == 0)

     {

     break; /* end of file */

     }

     nleft -= nread;

     ptr += nread;

     }

     return(n - nleft);

    }

ssize_t writen(int fd, const void *buf, size_t n)

    {

     size_t nleft;

     ssize_t nwritten;

     const char *ptr;

     ptr = buf;

     nleft = n;

     while(nleft > 0)

     {

     if( (nwritten = write(fd, ptr, nleft)) < 0)

     {

     if(errno == EINTR)

     {

     nwritten = 0;

     }

     else

     {

     return (-1);

     }

     }

     /* else if(nwritten == 0)

     {

     break;

     } */

     nleft -= nwritten;

     ptr += nwritten;

     }

     return (n);

    }

static void dbg_print(char *buf_s, char *buf_d, int cnt)

    {

     int i;

     if(buf_s == NULL || buf_d == NULL)

     {

     return;

     }

     printf("************buf_s***********\n");

     for(i = 0; i < cnt; i++)

     {

     printf("%c ", buf_s[i]);

     if((i+1) % 16 == 0)

     {

     printf("\n");

     }

     }

     printf("************buf_d***********\n");

     for(i = 0; i < cnt; i++)

     {

     printf("%c ", buf_d[i]);

     if((i+1) % 16 == 0)

     {

     printf("\n");

     }

     }

    }

本文来自CSDN博客,转载请标明出处:

    http://blog.csdn.net/bripengandre/archive/2009/03/22/4013753.aspx

     最近花了一个多星期写了一个SPI驱动。这个驱动是用来读写SPI接口的BIOS Flash的。貌似这个FLASHSPI对端(及主控制端)是与其它设备共同通过一个PCI桥接设备挂在PCI总线上。

     SPI接口四条线,一个时钟CLK,一个片选CE,以及两数据线MOSIMISO(主出从入和主入从出)。由于这里的SPI时序等都已经由SPI控制器做好了,所以我要做的就是读些那些

    控制器上的寄存器来控制读些flash,这个控制器的型号我不知道,是intel,貌似有点类似ICH系列的, flashSST25VF016B的。

1 SPIFlash

     i)往地址寄存器中写入addr

     ii)设置好下一条执行命令,要读的数据个数,是否采用ACSAtomic cycle Sequence读书据时我没用这个,如设置它需设置好pre 命令),这些都可通过控制寄存器设置。

     iii)清调状态寄存器的CDS等标志,然后读取SCIP标志确认设备空闲后,将控制寄存

    器在ii)的基础上置SCG0标志,这样SPI控制器将会启动一个读flash的过程。

     iv)读取状态寄存器,确认读取过程后,从数据寄存器读取出数据。

2SPIFlash。整个流程与1类似,只是在i)中加入往数据寄存器中写入要发的数据,

    iii)中需启动acs使flash写使能。但需要注意的是由于flash的只可10,而不可直0写变成1,在写之前一般要将flash相关区域擦除下,而且flash本身的写保护以及主控端的BIOS写保护都得关掉,具体可参看手册。

3、整个驱动是字符类型的,遵循一般的linux字符驱动编写流程,只是配置信息是从PCI

    桥接设备的寄存器中读出的。

4、说明。写flash要写擦flash,但写时,很难判断当前flash 是否已擦除,所以我把擦

    flash独立到了ioctl操作中,而写flash就只管写了,这也符合功能独立的原则。

5、写驱动,如果对并发同步效率的要求不高的话,基本上就只剩下读写寄存器操作了。读

    写寄存器虽然机械,但一定要小心,因为寄存器之间是有耦合的,在操作某个寄存器时,一

    定也要检查相关的寄存器是否已正确设置,不要想当然。我当初就是忘了自己已经使能了

    BIOS写保护,而导致一个上午没写成功flash

6、驱动是细心活。建议先单元测试,再集成测试。一下子就来集成测试问题可能有一大堆,

    会打击信心的~

最后贴上整个驱动的源码,没什么注释,大家将就下。相应的用户空间程序,见零一篇文章

    《读写SPI FLASH--应用程序部分》

/*****************************spi.h*************************************/

#ifndef _SPI_H_

    #define _SPI_H_

#include

    #include

#define DRIVERNAME "spi_char"

    #define MAJOR_NO 252

    #define MINOR_NO 0

    /* #define SPI_DEBUG */

/* pci configuration */

    #define SPI_VENDOR_ID 0x8086

    #define SPI_DEVICE_ID 0x5031

/* spi space */

    #define CRBA_BAR 0xF0

    #define BIOS_CTRL 0xDC

    #define SPI_BAR_OFFSET 0x3020

    #define SPI_MEM_SIZE 112

    #define SPI_FLASH_SIZE 2048*1024

    #define SPI_FLASH_SECTOR_BIT 12

    #define SPI_FLASH_SECTOR_SIZE 0x1000

/* spi registers */

    #define SPI_STATUS 0x00 #define SPI_CTRL 0x02

    #define SPI_ADDR 0x04

    #define SPI_DATA0 0x08 #define SPI_OPTYPE 0x56 #define SPI_OPMENU 0x58 #define SPI_PREOP 0x54 #define SPI_BBAR 0x50

    #define SPI_PBAR0 0x60

/* spi opcode */

    #define SPI_WR_STATUS 0x01 #define SPI_RD_STATUS 0x05 #define SPI_WR_DATA 0x02 #define SPI_RD_DATA 0x03 #define SPI_WR_ENABLE 0x06 #define SPI_WSR_ENABLE 0x50 #define SPI_4K_ERASE 0x20 #define SPI_WR_DISABLE 0x04 #define SPI_CHIP_ERASE 0x60

/* spi opcode type */

    #define SPI_OPTYPE_WIDTH 0x02 #define SPI_RD_NOADDR 0x00 #define SPI_WR_NOADDR 0x01 #define SPI_RD_ADDR 0x02 #define SPI_WR_ADDR 0x03

/* bit definition */

    #define SPI_STATUS_SCIP 0x01 #define SPI_STATUS_CDS 0x04 #define SPI_STATUS_BAS 0x08 #define SPI_STATUS_CFG_LOCK 0x8000

    #define SPI_CTRL_SCGO 0x02 #define SPI_CTRL_ACS 0x04 #define SPI_CTRL_SPOP 0x08 #define SPI_CTRL_COP 0x70 #define SPI_CTRL_COP_SHIFT 0x4 #define SPI_CTRL_DC 0x4000

Report this document

For any questions or suggestions please email
cust-service@docsford.com