最近花了一个多星期写了一个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);
}