diff options
Diffstat (limited to 'drivers/scp/spi_eeprom.c')
-rwxr-xr-x | drivers/scp/spi_eeprom.c | 350 |
1 files changed, 350 insertions, 0 deletions
diff --git a/drivers/scp/spi_eeprom.c b/drivers/scp/spi_eeprom.c new file mode 100755 index 00000000000..47c83a4679d --- /dev/null +++ b/drivers/scp/spi_eeprom.c @@ -0,0 +1,350 @@ +/* + * spi_eeprom.c + * + * 2003-2005 (c) MontaVista Software, Inc. This file is licensed under the + * terms of the GNU General Public License version 2. This program is + * licensed "as is" without any warranty of any kind, whether express + * or implied. + * + */ +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/proc_fs.h> +#include <linux/spinlock.h> +#include <linux/mtd/partitions.h> /* for mtd partitions */ + +//#undef SCP_DEBUG +#define SCP_DEBUG +#include "scp-dev.h" +DEFINE_SPINLOCK(spi_eeprom_lock); + +static struct spi_dev_desc eeprom_dev_desc = { + .baud = 1500000, /* 1.5Mbps */ + .tcss = 10, + .tcsh = 10, + .tcsr = 1, + .byteorder = 1, /* MSB-First */ + .polarity = 0, /* High-Active */ + .phase = 0, /* Sample-Then-Shift */ + +}; + +static struct mtd_info beech_spi_mtd[NUM_SPI_SLAVES]; + +static struct mtd_partition spi_partition_allmem[] = { + { + .name = "scp", + .offset = 0x0, + .size = 0x400, + }, +}; + +static int +spi_eeprom_io(int chipid, + const unsigned char **inbufs, unsigned int *incounts, + unsigned char **outbufs, unsigned int *outcounts) +{ + return scp_io(chipid, &eeprom_dev_desc, + inbufs, incounts, outbufs, outcounts, 0); +} + +int spi_eeprom_write_enable(int chipid, int enable) +{ + unsigned char inbuf[1]; + const unsigned char *inbufs[2]; + unsigned int incounts[2], outcounts[1]; + unsigned long flags; + int stat; + + inbuf[0] = enable ? ATMEL_WREN : ATMEL_WRDI; + inbufs[0] = inbuf; + incounts[0] = sizeof(inbuf); + inbufs[1] = NULL; + incounts[1] = 0; + outcounts[0] = 0; + spin_lock_irqsave(&spi_eeprom_lock, flags); + stat = spi_eeprom_io(chipid, inbufs, incounts, NULL, outcounts); + spin_unlock_irqrestore(&spi_eeprom_lock, flags); + return stat; +} + +static int spi_eeprom_read_status_nolock(int chipid) +{ + unsigned char inbuf[1], outbuf[1]; + const unsigned char *inbufs[2]; + unsigned char *outbufs[1]; + unsigned int incounts[2], outcounts[1]; + int stat; + + inbuf[0] = ATMEL_RDSR; + inbufs[0] = inbuf; + incounts[0] = sizeof(inbuf); + incounts[1] = 0; + outbufs[0] = outbuf; + outcounts[0] = sizeof(outbuf); + stat = spi_eeprom_io(chipid, inbufs, incounts, outbufs, outcounts); + if (stat < 0) { + printk("KERNEL_DEBUG %s bail\n", __FUNCTION__); + return stat; + } + return outbuf[0]; +} + +int spi_eeprom_read_status(int chipid) +{ + unsigned long flags; + int stat; + + spin_lock_irqsave(&spi_eeprom_lock, flags); + stat = spi_eeprom_read_status_nolock(chipid); + spin_unlock_irqrestore(&spi_eeprom_lock, flags); + return stat; +} + + +int spi_eeprom_read(struct mtd_info *mtd, ulong address, u_char * buf, + int len) +{ + unsigned char inbuf[3]; + const unsigned char *inbufs[2]; + unsigned char *outbufs[1]; + unsigned int incounts[2], outcounts[1]; + unsigned long flags; + int stat; + + SPI_DEV *spi_dev = mtd->priv; + + address &= 0xffff; + inbuf[0] = ATMEL_READ; + inbuf[1] = address >> 8; + inbuf[2] = address & 0xff; + + inbufs[0] = inbuf; + incounts[0] = sizeof(inbuf); + inbufs[1] = NULL; + incounts[1] = 0; + outbufs[0] = buf; + outcounts[0] = len; + spin_lock_irqsave(&spi_eeprom_lock, flags); + stat = spi_eeprom_io(spi_dev->slaveid, inbufs, incounts, + outbufs, outcounts); + spin_unlock_irqrestore(&spi_eeprom_lock, flags); + return stat; +} + +int spi_eeprom_write(struct mtd_info *mtd, ulong address, + const u_char * buf, int len) +{ + unsigned char inbuf[3]; + const unsigned char *inbufs[3]; + unsigned int incounts[3], outcounts[1]; + unsigned long flags; + int i, stat, w_len, remain; + + SPI_DEV *spi_dev = mtd->priv; + + remain = len; + while (remain > 0) { + stat = spi_eeprom_write_enable(spi_dev->slaveid, 1); + if (stat < 0) + return stat; + stat = spi_eeprom_read_status(spi_dev->slaveid); + if (stat < 0) + return stat; + if (!(stat & ATMEL_SR_WEN)) + return -EPERM; + + inbuf[0] = ATMEL_WRITE; + address &= 0xffff; + inbuf[1] = address >> 8; + inbuf[2] = address & 0xff; + + inbufs[0] = inbuf; + inbufs[1] = buf; + inbufs[2] = NULL; + + incounts[0] = sizeof(inbuf); + if (address & (ATMEL_WRITE_SIZE-1)) { + w_len = ATMEL_WRITE_SIZE - (address & (ATMEL_WRITE_SIZE-1)); + if (remain <= w_len) { + w_len = remain; + } + } + else { + w_len = (remain>= ATMEL_WRITE_SIZE) ? ATMEL_WRITE_SIZE:remain; + } + remain -= w_len; + address += w_len; + buf += w_len; + + incounts[1] = w_len; + incounts[2] = 0; + + outcounts[0] = 0; + spin_lock_irqsave(&spi_eeprom_lock, flags); + stat = spi_eeprom_io(spi_dev->slaveid, inbufs, incounts, + NULL, outcounts); + if (stat < 0) + goto unlock_return; + /* write start. max 10ms */ + for (i = 5; i > 0; i--) { + mdelay(2); + stat = spi_eeprom_read_status_nolock(spi_dev->slaveid); +#ifdef SCP_DEBUG + printk("w%d 0x%x\n",i, stat); +#endif + if (stat < 0) + goto unlock_return; + if (!(stat & ATMEL_SR_BSY)) { + /* Should bail out here */ + //printk("bail ATMEL_SR_BSY\n"); + break; + } + } + spin_unlock_irqrestore(&spi_eeprom_lock, flags); + if (i == 0) { + printk(KERN_DEBUG "bail -EIO\n"); + break; + //return -EIO; + } + } + return (len-remain); +unlock_return: + printk("bail unlock_return\n"); + spin_unlock_irqrestore(&spi_eeprom_lock, flags); + return stat; +} + + +/******************************************************************************* +* Function spi_read +* This function provides the mtd interface to read the buffer from the SPI flash +* at the offset +*******************************************************************************/ +int +spi_read(struct mtd_info *mtd, loff_t from, size_t len, + size_t * retlen, u_char * buf) +{ + int err = 0; + SPI_DEV *this = mtd->priv; +#ifdef SCP_DEBUG + printk(KERN_DEBUG "spi_read: from= 0x%08x, len= %d, buf= %p\n", + (uint) from, len, buf); +#endif + err = this->read_buf(mtd, from, buf, len); + if (err) { + printk(KERN_DEBUG "spi_read: read_data failed with err %d\n", err); + goto out; + } + + /* Tell the MTD device how many bytes have been read */ + *retlen = len; + +out: + return err; +} + +/******************************************************************************* +* Function spi_write +* This function provides the mtd interface to write the buffer to the SPI flash +* at the offset +*******************************************************************************/ +int +spi_write(struct mtd_info *mtd, loff_t to, size_t len, + size_t * retlen, const u_char * buf) +{ + int err = 0; + SPI_DEV *this = mtd->priv; + + printk(KERN_DEBUG "spi_write: to= 0x%08x, len= %d, buf= %p\n", + (uint) to, len, buf); + err = this->write_buf(mtd, to, buf, len); +#ifdef SCP_DEBUG + printk(KERN_DEBUG "spi_write: write_buf returned %d\n", err); +#endif + return err; +} + + +/******************************************************************************* +* Function spi_scan +* This function scans for the existence of the slave SPI Flash device +* by reading the Manufacturer ID, Memory Type and Chip Size. +*******************************************************************************/ +int spi_scan(struct mtd_info *mtd) +{ +#ifdef SCP_DEBUG + printk(KERN_DEBUG "spi_scan()\n"); +#endif + /* Fill in MTD driver data */ + mtd->type = MTD_DATAFLASH; + mtd->read = spi_read; + mtd->write = spi_write; + mtd->lock = NULL; + mtd->unlock = NULL; + mtd->writesize=0x00100000; + mtd->size = 0x00100000; + mtd->name = "AMCC-SCP"; + mtd->flags |= MTD_WRITEABLE; + mtd->owner = THIS_MODULE; + mtd->erasesize = 32; + return 0; +} + + +/******************************************************************************* + * Function: pine_scp_init + * This function is called by the LiveOak SSI device layer + * to initialize the device. +*******************************************************************************/ +int beech_scp_init(SPI_DEV * this) +{ + int id = this->slaveid; + int err = 0; + +#ifdef SCP_DEBUG + printk(KERN_DEBUG "beech_spi_init() : %s slave id : %d\n", + DEVICE_NAME, id); +#endif + memset(&(beech_spi_mtd[id]), 0, sizeof(beech_spi_mtd[id])); + + /* Link the private data with the MTD structure */ + beech_spi_mtd[id].priv = (void *) this; + + this->chip_delay = 100; + + this->read_buf = spi_eeprom_read; + this->write_buf = spi_eeprom_write; + + /* Init wait queue */ + //init_waitqueue_head(&this->wq); + + /* Scan to find existence of SPI Flash device */ + if ( (err = spi_scan(&beech_spi_mtd[id])) != 0) { + goto err; + } + + /* Adr MTD partition onto SPI device */ + add_mtd_partitions(&beech_spi_mtd[id], spi_partition_allmem, + sizeof(spi_partition_allmem) / + sizeof(spi_partition_allmem[0])); +err: + return err; +} + +/**************************************************************************** + * Function: pine_spi_cleanup + * This function is called by the Pine SCP device layer + * on exit. +*****************************************************************************/ +void beech_spi_cleanup(void) +{ + int i; +#ifdef SCP_DEBUG + printk(KERN_DEBUG "beech_spi_cleanup()\n"); +#endif + for(i = 0; i < NUM_SPI_SLAVES; i++) { + /* Unregister partitions */ + del_mtd_device(&beech_spi_mtd[i]); + } +} |