读写SPI FLASH--驱动部分

本文介绍了一个用于读写SPI接口BIOS Flash的驱动程序的实现,详细阐述了读写流程,包括SPI控制器的操作、读写Flash的步骤以及SPI寄存器的管理。此外,还提到了驱动程序的字符类型、PCI桥接设备的使用以及并发同步效率不高的情况下,主要关注读写寄存器操作。最后,提到了驱动程序的单元测试和集成测试的重要性。
摘要由CSDN通过智能技术生成

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

    SPI接口四条线,一个时钟CLK,一个片选CE,以及两数据线MOSI和MISO(主出从入和主入从出)。由于这里的SPI时序等都已经由SPI控制器做好了,所以我要做的就是读些那些控制器上的寄存器来控制读些flash,这个控制器的型号我不知道,是intel,貌似有点类似ICH系列的, flash是SST25VF016B的。

1、 SPI读Flash。

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

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

    iii)清调状态寄存器的CDS等标志,然后读取SCIP标志确认设备空闲后,将控制寄存器在ii)的基础上置SCG0标志,这样SPI控制器将会启动一个读flash的过程。

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

2、SPI写Flash。整个流程与1类似,只是在i)中加入往数据寄存器中写入要发的数据,在iii)中需启动acs使flash写使能。但需要注意的是由于flash的只可1变0,而不可直接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 <linux/cdev.h>
#include <linux/types.h>

#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
#define SPI_CTRL_DBC 0x3f00
#define SPI_CTRL_DBC_SHIFT 0x8

#define SPI_CTRL_OPMENU_RD 0x00
#define SPI_CTRL_OPMENU_RDSR 0x01
#define SPI_CTRL_OPMENU_WR 0x02
#define SPI_CTRL_OPMENU_WRSR 0x03
#define SPI_CTRL_OPMENU_4KERASE 0x04
#define SPI_CTRL_OPMENU_CHIPERASE 0x05

#define SPI_FLASH_PROTECTION 0x3C
#define BIOS_NOPROTECTION 0x01


#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 BUF_SIZE 512

/******************************************************************************
 
  Description:
     This structure contains data that is used for most driver operations.
 
 *****************************************************************************/
typedef struct _spi_char
{
    dev_t dev;
    struct cdev cdev;
    uint32_t bios_ctrl_addr;
    void *mem_virt; /* base virtual address  */
    uint32_t mem_base;
    uint32_t mem_size;
    uint32_t mem_ready; /* ioremap successfully? */
    uint8_t iomem_ready;
    uint32_t fp; /* file pointer */
} spi_char_t;

typedef struct _ioctl_arg
{
    uint32_t type;
    union
    {
        struct
        {
            uint32_t start;
            uint32_t end;
        }erase_range;
    }data;  
}ioctl_arg_t;


#ifdef SPI_DEBUG
#define SPI_DBG(args...) printk(KERN_DEBUG "SPI: " args)
#else
#define SPI_DBG(args...)
#endif

#define SPI_ERR(args...) printk(KERN_ERR "SPI: " args)

#endif /* _SPI_H_ */

 

 

 

/*****************************spi.c*************************************/

#include <linux/types.h>
#include <linux/fs.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/pci.h>
#include <asm/uaccess.h>
#include <linux/ioport.h>
#include <linux/delay.h>
#include <linux/ioctl.h>
#include <linux/cdev.h>
#include "spi.h"

static spi_char_t spi_char;
MODULE_LICENSE("Dual BSD/GPL");

static uint8_t get_flash_status(void);
static uint8_t set_flash_status(uint8_t value);

static int  is_flash_protection(void);
static int disable_flash_protection(void);
static int is_bios_protection(void);
static int disable_bios_protection(void);

static int erase_4K_sector(unsigned int idx);
static int erase_chip(void);
static uint8_t read_byte(uint32_t addr);
static int write_byte(uint32_t addr, uint8_t byte);


/*****************************************************************************
  Description:
    This function is called when the driver is unloaded from memory.   

  Parameters:
    none

  Returns:
      none
******************************************************************************/
void spi_cleanup(void)
{
    //remove cdev struct from system
    cdev_del(&spi_char.cdev);

    //unregister driver module
    unregister_chrdev_region(spi_char.dev, 1);

    //release the reserved IO memory space
    if ( spi_char.mem_ready )
    {
          iounmap(spi_char.mem_virt);
    }
    if(spi_char.iomem_ready)
    {
         release_mem_region(spi_char.mem_base, SPI_MEM_SIZE);
    }

    SPI_DBG("%s:spi_cleanup-Driver unload complete/n", DRIVERNAME);
}


/*****************************************************************************
  Description:
    This function is called when the driver interface is opened    

  Parameters:
    none

  Returns:
      0 => success
    < 0 => error
******************************************************************************/

  • 0
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值