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"); } (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |