您的位置:首页 > 其它

读写SPI FLASH--驱动部分

2009-03-20 23:06 701 查看
    最近花了一个多星期写了一个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
******************************************************************************/
int spi_open(struct inode *inode, struct file *filp)
{
    SPI_DBG("%s:spi_open-module opened/n", DRIVERNAME);
    spi_char.fp = 0;
    SPI_DBG("spi_char.fp = %08x/n", spi_char.fp);
    return 0;
}

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

  Parameters:
    none

  Returns:
      0 => success
    < 0 => error

******************************************************************************/
int spi_release(struct inode *inode, struct file *filp)
{
    spi_char.fp = 0;
    SPI_DBG("spi_char.fp = %08x/n", spi_char.fp);
    SPI_DBG("%s:spi_release-module released/n", DRIVERNAME);
    return 0;
}

int spi_read (struct file *filp, char __user *buf, size_t count, loff_t *f_pos)
{
    int i;
    uint32_t addr, val;
    uint16_t word;
    unsigned char data[BUF_SIZE];
    ssize_t retval = 0;
   
    /* print some information for debug */
    SPI_DBG("start spi_read/n");
    val = readw((void *)(spi_char.mem_virt+SPI_STATUS));
    SPI_DBG("status=%08x/n", val);
    val = readw((void *)(spi_char.mem_virt+SPI_CTRL));
    SPI_DBG("ctrl=%08x/n", val);
    val = readw((void *)(spi_char.mem_virt+SPI_ADDR));
    SPI_DBG("addr=%08x/n", val);
    val = readl((void *)(spi_char.mem_virt+SPI_DATA0));
    SPI_DBG("data0.0=%08x/n", val);
    val = readl((void *)(spi_char.mem_virt+SPI_DATA0+4));
    SPI_DBG("data0.1=%08x/n", val);
   
    /* check count */
    if(count > BUF_SIZE)
    {
        count = BUF_SIZE;
    }
    if(spi_char.fp+count > SPI_FLASH_SIZE)
    {
        count = SPI_FLASH_SIZE-spi_char.fp;
    }
   
   
    SPI_DBG("spi_char.fp = %08x/n", spi_char.fp);
    addr = spi_char.fp;
    SPI_DBG("spi_read: addr = %08x/n", addr);
    for(i = 0; i < count; i++)
    {
        data[i] = read_byte(addr+i);
    }
   
    if(copy_to_user(buf, data, count))
    {
        SPI_ERR("spi_read::copty_to_user eror/n");
        return (-EFAULT);
    }
    spi_char.fp += count;
    SPI_DBG("leave spi_read, count=%d/n", count);
    
    return (count);
}
  

int spi_write (struct file *filp, const char __user *buf, size_t count, loff_t *f_pos)
{
    int i;
    uint32_t addr, val;
    uint16_t word;
    unsigned char data[BUF_SIZE];
    ssize_t retval = 0;
   
    /* print some information for debug */
    SPI_DBG("start spi_write/n");
    val = readw((void *)(spi_char.mem_virt+SPI_STATUS));
    SPI_DBG("status=%08x/n", val);
    val = readw((void *)(spi_char.mem_virt+SPI_CTRL));
    SPI_DBG("ctrl=%08x/n", val);
    val = readw((void *)(spi_char.mem_virt+SPI_ADDR));
    SPI_DBG("addr=%08x/n", val);
    val = readl((void *)(spi_char.mem_virt+SPI_DATA0));
    SPI_DBG("data0.0=%08x/n", val);
    val = readl((void *)(spi_char.mem_virt+SPI_DATA0+4));
    SPI_DBG("data0.1=%08x/n", val);
    val = readl((void *)(spi_char.mem_virt+SPI_BBAR));
    SPI_DBG("BBAR=%08x/n", val);
    val = readl((void *)(spi_char.mem_virt+SPI_PBAR0));
    SPI_DBG("PBAR0=%08x/n", val);
  
    /* check count */
    if(count > BUF_SIZE)
    {
        count = BUF_SIZE;
    }
    if(spi_char.fp+count > SPI_FLASH_SIZE)
    {
        count = SPI_FLASH_SIZE-spi_char.fp;
    }
   
    if(copy_from_user(data, buf, count))
    {
         SPI_ERR("spi_read::copy_from_user error/n");
         return(-EFAULT);
    }
   
    /* disable flash protection */
    if(is_flash_protection())
    {
        if(!disable_flash_protection())
        {
            SPI_ERR("can't disable flash protection/n");
            return(-EFAULT);
        }
    }
   
    addr = spi_char.fp;
    for(i = 0; i < count; i++)
    {
        if(!write_byte(addr+i, data[i]))
        {
            SPI_ERR("spi_write error/n");
        }
        
         /* print some information for debug */
         val = readl((void *)(spi_char.mem_virt+SPI_DATA0));
         SPI_DBG("after write, data=%08x/n", val);
         val = readw((void *)(spi_char.mem_virt+SPI_CTRL));
         SPI_DBG("after write, ctrl=%08x/n", val);
         val = readw((void *)(spi_char.mem_virt+SPI_STATUS));
         SPI_DBG("after write, status=%08x/n", val);
         word = readw((void *)(spi_char.mem_virt+SPI_STATUS));
         word |= SPI_STATUS_CDS;
         word |= SPI_STATUS_BAS;
         writew(word, (void *)(spi_char.mem_virt+SPI_STATUS));
         val = readw((void *)(spi_char.mem_virt+SPI_STATUS));
         SPI_DBG("after write1, status=%08x/n", val);
    }
   
    spi_char.fp += count;
    SPI_DBG("leave spi_write, count=%d/n", count);
    return (count);
}

