/****************************************************************************** ** ** FILE NAME : amazon_s_eeprom.c ** PROJECT : Amazon-s ** MODULES : EEPROM ** ** DESCRIPTION : X25040 EEPROM Driver ** COPYRIGHT : Copyright (c) 2006 ** Infineon Technologies AG ** Am Campeon 1-12, 85579 Neubiberg, Germany ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License as published by ** the Free Software Foundation; either version 2 of the License, or ** (at your option) any later version. ** ** HISTORY ** $Version $Date $Author $Comment ** V1.0 19/07/07 Wu Qi Ming ** 03 Jan 2008 Lei Chuanhua Clean up source code ** 04 Jan 2008 Lei Chuanhua Modify to adapt to new SPI API *******************************************************************************/ /* ************************************************** * * This driver was originally based on the INCA-IP driver, but due to * fundamental conceptual drawbacks there has been changed a lot. * * Based on INCA-IP driver Copyright (c) 2003 Gary Jennejohn * Based on the VxWorks drivers Copyright (c) 2002, Infineon Technologies. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #ifndef EXPORT_SYMTAB #define EXPORT_SYMTAB #endif #ifndef AUTOCONF_INCLUDED #include #endif /* AUTOCONF_INCLUDED */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define AMAZON_S_EEPROM_EMSG(fmt,args...) printk("%s:" fmt, __func__, ##args) //#define AMAZON_S_EEPROM_DBG #ifdef AMAZON_S_EEPROM_DBG #define AMAZON_S_EEPROM_DMSG(fmt,args...) printk("%s:" fmt, __func__, ##args) #define INLINE #else #define AMAZON_S_EEPROM_DMSG(fmt,args...) #define INLINE inline #endif static IFX_SSC_HANDLE *eeprom_handler; /* allow the user to set the major device number 60 ~ 63 for experimental */ static int maj = 60; /* commands for EEPROM, x25160, x25140 */ #define EEPROM_WREN ((u8)0x06) #define EEPROM_WRDI ((u8)0x04) #define EEPROM_RDSR ((u8)0x05) #define EEPROM_WRSR ((u8)0x01) #define EEPROM_READ ((u8)0x03) #define EEPROM_WRITE ((u8)0x02) #define EEPROM_PAGE_SIZE 4 #define EEPROM_SIZE 512 #define EEPROM_DETECT_COUNTER 100000 #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,52)) MODULE_PARM (maj, "i"); #else #include module_param(maj, int, 0600); #endif MODULE_PARM_DESC (maj, "EEPROM major device number"); static int eeprom_rdsr (char *status) { int ret = 0; u8 cmd = EEPROM_RDSR; if ((ret = amazon_s_sscTxRx(eeprom_handler,(char *)&cmd, sizeof(u8), status, sizeof(u8))) != 2){ AMAZON_S_EEPROM_EMSG("line %d amazon_s_sscTxRx fails %d\n", __LINE__, ret); return -1; } return 0; } static INLINE int eeprom_wip_over (void) { int ret = 0; u8 status; int count = 0; while (1) { ret = eeprom_rdsr (&status); AMAZON_S_EEPROM_DMSG ("status %x \n", status); if (ret) { AMAZON_S_EEPROM_EMSG ("read back status fails %d\n", ret); break; } else if ((status & 1) == 0) { break; } if (++count > EEPROM_DETECT_COUNTER) { AMAZON_S_EEPROM_EMSG("Detect counter out of range\n"); ret = -1; break; } } return ret; } static int eeprom_wren (void) { int ret = 0; u8 cmd = EEPROM_WREN; if ((ret = amazon_s_sscTx(eeprom_handler,(char *)&cmd, sizeof(u8))) != 1){ AMAZON_S_EEPROM_EMSG("line %d amazon_s_sscTxRx fails %d\n", __LINE__, ret); return ret; } eeprom_wip_over (); return 0; } static int eeprom_wrsr (void) { int ret = 0; u8 cmd[2]; cmd[0] = EEPROM_WRSR; cmd[1] = 0; if ((ret = eeprom_wren ())) { AMAZON_S_EEPROM_EMSG ("eeprom_wren fails\n"); return ret; } if ((ret = amazon_s_sscTx(eeprom_handler,(char *)&cmd, sizeof(cmd))) != 2){ AMAZON_S_EEPROM_EMSG("line %d amazon_s_sscTxRx fails %d\n", __LINE__, ret); return ret; } eeprom_wip_over(); return 0; } static int eeprom_read (u32 addr, unsigned char *buf, u32 len) { int ret = 0; u8 write_buf[2]; u32 eff = 0; int total = 0; while (1) { eeprom_wip_over (); eff = EEPROM_PAGE_SIZE - (addr % EEPROM_PAGE_SIZE); eff = (eff < len) ? eff : len; write_buf[0] = EEPROM_READ | ((u8) ((addr & 0x100) >> 5)); write_buf[1] = (addr & 0xff); if ((ret = amazon_s_sscTxRx (eeprom_handler, write_buf, 2, buf, eff)) != (eff + 2)) { AMAZON_S_EEPROM_EMSG ("amazon_s_sscTxRx fails %d\n", ret); goto eeprom_read_err_out; } buf += eff; len -= eff; addr += eff; total += eff; if (len <= 0) { break; } } eeprom_read_err_out: return total; } static int eeprom_write (u32 addr, unsigned char *buf, u32 len) { int ret = 0; u32 eff = 0; int start = 0; int total = 0; static u8 write_buf[EEPROM_SIZE] = {0}; /* XXX */ while (1) { eeprom_wip_over (); if ((ret = eeprom_wren ())) { AMAZON_S_EEPROM_EMSG ("eeprom_wren fails\n"); return ret; } write_buf[0] = EEPROM_WRITE | ((u8) ((addr & 0x100) >> 5)); write_buf[1] = (addr & 0xff); start = 2; eff = EEPROM_PAGE_SIZE - (addr % EEPROM_PAGE_SIZE); eff = (eff < len) ? eff : len; memcpy(write_buf + start, buf, eff); total = start + eff; if ((ret = amazon_s_sscTx(eeprom_handler, write_buf, total)) != total) { AMAZON_S_EEPROM_EMSG ("amazon_s_sscTxRx fails %d\n", ret); return ret; } buf += eff; len -= eff; addr += eff; if (len <= 0) break; } return 0; } static INLINE int amazon_s_eeprom_open (struct inode *inode, struct file *filp) { filp->f_pos = 0; return 0; } static INLINE int amazon_s_eeprom_close (struct inode *inode, struct file *filp) { return 0; } static INLINE int amazon_s_eeprom_ioctl (struct inode *inode, struct file *filp, unsigned int cmd, unsigned long data) { return 0; } ssize_t amazon_s_eeprom_kread (char *buf, size_t len, u32 addr) { int ret = 0; if ((addr + len) > EEPROM_SIZE) { AMAZON_S_EEPROM_EMSG ("invalid len\n"); addr = 0; len = EEPROM_SIZE / 2; } if ((ret = eeprom_wrsr ())) { AMAZON_S_EEPROM_EMSG ("EEPROM reset fails\n"); goto read_err_out; } if ((ret = eeprom_read (addr, buf, len)) != len) { AMAZON_S_EEPROM_EMSG ("eeprom read fails\n"); goto read_err_out; } read_err_out: return ret; } EXPORT_SYMBOL (amazon_s_eeprom_kread); static ssize_t amazon_s_eeprom_read (struct file *filp, char *ubuf, size_t len, loff_t * off) { int ret = 0; u8 ssc_rx_buf[EEPROM_SIZE]; if (*off >= EEPROM_SIZE) return 0; if (*off + len > EEPROM_SIZE) len = EEPROM_SIZE - *off; if (len == 0) return 0; if ((ret = amazon_s_eeprom_kread (ssc_rx_buf, len, *off)) < 0) { AMAZON_S_EEPROM_EMSG ("read fails, err=%x\n", ret); return ret; } if (copy_to_user ((void *) ubuf, ssc_rx_buf, ret) != 0) { ret = -EFAULT; } *off += len; return len; } ssize_t amazon_s_eeprom_kwrite (char *buf, size_t len, u32 addr) { int ret = 0; if ((ret = eeprom_wrsr ())) { AMAZON_S_EEPROM_EMSG ("EEPROM reset fails\n"); goto write_err_out; } if ((ret = eeprom_write (addr, buf, len))) { AMAZON_S_EEPROM_EMSG ("eeprom write fails\n"); goto write_err_out; } write_err_out: return ret; } EXPORT_SYMBOL (amazon_s_eeprom_kwrite); static ssize_t amazon_s_eeprom_write (struct file *filp, const char *ubuf, size_t len, loff_t * off) { int ret = 0; unsigned char ssc_tx_buf[EEPROM_SIZE]; if (*off >= EEPROM_SIZE) return 0; if ((len + *off) > EEPROM_SIZE) len = EEPROM_SIZE - *off; if ((ret = copy_from_user (ssc_tx_buf, ubuf, len))) { return EFAULT; } ret = amazon_s_eeprom_kwrite (ssc_tx_buf, len, *off); if (ret > 0) { *off = ret; } return ret; } static loff_t amazon_s_eeprom_llseek (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 = filp->f_pos + off; break; default: return -EINVAL; } if (newpos < 0) return -EINVAL; filp->f_pos = newpos; return newpos; } static struct file_operations amazon_s_eeprom_fops = { .owner = THIS_MODULE, .llseek = amazon_s_eeprom_llseek, .read = amazon_s_eeprom_read, .write = amazon_s_eeprom_write, .ioctl = amazon_s_eeprom_ioctl, .open = amazon_s_eeprom_open, .release = amazon_s_eeprom_close, }; #define IFX_EEPROM_MODE IFX_SSC_MODE_0 #define IFX_EEPROM_PRIORITY IFX_SSC_PRIO_LOW #define IFX_EEPROM_FRAGSIZE 512 #define IFX_EEPROM_MAXFIFOSIZE 32 #define IFX_EEPROM_CS IFX_SSC_WHBGPOSTAT_OUT3_POS #define IFX_EEPROM_NAME "amazon_s-eeprom" #ifdef CONFIG_USE_EMULATOR #define IFX_EEPROM_BAUDRATE 10000 /* 10KHz */ #else #define IFX_EEPROM_BAUDRATE 1000000 /* 1 MHz */ #endif #define IFAP_EEPROM_DRV_VERSION "1.0.1" static INLINE int amazon_s_eeprom_cs_handler(u32 csq, IFX_CS_DATA cs_data) { if (csq == IFX_SSC_CS_ON) { /* Low active */ return amazon_s_ssc_cs_low(cs_data); } else { return amazon_s_ssc_cs_high(cs_data); } } static INLINE void amazon_s_eeprom_version(void) { printk("Infineon Technologies SPI eeprom driver version %s \n", IFAP_EEPROM_DRV_VERSION); } static INLINE IFX_SSC_HANDLE danbue_eeprom_register(char *dev_name) { IFX_SSC_CONFIGURE_t ssc_cfg = {0}; ssc_cfg.baudrate = IFX_EEPROM_BAUDRATE; ssc_cfg.csset_cb = amazon_s_eeprom_cs_handler; ssc_cfg.cs_data = IFX_EEPROM_CS; ssc_cfg.fragSize = IFX_EEPROM_FRAGSIZE; ssc_cfg.maxFIFOSize = IFX_EEPROM_MAXFIFOSIZE; ssc_cfg.ssc_mode = IFX_EEPROM_MODE; ssc_cfg.ssc_prio = IFX_EEPROM_PRIORITY; return amazon_s_sscAllocConnection(dev_name, &ssc_cfg); } static int __init amazon_s_eeprom_init (void) { int ret = 0; if ((ret = register_chrdev (maj, "eeprom", &amazon_s_eeprom_fops)) < 0) { printk ("Unable to register major %d for the Infineon Amazon EEPROM\n", maj); if (maj == 0) { goto errout; } else { maj = 0; if ((ret = register_chrdev (maj, "ssc", &amazon_s_eeprom_fops)) < 0) { printk ("Unable to register major 0 for the Infineon Amazon EEPROM\n"); goto errout; } } } if (maj == 0) maj = ret; eeprom_handler = danbue_eeprom_register(IFX_EEPROM_NAME); if (eeprom_handler == NULL) { printk("%s failed to register eeprom\n", __func__); unregister_chrdev (maj, "eeprom"); return -ENOMEM; } amazon_s_eeprom_version(); errout: return ret; } static void __exit amazon_s_eeprom_exit (void) { unregister_chrdev (maj, "eeprom"); amazon_s_sscFreeConnection(eeprom_handler); } module_init (amazon_s_eeprom_init); module_exit (amazon_s_eeprom_exit); MODULE_LICENSE ("GPL"); MODULE_AUTHOR ("Peng Liu"); MODULE_DESCRIPTION ("IFAP EEPROM driver"); MODULE_SUPPORTED_DEVICE ("amazon_s_eeprom"); #ifndef MODULE static int __init amazon_s_eeprom_set_maj (char *str) { maj = simple_strtol (str, NULL, 0); return 1; } __setup ("eeprom_maj=", amazon_s_eeprom_set_maj); #endif /* !MODULE */