加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 百科 > 正文

SPI flash D.TC.S25FL064A (W25Q64FV)驱动

发布时间:2020-12-15 20:03:16 所属栏目:百科 来源:网络整理
导读:D.TC.S25FL064A (W25Q64FV)是一个串行的spiflash,我们用于存储fpga程序(epcs),fpga上电时通过spi从该flash读取程序 进行fpga在线升级时,通过cpu的spi接口进行读写,该芯片只有一组spi接口,所以需要一个选路器OE进行选路,当cpu的gpio11拉低时,CPU的spi

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 <asm/uaccess.h>
#include <linux/module.h>
#include <linux/version.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/kernel.h>
#include <linux/platform_device.h>
#include <linux/init.h>
#include <linux/spi/spi.h>
#include <linux/delay.h>
#include "w25p16.h"
#include <linux/semaphore.h>
#include <typedefs.h>
#include <bcmutils.h>
#include <siutils.h>
#include <bcmdevs.h>
#include <linux_gpio.h>

#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,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,GPIO_HI_PRIORITY);//0 mains do not clear other bits
//return 0;
	switch (cmd) {
	case W25_ERASE_CHIP:
		//printk("nnow erase chipn");
		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,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,sizeof(w25_rw_date_t));
		if(retval != 0)
			break;	
		retval  =  w25p16_write(&flash,w25p16_date.buf);
		if(retval == 0)
		{
			retval = copy_to_user((w25_rw_date_t *)arg,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,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,1,"epcs");

	major_epcs = MAJOR(epcs_dev_no);
//	#ifdef DEBUG_FPGA
	debugk("FPGA reg alloced major num: %un",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");


	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 initn");
	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 exitn");
	spi_unregister_driver(&epcs_driver);
}
module_exit(epcs_exit);

MODULE_DESCRIPTION ("epcs driver");
MODULE_AUTHOR ("zhangjj@huahuan.com");
MODULE_LICENSE ("GPL");


w25q64.c


/*
 * MTD SPI driver for ST M25Pxx (and similar) serial flash chips
 *
 * Author: Mike Lavender,mike@steroidmicros.com
 *
 * 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 <linux/init.h>
#include <linux/module.h>
#include <linux/device.h>
#include <linux/interrupt.h>
#include <linux/mutex.h>
#include <linux/math64.h>

#include <linux/mtd/mtd.h>
#include <linux/mtd/partitions.h>

#include <linux/spi/spi.h>
#include <linux/spi/flash.h>
#include "w25p16.h"
#include <linux/delay.h>
/****************************************************************************/



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,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 SRn",(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,0);
	return spi_write(flash->spi,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 %lldKiBn",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,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%08xn",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,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 %lldn","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 %zdn","from",(u32)from,len);

	/* sanity checks */
	if (!len)
		return 0;

	if (from + len > flash->mtd.size)
		return -EINVAL;

	spi_message_init(&m);
	memset(t,(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,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,"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,(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,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,rx_buf[i]);
		}
		printk("n");
    }	


	//printk("now test begin n");
	
}

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读