/*****************************************************************************
  Description:
    This function is the ioctl interface to the driver.   

  Parameters:
    inode  ptr to inode
    filp   ptr to file struct
    cmd    ioctl command
    arg    passed in argument

  Returns:
      0 => success
    < 0 => error
******************************************************************************/
int spi_ioctl(struct inode *inode, struct file *filp, unsigned int cmd,
                unsigned long arg)
{
    ioctl_arg_t info;
    unsigned int start, end;
    int i;
   
    switch(cmd)
    {
        case IOCTL_ERASE_CHIP:
            if( !erase_chip())
            {
                return (-EFAULT);
            }
            break;
        
        case IOCTL_ERASE_BLOCK:
            if((void *)arg == NULL)
            {
                return(-EINVAL);
            }
            //get the ioctl struct passed in by user
       if ( copy_from_user(&info, (ioctl_arg_t*)arg, sizeof(ioctl_arg_t)) )
       {
           SPI_ERR("%s:spi_ioctl::copy_from_user error./n", DRIVERNAME);
           return -EFAULT;
           }
           
             /* sector & address index start from 0 */
            start = (info.data.erase_range.start) >> SPI_FLASH_SECTOR_BIT;
            end = (info.data.erase_range.end) >> SPI_FLASH_SECTOR_BIT;
            /* start = (info.data.erase_range.start + SPI_FLASH_SECTOR_SIZE -1) >> SPI_FLASH_SECTOR_BIT;
            end = (info.data.erase_range.end + SPI_FLASH_SECTOR_SIZE -1) >> SPI_FLASH_SECTOR_BIT;
            start--;
            end--; */
            if(start > end || start < 0 || end >= SPI_FLASH_SIZE/SPI_FLASH_SECTOR_SIZE)
            {
                return (-EINVAL);
            }
          
                SPI_DBG("start=%d,end=%d/n", start, end);               
 
            for(i = start; i <= end; i++)
            {
                if(!erase_4K_sector(i))
                {
                    return (-EFAULT);
                }
            }
            break;
           
            default:
                return (-EINVAL);
                    break;
    }
           
  
    return 0;
}

