D.TC.S25FL064A (W25Q64FV)是一个串行的spiflash,我们用于存储fpga程序(epcs),fpga上电时通过spi从该flash读取程序
进行fpga在线升级时,通过cpu的spi接口进行读写,该芯片只有一组spi接口,所以需要一个选路器OE进行选路,当cpu的gpio11拉低时,CPU的spi控制有效
当gpio11拉高时,FPGA的spi有效
由于cpu spi的差异,需要适当修改spi接口
以下是W25Q64在CPU BCM53003(mips架构)的驱动
epcs.c
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "w25p16.h"
#include
#include
#include
#include
#include
#include
#define W25_ERASE_CHIP 9
#define W25_ERASE_SECTOR 10
#define W25P16_READ 11
#define W25P16_WRITE 12
#define W25P1165_ID 13
#define SPI_IOC_OPER_FLASH 14
#define SPI_IOC_OPER_FLASH_DONE 15
static void *gpio_sih;
struct w25p flash;
#undef DEBUG_FPGA
#ifdef DEBUG_FPGA
#define debugk(fmt,args...) printk(fmt ,##args)
#else
#define debugk(fmt,args...)
#endif
/*epcs device struct */
struct epcs_dev_t {
struct cdev *pdev;
struct spi_device *spi;
int devno;
};
static struct epcs_dev_t *epcs = NULL;
static ssize_t epcs_cdev_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos)
{
return 0;
}
static ssize_t epcs_cdev_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos)
{
return 0;
}
static long epcs_cdev_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
w25_rw_date_t w25p16_date;
size_t retval = 0;
size_t retlen = 0;
unsigned int tmp, ret;
si_gpioout(gpio_sih, 1<<11, 0<<11,GPIO_HI_PRIORITY);
si_gpioouten(gpio_sih, 1<<11, 1<<11,GPIO_HI_PRIORITY);//0 mains do not clear other bits
//return 0;
switch (cmd) {
case W25_ERASE_CHIP:
//printk("\nnow erase chip\n");
retval = erase_chip(&flash);
break;
case W25_ERASE_SECTOR:
retval = __get_user(tmp, (__u32 __user *)arg);
if(retval == 0)
{
//printk("\n ++++tmp = 0x%08x+++++\n", tmp);
retval = erase_sector(&flash, tmp);
}
break;
case W25P16_READ:
retval = copy_from_user(&w25p16_date, (w25_rw_date_t *)arg, sizeof(w25_rw_date_t));
if(retval != 0)
break;
//printk("\nnow read chip:w25p16_date.addr = 0x%08x, w25p16_date.len = 0x%08x \n", (u32)w25p16_date.addr,w25p16_date.len);
retval = w25p16_read(&flash , w25p16_date.addr, w25p16_date.len, &retlen, w25p16_date.buf);
#if 0
ret = w25p16_read(&flash , 0x100000, 0xff, &retlen, buf_t);
if(ret)
printk("\n+++++++++\erase error ret = %d ++++\n", ret);
#endif
#if 0
{
int i = 0;
for(i = 0; i < 0x10; i++)
{
printk(" buf[%d ] = 0x%02x ",i, w25p16_date.buf[i]);
if(i % 8 == 0)
printk("\n");
}
}
#endif
if(retval == 0)
{
retval = copy_to_user((w25_rw_date_t *)arg, &w25p16_date, sizeof(w25_rw_date_t));
}
// msleep(1); /*this is needed,else cost a long time 2015-8-20 zhangjj */
break;
case W25P16_WRITE:
retval = copy_from_user(&w25p16_date, (w25_rw_date_t *)arg, sizeof(w25_rw_date_t));
if(retval != 0)
break;
retval = w25p16_write(&flash , w25p16_date.addr, w25p16_date.len, &retlen, w25p16_date.buf);
if(retval == 0)
{
retval = copy_to_user((w25_rw_date_t *)arg, &w25p16_date, sizeof(w25_rw_date_t));
}
// msleep(1); /*this is needed,else cost a long time 2015-8-20 zhangjj */
break;
#if 0
case W25P1165_ID:
w25p16_read_id(spi);
// w25p16_read_test(spi);
break;
#endif
default:
break;
}
si_gpioout(gpio_sih, 1<<11, 1<<11,GPIO_HI_PRIORITY);
si_gpioouten(gpio_sih, 1<<11, 1<<11,GPIO_HI_PRIORITY);//0 mains do not clear other bits
return retval;
}
static struct file_operations epcs_cdev_fops =
{
owner: THIS_MODULE,
read: epcs_cdev_read,
write: epcs_cdev_write,
unlocked_ioctl: epcs_cdev_ioctl,
};
static struct class *epcs_class;
int major_epcs;
static int __devinit epcs_probe(struct spi_device *spi)
{
int epcs_dev_no;
struct cdev *pdev;
alloc_chrdev_region(&epcs_dev_no, 0, 1, "epcs");
major_epcs = MAJOR(epcs_dev_no);
// #ifdef DEBUG_FPGA
debugk("FPGA reg alloced major num: %u\n", MAJOR(epcs_dev_no));
// #endif
pdev = cdev_alloc();
if (IS_ERR(pdev))
return PTR_ERR(pdev);
pdev->ops = &epcs_cdev_fops;
cdev_add(pdev, epcs_dev_no, 1);
epcs_class = class_create(THIS_MODULE, "epcs");
device_create(epcs_class, NULL, epcs_dev_no, NULL, "epcs");
spi->mode = SPI_MODE_3;
spi->bits_per_word = 8;
spi->max_speed_hz = 2000000;
spi_setup(spi);
epcs = kzalloc(sizeof *epcs, GFP_KERNEL);
if (!epcs) {
cdev_del(pdev);
unregister_chrdev_region(epcs_dev_no, 1);
return -ENOMEM;
}
epcs->spi = spi;
epcs->pdev = pdev;
epcs->devno = epcs_dev_no;
dev_set_drvdata(&spi->dev, epcs);
flash.spi = spi;
flash.mtd.size = 0x7fffff;
mutex_init(&(flash.lock));
return 0;
}
static int __devexit epcs_remove(struct spi_device *spi)
{
device_destroy(epcs_class, MKDEV(major_epcs, 0));
class_destroy(epcs_class);
if (epcs) {
if (epcs->pdev)
cdev_del(epcs->pdev);
if (epcs->devno)
unregister_chrdev_region(epcs->devno, 1);
kfree(epcs);
}
return 0;
}
static struct spi_driver epcs_driver = {
.driver = {
.name = "spi-epcs",
.bus = &spi_bus_type,
.owner = THIS_MODULE,
},
.probe = epcs_probe,
.remove = __devexit_p(epcs_remove),
};
static __init int epcs_init(void)
{
int val=1;
debugk("epcs spi driver init\n");
spi_register_driver(&epcs_driver);
#if 1 /*gpio init for set*/
if (!(gpio_sih = si_kattach(SI_OSH)))
return -ENODEV;
si_gpiosetcore(gpio_sih);
#endif
return 0;
}
module_init(epcs_init);
static __exit void epcs_exit(void)
{
debugk("epcs spi driver exit\n");
spi_unregister_driver(&epcs_driver);
}
module_exit(epcs_exit);
MODULE_DESCRIPTION ("epcs driver");
MODULE_AUTHOR ("[email protected]");
MODULE_LICENSE ("GPL");
w25q64.c
/*
* MTD SPI driver for ST M25Pxx (and similar) serial flash chips
*
* Author: Mike Lavender, [email protected]
*
* Copyright (c) 2005, Intec Automation Inc.
*
* Some parts are based on lart.c by Abraham Van Der Merwe
*
* Cleaned up and generalized based on mtd_dataflash.c
*
* This code is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "w25p16.h"
#include
/****************************************************************************/
static inline struct w25p *mtd_to_w25p(struct mtd_info *mtd)
{
return container_of(mtd, struct w25p, mtd);
}
/****************************************************************************/
/*
* Internal helper functions
*/
/*
* Read the status register, returning its value in the location
* Return the status register value.
* Returns negative if error occurred.
*/
static int read_sr(struct w25p *flash)
{
ssize_t retval;
u8 code = OPCODE_RDSR;
u8 val;
int ret;
struct spi_message message;
struct spi_transfer xfer;
unsigned char buf[14] = {0};
unsigned char rx_buf[14] = {0};
buf[0] = code;
/* Build our spi message */
spi_message_init(&message);
memset(&xfer, 0, sizeof(xfer));
xfer.len = 1;
//xfer.len = count ;
/* Can tx_buf and rx_buf be equal? The doc in spi.h is not sure... */
xfer.tx_buf = buf;
xfer.rx_buf = rx_buf;
spi_message_add_tail(&xfer, &message);
ret = spi_sync(flash->spi, &message);
if (ret < 0) {
dev_err(&flash->spi->dev, "error %d reading SR\n",
(int) ret);
return ret;
}
//printk("\n rx_buf[1]0x%08x, 0x%08x \n", rx_buf[1], rx_buf[0]);
return rx_buf[0];
}
/*
* Write status register 1 byte
* Returns negative if error occurred.
*/
static int write_sr(struct w25p *flash, u8 val)
{
flash->command[0] = OPCODE_WRSR;
flash->command[1] = val;
return spi_write(flash->spi, flash->command, 2);
}
/*
* Set write enable latch with Write Enable command.
* Returns negative if error occurred.
*/
static inline int write_enable(struct w25p *flash)
{
u8 code = OPCODE_WREN;
//return spi_write_then_read(flash->spi, &code, 1, NULL, 0);
return spi_write(flash->spi, &code, 1);
}
/*
* Service routine to read status register until ready, or timeout occurs.
* Returns non-zero if error.
*/
static int wait_till_ready(struct w25p *flash)
{
int count;
int sr;
/* one chip guarantees max 5 msec wait here after page writes,
* but potentially three seconds (!) after page erase.
*/
for (count = 0; count < MAX_READY_WAIT_COUNT; count++) {
if ((sr = read_sr(flash)) < 0)
break;
else if (!(sr & SR_WIP))
return 0;
msleep(1);
/* REVISIT sometimes sleeping would be best */
}
return 1;
}
/*
* Erase the whole flash memory
*
* Returns 0 if successful, non-zero otherwise.
*/
int erase_chip(struct w25p *flash)
{
DEBUG(MTD_DEBUG_LEVEL3, "%s: %s %lldKiB\n",
dev_name(&flash->spi->dev), __func__,
(long long)(flash->mtd.size >> 10));
/* Wait until finished previous write command. */
if (wait_till_ready(flash))
return 1;
/* Send write enable, then erase commands. */
write_enable(flash);
/* Set up command buffer. */
flash->command[0] = OPCODE_CHIP_ERASE;
spi_write(flash->spi, flash->command, 1);
return 0;
}
/*
* Erase one sector of flash memory at offset ``offset'' which is any
* address within the sector which should be erased.
*
* Returns 0 if successful, non-zero otherwise.
*/
int erase_sector(struct w25p *flash, u32 offset)
{
DEBUG(MTD_DEBUG_LEVEL3, "%s: %s %dKiB at 0x%08x\n",
dev_name(&flash->spi->dev), __func__,
flash->mtd.erasesize / 1024, offset);
/* Wait until finished previous write command. */
if (wait_till_ready(flash))
return 1;
/* Send write enable, then erase commands. */
write_enable(flash);
/* Set up command buffer. */
flash->command[0] = OPCODE_SE;
flash->command[1] = offset >> 16;
flash->command[2] = offset >> 8;
flash->command[3] = offset;
spi_write(flash->spi, flash->command, CMD_SIZE);
return 0;
}
/****************************************************************************/
/*
* MTD implementation
*/
/*
* Erase an address range on the flash chip. The address range may extend
* one or more erase sectors. Return an error is there is a problem erasing.
*/
static int w25p16_erase(struct mtd_info *mtd, struct erase_info *instr)
{
struct w25p *flash = mtd_to_w25p(mtd);
u32 addr,len;
uint32_t rem;
DEBUG(MTD_DEBUG_LEVEL2, "%s: %s %s 0x%llx, len %lld\n",
dev_name(&flash->spi->dev), __func__, "at",
(long long)instr->addr, (long long)instr->len);
/* sanity checks */
if (instr->addr + instr->len > flash->mtd.size)
return -EINVAL;
div_u64_rem(instr->len, mtd->erasesize, &rem);
if (rem)
return -EINVAL;
addr = instr->addr;
len = instr->len;
mutex_lock(&flash->lock);
/* whole-chip erase? */
if (len == flash->mtd.size && erase_chip(flash)) {
instr->state = MTD_ERASE_FAILED;
mutex_unlock(&flash->lock);
return -EIO;
/* REVISIT in some cases we could speed up erasing large regions
* by using OPCODE_SE instead of OPCODE_BE_4K. We may have set up
* to use "small sector erase", but that's not always optimal.
*/
/* "sector"-at-a-time erase */
} else {
while (len) {
if (erase_sector(flash, addr)) {
instr->state = MTD_ERASE_FAILED;
mutex_unlock(&flash->lock);
return -EIO;
}
addr += mtd->erasesize;
len -= mtd->erasesize;
}
}
mutex_unlock(&flash->lock);
instr->state = MTD_ERASE_DONE;
mtd_erase_callback(instr);
return 0;
}
/*
* Read an address range from the flash chip. The address range
* may be any size provided it is within the physical boundaries.
*/
int w25p16_read(struct w25p *flash , loff_t from, size_t len,
size_t *retlen, u_char *buf)
{
struct spi_transfer t[2];
struct spi_message m;
u_char rx_buf[256]= {0};
u_char tx_buf[256]= {0};
DEBUG(MTD_DEBUG_LEVEL2, "%s: %s %s 0x%08x, len %zd\n",
dev_name(&flash->spi->dev), __func__, "from",
(u32)from, len);
/* sanity checks */
if (!len)
return 0;
if (from + len > flash->mtd.size)
return -EINVAL;
spi_message_init(&m);
memset(t, 0, (sizeof t));
/* NOTE:
* OPCODE_FAST_READ (if available) is faster.
* Should add 1 byte DUMMY_BYTE.
*/
tx_buf[0] = OPCODE_READ;
tx_buf[1] = from >> 16;
tx_buf[2] = from >> 8;
tx_buf[3] = from;
t[0].tx_buf =tx_buf;
t[0].len = len;
t[0].rx_buf = rx_buf;
spi_message_add_tail(&t[0], &m);
// t[1].rx_buf = buf;
// t[1].len = len;
// spi_message_add_tail(&t[1], &m);
/* Byte count starts at zero. */
if (retlen)
*retlen = 0;
mutex_lock(&flash->lock);
#if 1
/* Wait till previous write/erase is done. */
if (wait_till_ready(flash)) {
/* REVISIT status return?? */
mutex_unlock(&flash->lock);
return 1;
}
#endif
/* FIXME switch to OPCODE_FAST_READ. It's required for higher
* clocks; and at this writing, every chip this driver handles
* supports that opcode.
*/
/* Set up the write data buffer. */
flash->command[0] = OPCODE_READ;
flash->command[1] = from >> 16;
flash->command[2] = from >> 8;
flash->command[3] = from;
spi_sync(flash->spi, &m);
*retlen = m.actual_length - CMD_SIZE - FAST_READ_DUMMY_BYTE;
memcpy(buf, rx_buf, len);
mutex_unlock(&flash->lock);
return 0;
}
/*
* Write an address range to the flash chip. Data must be written in
* FLASH_PAGESIZE chunks. The address range may be any size provided
* it is within the physical boundaries.
*/
int w25p16_write(struct w25p *flash , loff_t to, size_t len,
size_t *retlen, const u_char *buf)
{
u32 page_offset, page_size;
struct spi_transfer t[2];
struct spi_message m;
unsigned char tx_buf[256] = {0};
//unsigned char rx_buf[256] = {0};
t[0].tx_buf = tx_buf;
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 = tx_buf;
//t[0].rx_buf = rx_buf;
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);
#if 1 /*delete temp by zhangjiajie*/
/* Wait until finished previous write command. */
if (wait_till_ready(flash)) {
mutex_unlock(&flash->lock);
return 1;
}
#endif
write_enable(flash);
/* Set up the opcode in the write buffer. */
tx_buf [0] = OPCODE_PP;
tx_buf [1] = to >> 16;
tx_buf [2] = to >> 8;
tx_buf [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;
// t[0].tx_buf + 4 = buf;
t[0].len = CMD_SIZE + len ;
memcpy(tx_buf + 4 , buf, len);
spi_sync(flash->spi, &m);
*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;
}
void w25p16_read_id(struct spi_device *spi)
{
int ret;
struct spi_message message;
struct spi_transfer xfer;
unsigned char buf[14] = {0};
unsigned char rx_buf[14] = {0};
buf[0] = 0x90;
buf[1] = 0x00;
buf[2]=0x00;
buf[3]=0x00;
/* Build our spi message */
spi_message_init(&message);
memset(&xfer, 0, sizeof(xfer));
xfer.len = 6;
//xfer.len = count ;
/* Can tx_buf and rx_buf be equal? The doc in spi.h is not sure... */
xfer.tx_buf = buf;
xfer.rx_buf = rx_buf;
spi_message_add_tail(&xfer, &message);
ret = spi_sync(spi, &message);
{
int i;
for (i = 0; i < xfer.len; i++) {
printk(" %02x", rx_buf[i]);
}
printk("\n");
}
//printk("now test begin \n");
}
void w25p16_read_test(struct spi_device *spi)
{
int ret;
struct spi_message message;
struct spi_transfer xfer;
unsigned char buf[14] = {0};
unsigned char rx_buf[14] = {0};
buf[0] = 0x30;
buf[1] = 0x00;
buf[2]=0x10;
buf[3]=0x00;
/* Build our spi message */
spi_message_init(&message);
memset(&xfer, 0, sizeof(xfer));
xfer.len = 6;
//xfer.len = count ;
/* Can tx_buf and rx_buf be equal? The doc in spi.h is not sure... */
xfer.tx_buf = buf;
xfer.rx_buf = rx_buf;
spi_message_add_tail(&xfer, &message);
ret = spi_sync(spi, &message);
{
int i;
for (i = 0; i < xfer.len; i++) {
printk(" %02x", rx_buf[i]);
}
printk("\n");
}
//printk("now test begin \n");
}