void EP80579_spi_init(uint32_t base)
{
    uint8_t byte;
    uint16_t word;
    uint32_t dword,val;
    uint64_t qword;
    uint32_t addr;
       
    /* print some information for debug */
    val = readw((void *)(spi_char.mem_virt+SPI_STATUS));
    SPI_DBG("status=%08x/n", val);
    val = readw((void *)(spi_char.mem_virt+SPI_CTRL));
    SPI_DBG("ctrl=%08x/n", val);
    val = readl((void *)(spi_char.mem_virt+SPI_ADDR));
    SPI_DBG("addr=%08x/n", val);
    val = readl((void *)(spi_char.mem_virt+SPI_DATA0));
    SPI_DBG("data0=%08x/n", val);
    val = readl((void *)(spi_char.mem_virt+SPI_BBAR));
    SPI_DBG("BBAR=%08x/n", val);
    val = readl((void *)(spi_char.mem_virt+SPI_PBAR0));
    SPI_DBG("PBAR0=%08x/n", val);

    /* enable configuration */
    addr = base + SPI_STATUS;
    word = readw((void *)addr);
    word &= ~SPI_STATUS_CFG_LOCK;
    writew(word, (void *)addr);
   
    /* fill opcode */
    addr = base + SPI_OPMENU;
    val = readl((void *)(spi_char.mem_virt+SPI_OPMENU));
    SPI_DBG("before init, SPI_OPMENU.0=%08x/n", val);
    val = readl((void *)(spi_char.mem_virt+SPI_OPMENU+4));
    SPI_DBG("before init, SPI_OPMENU.1=%08x/n", val);
    dword = SPI_RD_DATA + (SPI_RD_STATUS<<8) + (SPI_WR_DATA<<16) + (SPI_WR_STATUS<<24);
    writel(dword, (void *)addr);
    dword = (SPI_4K_ERASE)+(SPI_CHIP_ERASE<<8);
    writel(dword, (void *)(addr+4));
    val = readl((void *)(spi_char.mem_virt+SPI_OPMENU));
    SPI_DBG("after init, SPI_OPMENU.0=%08x/n", val);
    val = readl((void *)(spi_char.mem_virt+SPI_OPMENU+4));
    SPI_DBG("after init, SPI_OPMENU.1=%08x/n", val);
   
    /* fill opcode type */
    val = readw((void *)(spi_char.mem_virt+SPI_OPTYPE));
    SPI_DBG("before init, SPI_OPTTYPE=%08x/n", val);
    val &= 0x0000;
    val = val |SPI_RD_ADDR|(SPI_RD_NOADDR<<SPI_OPTYPE_WIDTH)|(SPI_WR_ADDR<<SPI_OPTYPE_WIDTH*2)
              |(SPI_WR_NOADDR<<SPI_OPTYPE_WIDTH*3)|(SPI_WR_ADDR<<SPI_OPTYPE_WIDTH*4)|(SPI_WR_NOADDR<<SPI_OPTYPE_WIDTH*5);
    writew(val, (void *)(spi_char.mem_virt+SPI_OPTYPE));
    val = readw((void *)(spi_char.mem_virt+SPI_OPTYPE));/* check the status */
    SPI_DBG("after init, SPI_OPTTYPE=%08x/n", val);

   /* fill pre opcode */
    addr = base + SPI_PREOP;
    val = readw((void *)(spi_char.mem_virt+SPI_PREOP));
    SPI_DBG("before init, SPI_PREOOP=%08x/n", val);
    val = SPI_WR_ENABLE | (SPI_WSR_ENABLE << 8);
    writew(val, (void *)addr);
    val = readw((void *)(spi_char.mem_virt+SPI_PREOP));
    SPI_DBG("after init, SPI_PREOP=%08x/n", val);
  
   
    /* disable Atomic Cycle Sequence */
    val = readw((void *)(spi_char.mem_virt+SPI_CTRL));
    val &= ~SPI_CTRL_ACS;
    val &= ~SPI_CTRL_SPOP;
    writew(val, (void*)(spi_char.mem_virt+SPI_CTRL));
}

loff_t spi_lseek(struct file *filp, loff_t off, int whence)
{
    loff_t newpos;
   
    switch (whence)
    {
        case 0: /* SEEK_SET */
            newpos = off;
            break;
        
        case 1: /* SEEK_CUR */
            newpos = spi_char.fp + off;
            break;
           
        case 2: /* SEEK_END */
            newpos = spi_char.fp + off;
            break;
        
         default: /* cant't happen */
            return (-EINVAL);
            break;
     }
    
     if(newpos < 0 || newpos >= SPI_FLASH_SIZE)
     {
        return (-EINVAL);
     }
    
     spi_char.fp = newpos;
     return (newpos);
}

// Linux file operations
struct file_operations file_ops =
{
    .owner =     THIS_MODULE,
    .read =      spi_read,
    .write =     spi_write,
    .llseek = spi_lseek,
     .open =        spi_open,
     .release =   spi_release,
     .ioctl    = spi_ioctl,
};

/*****************************************************************************
  Description:
    Entry point for the driver.   

  Parameters:
    none

  Returns:
      0 => success
    < 0 => error
******************************************************************************/
int spi_init(void)
{
    int ret;
    dev_t devno;
    struct pci_dev *pdev = NULL;
   
    spi_char.mem_base = 0;
    spi_char.mem_ready = 0;

    //request and reserve a device number
   /* ret = alloc_chrdev_region(&spi_char.dev, 0, 1, DRIVERNAME);

    if ( ret < 0)
    {
        SPI_ERR("%s:spi_init-Could not register module/n", DRIVERNAME);
        return ret;
    } */

    //init cdev struct for adding device to kernel
    cdev_init(&spi_char.cdev, &file_ops);
    spi_char.cdev.owner = THIS_MODULE;
    spi_char.cdev.ops = &file_ops;
    spi_char.mem_size = SPI_FLASH_SIZE;
     
    //devno = MKDEV(MAJOR(spi_char.dev), 0);
    devno = MKDEV(MAJOR_NO, MINOR_NO);
    SPI_DBG("major id:%d, minor id:%d/n", MAJOR(devno), MINOR(devno));
    if(cdev_add(&spi_char.cdev, devno, 1))
    {
        SPI_ERR("%s:spi_init-cdev_add failed/n", DRIVERNAME);
        goto Exit_Error;
    }
   

    //Get dev struct for the LPC device. The GPIO BAR is located in the
    //LPC device config space
    pdev = pci_get_device(SPI_VENDOR_ID, SPI_DEVICE_ID, NULL);
    if ( !pdev )
    {
        SPI_ERR("%s:spi_char_init-Could not find pci device/n", DRIVERNAME);
        goto Exit_Error;
    }
       
    //Get base address from the LPC configuration space.
    pci_read_config_dword(pdev, CRBA_BAR, &(spi_char.mem_base));
    SPI_DBG("base addr1 %08x/n", spi_char.mem_base);
    //Get BIOS Contrl address
    pci_read_config_byte(pdev, BIOS_CTRL, &(spi_char.bios_ctrl_addr));
    SPI_DBG("bios ctrl addr %08x/n", spi_char.bios_ctrl_addr);

    /* Get own base address */
    spi_char.mem_base &= 0xFFFFC000;
    SPI_DBG("base addr11 %08x/n", spi_char.mem_base);
    spi_char.mem_base += SPI_BAR_OFFSET;
    SPI_DBG("base addr21 %08x/n", spi_char.mem_base);

    //release reference to device
    pci_dev_put(pdev);

    //obtain memory space
    if ( !request_mem_region(spi_char.mem_base, SPI_MEM_SIZE, DRIVERNAME) )
    {
        spi_char.iomem_ready = 0;
        SPI_ERR("%s:spi_init-IO memory region has been reserved?/n", DRIVERNAME);
    }
    else
    {
        spi_char.iomem_ready = 1;
    }
    spi_char.mem_virt = ioremap(spi_char.mem_base, SPI_MEM_SIZE);
    SPI_DBG("virtual addr %08x/n", spi_char.mem_virt);
    //indicate memory space reserved
    spi_char.mem_ready = 1;
   
   
    //do some special initiation
    EP80579_spi_init(spi_char.mem_virt);
   

    goto Exit;

Exit_Error:
    SPI_ERR("%s:spi_init-Initialization failed/n", DRIVERNAME);
    spi_cleanup();
    return -ENODEV;
   
Exit:
    SPI_DBG("%s:spi_init-Initialization complete/n", DRIVERNAME);
    return 0;
}

module_init(spi_init);
module_exit(spi_cleanup);

static uint8_t get_flash_status(void)
{
    uint32_t word, val;
    uint8_t ret;
   
     word = readw((void *)(spi_char.mem_virt+SPI_CTRL));
     word |= SPI_CTRL_SCGO; /* set cycle */
     word |= SPI_CTRL_ACS; /* Enable Atomic Cycle Sequence */
     word |= SPI_CTRL_SPOP;
     word &= ~SPI_CTRL_COP;    
     word = word | SPI_CTRL_OPMENU_RDSR<<SPI_CTRL_COP_SHIFT; /* read */
     word &= ~SPI_CTRL_DBC;    
     word = word | 0x00<<SPI_CTRL_DBC_SHIFT; /* read count=1 */
     word |= SPI_CTRL_DC;
    
      /* check the status */
     val = readw((void *)(spi_char.mem_virt+SPI_STATUS));
     while(val & SPI_STATUS_SCIP)
     {
            val = readw((void *)(spi_char.mem_virt+SPI_STATUS));/* check the status */
     }
        
     writew(word, (void *)(spi_char.mem_virt+SPI_CTRL));
        
     /* check the status */
     val = readw((void *)(spi_char.mem_virt+SPI_STATUS));
     while(val & SPI_STATUS_SCIP)
     {
        val = readw((void *)(spi_char.mem_virt+SPI_STATUS));/* check the status */
     }
       
     val = readl((void *)(spi_char.mem_virt+SPI_DATA0));
     SPI_DBG("get_flash_status:%08x/n", val);    
     ret = (val&0xFF);
    
     word = readw((void *)(spi_char.mem_virt+SPI_STATUS));
     word |= SPI_STATUS_CDS; /* clear Cycle Done Status flag */
     word |= SPI_STATUS_BAS;  /* clear blocked flag */
     writew(word, (void *)(spi_char.mem_virt+SPI_STATUS));
        
     return (ret);
}

static uint8_t set_flash_status(uint8_t value)
{
     uint32_t word, var, val;
   
     var = value;
     writel(var, (void *)(spi_char.mem_virt+SPI_DATA0));
     word = readw((void *)(spi_char.mem_virt+SPI_CTRL));
     word |= SPI_CTRL_SCGO; /* set cycle */
     word |= SPI_CTRL_ACS; /* Enable Atomic Cycle Sequence */
     word |= SPI_CTRL_SPOP;
     word &= ~SPI_CTRL_COP;    
     word = word | SPI_CTRL_OPMENU_WRSR<<SPI_CTRL_COP_SHIFT; /* read */
     word &= ~SPI_CTRL_DBC;    
     word = word | 0x00<<SPI_CTRL_DBC_SHIFT; /* write count=1 */
     word |= SPI_CTRL_DC;
    
      /* check the status */
     val = readw((void *)(spi_char.mem_virt+SPI_STATUS));
     while(val & SPI_STATUS_SCIP)
     {
            val = readw((void *)(spi_char.mem_virt+SPI_STATUS));/* check the status */
     }
        
     writew(word, (void *)(spi_char.mem_virt+SPI_CTRL));
        
     /* check the status */
     val = readw((void *)(spi_char.mem_virt+SPI_STATUS));
     while(val & SPI_STATUS_SCIP)
     {
        val = readw((void *)(spi_char.mem_virt+SPI_STATUS));/* check the status */
     }
     word = readw((void *)(spi_char.mem_virt+SPI_STATUS));
     word |= SPI_STATUS_CDS; /* clear Cycle Done Status flag */
     word |= SPI_STATUS_BAS;  /* clear blocked flag */
     writew(word, (void *)(spi_char.mem_virt+SPI_STATUS));
    
     var = get_flash_status();
     return (var);
}

static int is_bios_protection(void)
{
    unsigned int ret;
   
    ret = spi_char.bios_ctrl_addr & BIOS_NOPROTECTION;
   
    return (!ret);
}

static  int disable_bios_protection(void)
{
    unsigned int temp;
    struct pci_dev *pdev = NULL;
   
    if(is_bios_protection())
    {
        temp = spi_char.bios_ctrl_addr | BIOS_NOPROTECTION;
        pdev = pci_get_device(SPI_VENDOR_ID, SPI_DEVICE_ID, NULL);
        if ( !pdev )
        {
            SPI_ERR("%s:spi_char_init-Could not find pci device/n", DRIVERNAME);
            return (0);
         }
        pci_write_config_byte(pdev, BIOS_CTRL, temp);
        spi_char.bios_ctrl_addr = temp;
    //release reference to device
    pci_dev_put(pdev);
    }
   
    return (1);
}

static int  is_flash_protection(void)
{
    unsigned int var;
   
    var = get_flash_status();
    var &= SPI_FLASH_PROTECTION;
    if(var || is_bios_protection())
    {
        return (1);
    }
    return (0);
}

static int disable_flash_protection(void)
{
    uint32_t word, var;
    uint8_t ret1, ret2;
   
    var = get_flash_status();
    var &= ~SPI_FLASH_PROTECTION;
    ret1 = set_flash_status(var);
   
    ret2 = disable_bios_protection();
   
    if(ret1 != var || !ret2)
    {
        return 0;
    }
   
    return (1);
}

static int write_byte(uint32_t addr, uint8_t byte)
{
    uint32_t word, var, val;
   
    var = byte;
    writel(var, (void *)(spi_char.mem_virt+SPI_DATA0));
    writel(addr, (void*)(spi_char.mem_virt+SPI_ADDR));
    word = readw((void *)(spi_char.mem_virt+SPI_CTRL));
    word |= SPI_CTRL_SCGO; /* set cycle */
    word |= SPI_CTRL_ACS; /* Enable Atomic Cycle Sequence */
    word &= ~SPI_CTRL_SPOP;
    word &= ~SPI_CTRL_COP;    
    word = word | SPI_CTRL_OPMENU_WR<<SPI_CTRL_COP_SHIFT; /* write */
    word &= ~SPI_CTRL_DBC;    
    word = word | 0x00<<SPI_CTRL_DBC_SHIFT; /* write count=1 */
    word |= SPI_CTRL_DC;
    
     /* check the status */
     val = readw((void *)(spi_char.mem_virt+SPI_STATUS));
     while(val & SPI_STATUS_SCIP)
     {
            val = readw((void *)(spi_char.mem_virt+SPI_STATUS));/* check the status */
     }
        
     writew(word, (void *)(spi_char.mem_virt+SPI_CTRL));
        
     /* check the status */
     val = readw((void *)(spi_char.mem_virt+SPI_STATUS));
     while(val & SPI_STATUS_SCIP)
     {
        val = readw((void *)(spi_char.mem_virt+SPI_STATUS));/* check the status */
     }
    
     word = readw((void *)(spi_char.mem_virt+SPI_STATUS));
     word |= SPI_STATUS_CDS; /* clear Cycle Done Status flag */
     word |= SPI_STATUS_BAS;  /* clear blocked flag */
     writew(word, (void *)(spi_char.mem_virt+SPI_STATUS));
    
    return (1);
}

static uint8_t read_byte(uint32_t addr)
{
    uint32_t word, var, val;
    uint8_t ret;
   
     word = readw((void *)(spi_char.mem_virt+SPI_STATUS));
     word |= SPI_STATUS_CDS; /* clear Cycle Done Status flag */
     word |= SPI_STATUS_BAS;  /* clear blocked flag */
     writew(word, (void *)(spi_char.mem_virt+SPI_STATUS));
    writel(addr, (void*)(spi_char.mem_virt+SPI_ADDR));
    word = readw((void *)(spi_char.mem_virt+SPI_CTRL));
    word |= SPI_CTRL_SCGO; /* set cycle */
    word &= ~SPI_CTRL_ACS; /* Diable Atomic Cycle Sequence */
    //word &= ~SPI_CTRL_SPOP;
    word &= ~SPI_CTRL_COP;    
    word = word | SPI_CTRL_OPMENU_RD<<SPI_CTRL_COP_SHIFT; /* read */
    word &= ~SPI_CTRL_DBC;    
    word = word | 0x00<<SPI_CTRL_DBC_SHIFT; /* read count=1 */
    word |= SPI_CTRL_DC;
    
     /* check the status */
      val = readw((void *)(spi_char.mem_virt+SPI_STATUS));/* check the status */
     while(val & SPI_STATUS_SCIP)
     {
          val = readw((void *)(spi_char.mem_virt+SPI_STATUS));/* check the status */
     }
        
     writew(word, (void *)(spi_char.mem_virt+SPI_CTRL));
     SPI_DBG("read_byte: ctrl=%08x/n", word);
        
     /* check the status */
     val = readw((void *)(spi_char.mem_virt+SPI_STATUS));
     while(val & SPI_STATUS_SCIP)
     {
        val = readw((void *)(spi_char.mem_virt+SPI_STATUS));/* check the status */
     }
     var = readl((void *)(spi_char.mem_virt+SPI_DATA0));
     SPI_DBG("read_byte: data=%08x", var);
     ret = var & 0xFF;
   
     word = readw((void *)(spi_char.mem_virt+SPI_STATUS));
     word |= SPI_STATUS_CDS; /* clear Cycle Done Status flag */
     word |= SPI_STATUS_BAS;  /* clear blocked flag */
     writew(word, (void *)(spi_char.mem_virt+SPI_STATUS));
    
    return (ret);
}

static int erase_chip(void)
{
     uint32_t word, var, val;
    
    /* disable flash protection */
    if(is_flash_protection())
    {
        if(!disable_flash_protection())
        {
            SPI_ERR("can't disable flash protection/n");
            return (0);
        }
        SPI_DBG("erase_chip is_flash_protection/n") ;
    }
  
    word = readw((void *)(spi_char.mem_virt+SPI_CTRL));
    word |= SPI_CTRL_SCGO; /* set cycle */
    word |= SPI_CTRL_ACS; /* Enable Atomic Cycle Sequence */
    word &= ~SPI_CTRL_SPOP;
    word &= ~SPI_CTRL_COP;    
    word = word | SPI_CTRL_OPMENU_CHIPERASE<<SPI_CTRL_COP_SHIFT; /* write */
    word &= ~SPI_CTRL_DBC;    
    word = word | 0x00<<SPI_CTRL_DBC_SHIFT; /* write count=1 */
    word &= ~SPI_CTRL_DC;
    
     /* check the status */
     val = readw((void *)(spi_char.mem_virt+SPI_STATUS));
     while(val & SPI_STATUS_SCIP)
     {
            val = readw((void *)(spi_char.mem_virt+SPI_STATUS));/* check the status */
     }
        
     writew(word, (void *)(spi_char.mem_virt+SPI_CTRL));
        
     /* check the status */
     val = readw((void *)(spi_char.mem_virt+SPI_STATUS));
     while(val & SPI_STATUS_SCIP)
     {
        val = readw((void *)(spi_char.mem_virt+SPI_STATUS));/* check the status */
     }
    
     word = readw((void *)(spi_char.mem_virt+SPI_STATUS));
     word |= SPI_STATUS_CDS; /* clear Cycle Done Status flag */
     word |= SPI_STATUS_BAS;  /* clear blocked flag */
     writew(word, (void *)(spi_char.mem_virt+SPI_STATUS));
     SPI_DBG("erase_chip() end/n");
    
     return (1);
}

static int erase_4K_sector(unsigned int idx)
{
    uint32_t addr;
    uint32_t word, var, val;
   
    if(((idx+1)<< SPI_FLASH_SECTOR_BIT) > SPI_FLASH_SIZE)
    {
        return (1);
    }
   
     /* disable flash protection */
    if(is_flash_protection())
    {
        if(!disable_flash_protection())
        {
            SPI_ERR("can't disable flash protection/n");
            return (0);
        }
    }
   
    addr = idx<<SPI_FLASH_SECTOR_BIT;
    writel(addr, (void*)(spi_char.mem_virt+SPI_ADDR));
    word = readw((void *)(spi_char.mem_virt+SPI_CTRL));
    word |= SPI_CTRL_SCGO; /* set cycle */
    word |= SPI_CTRL_ACS; /* Enable Atomic Cycle Sequence */
    word &= ~SPI_CTRL_SPOP;
    word &= ~SPI_CTRL_COP;    
    word = word | SPI_CTRL_OPMENU_4KERASE<<SPI_CTRL_COP_SHIFT; /* write */
    word &= ~SPI_CTRL_DBC;    
    word = word | 0x00<<SPI_CTRL_DBC_SHIFT; /* write count=1 */
    word &= ~SPI_CTRL_DC;
    
     /* check the status */
     val = readw((void *)(spi_char.mem_virt+SPI_STATUS));
     while(val & SPI_STATUS_SCIP)
     {
         val = readw((void *)(spi_char.mem_virt+SPI_STATUS));/* check the status */
     }
        
     writew(word, (void *)(spi_char.mem_virt+SPI_CTRL));
        
     /* check the status */
     val = readw((void *)(spi_char.mem_virt+SPI_STATUS));
     while(val & SPI_STATUS_SCIP)
     {
        val = readw((void *)(spi_char.mem_virt+SPI_STATUS));/* check the status */
     }
    
     word = readw((void *)(spi_char.mem_virt+SPI_STATUS));
     word |= SPI_STATUS_CDS; /* clear Cycle Done Status flag */
     word |= SPI_STATUS_BAS;  /* clear blocked flag */
     writew(word, (void *)(spi_char.mem_virt+SPI_STATUS));

     SPI_DBG("spi_4kerase end/n");
    
    return (1);
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息