/****************************************************************************** ** ** FILE NAME : danube_eeprom.c ** PROJECT : Danube ** MODULES : EEPROM ** ** DATE : 31 DEC 2004 ** AUTHOR : Liu Peng ** 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 ** $Date $Author $Comment ** 31 DEC 2004 Liu Peng Initiate Version ** 25 OCT 2006 Xu Liang Add GPL header. *******************************************************************************/ #define IFAP_EEPROM_DRV_VERSION "0.0.1" /* ************************************************** * * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if CONFIG_MODVERSIONS==1 # include #endif #define AMAZON_EEPROM_EMSG(fmt,args...) printk("%s:" fmt, __FUNCTION__, ##args) #if 0 #define AMAZON_EEPROM_DMSG(fmt,args...) printk("%s:" fmt, __FUNCTION__, ##args) #else #define AMAZON_EEPROM_DMSG(fmt,args...) #endif MODULE_LICENSE ("GPL"); MODULE_AUTHOR ("Peng Liu"); MODULE_DESCRIPTION ("IFAP EEPROM driver"); MODULE_SUPPORTED_DEVICE ("danube_eeprom"); MODULE_PARM (maj, "i"); MODULE_PARM_DESC (maj, "Major device number"); /* allow the user to set the major device number */ static int maj = 0; static ssize_t danube_eeprom_read (struct file *, char *, size_t, loff_t *); static ssize_t danube_eeprom_write (struct file *, const char *, size_t, loff_t *); static int danube_eeprom_ioctl (struct inode *, struct file *, unsigned int, unsigned long); static int danube_eeprom_open (struct inode *, struct file *); static int danube_eeprom_close (struct inode *, struct file *); //ifx_ssc.c extern int ifx_ssc_init (void); extern int ifx_ssc_open (struct inode *inode, struct file *filp); extern int ifx_ssc_close (struct inode *inode, struct file *filp); extern void ifx_ssc_cleanup_module (void); extern int ifx_ssc_ioctl (struct inode *inode, struct file *filp, unsigned int cmd, unsigned long data); extern ssize_t ifx_ssc_kwrite (int port, const char *kbuf, size_t len); extern ssize_t ifx_ssc_kread (int port, char *kbuf, size_t len); extern int ifx_ssc_cs_low (u32 pin); extern int ifx_ssc_cs_high (u32 pin); extern int ifx_ssc_txrx (char *tx_buf, u32 tx_len, char *rx_buf, u32 rx_len); extern int ifx_ssc_tx (char *tx_buf, u32 tx_len); extern int ifx_ssc_rx (char *rx_buf, u32 rx_len); /* CS: board dependant */ #define EEPROM_CS IFX_SSC_WHBGPOSTAT_OUT0_POS /* 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 /* Brief: EEPROM_RDSR command * Return: 0 OK * error code */ static int eeprom_rdsr (char *status) { int ret = 0; unsigned char cmd = EEPROM_RDSR; unsigned long flag; local_irq_save (flag); if ((ret = ifx_ssc_cs_low (EEPROM_CS))) { local_irq_restore (flag); return ret; } if ((ret = ifx_ssc_txrx (&cmd, 1, status, 1)) < 0) { ifx_ssc_cs_high (EEPROM_CS); local_irq_restore (flag); return ret; } if ((ret = ifx_ssc_cs_high (EEPROM_CS))) { local_irq_restore (flag); return ret; } local_irq_restore (flag); return ret; } /* Brief: wait for WIP is over * Return: * 0 WIP is over * <0 Error code */ static inline int eeprom_wip_over (void) { int ret = 0; u8 status; while (1) { ret = eeprom_rdsr (&status); AMAZON_EEPROM_DMSG ("status %x \n", status); if (ret) { AMAZON_EEPROM_EMSG ("read back status fails %d\n", ret); break; } else if (((status) & 1) != 0) { AMAZON_EEPROM_DMSG ("read back status not zero %x\n", status); } else { break; } } return ret; } /* Brief: EEPROM_WREN command * Return: 0 OK * error code */ static int eeprom_wren (void) { unsigned char cmd = EEPROM_WREN; int ret = 0; unsigned long flag; local_irq_save (flag); if ((ret = ifx_ssc_cs_low (EEPROM_CS))) { local_irq_restore (flag); return ret; } if ((ret = ifx_ssc_tx (&cmd, 1)) < 0) { ifx_ssc_cs_high (EEPROM_CS); local_irq_restore (flag); return ret; } if ((ret = ifx_ssc_cs_high (EEPROM_CS))) { local_irq_restore (flag); return ret; } local_irq_restore (flag); eeprom_wip_over (); return ret; } /* Brief: EEPROM_WRSR command * Return: 0 OK * error code */ static int eeprom_wrsr (void) { int ret = 0; unsigned char cmd[2]; cmd[0] = EEPROM_WRSR; cmd[1] = 0; if ((ret = eeprom_wren ())) { AMAZON_EEPROM_EMSG ("eeprom_wren fails\n"); return ret; } unsigned long flag; local_irq_save (flag); if ((ret = ifx_ssc_cs_low (EEPROM_CS))) { goto eeprom_wrsr_err_out; } if ((ret = ifx_ssc_tx (cmd, 2)) < 0) { ifx_ssc_cs_high (EEPROM_CS); goto eeprom_wrsr_err_out; } if ((ret = ifx_ssc_cs_low (EEPROM_CS))) { goto eeprom_wrsr_err_out; } local_irq_restore (flag); eeprom_wip_over (); return ret; eeprom_wrsr_err_out: local_irq_restore (flag); eeprom_wip_over (); return ret; } /* Brief: read EEPROM * Parameter: * addr * len * buf * Return: ok * -EFAULT */ static int eeprom_read (u32 addr, unsigned char *buf, u32 len) { int ret = 0; unsigned char write_buf[2]; u32 eff = 0; unsigned long flag; while (1) { eeprom_wip_over (); eff = EEPROM_PAGE_SIZE - (addr % EEPROM_PAGE_SIZE); eff = (eff < len) ? eff : len; //Format: //CMD + ADDR local_irq_save (flag); if ((ret = ifx_ssc_cs_low (EEPROM_CS)) < 0) { goto eeprom_read_err_out; } write_buf[0] = EEPROM_READ | ((u8) ((addr & 0x100) >> 5)); write_buf[1] = (addr & 0xff); if ((ret = ifx_ssc_txrx (write_buf, 2, buf, eff)) != eff) { AMAZON_EEPROM_EMSG ("ssc_txrx fails %d\n", ret); ifx_ssc_cs_high (EEPROM_CS); goto eeprom_read_err_out; } buf += ret; len -= ret; addr += ret; if ((ret = ifx_ssc_cs_high (EEPROM_CS))) { goto eeprom_read_err_out; } local_irq_restore (flag); if (len <= 0) { break; } } return ret; eeprom_read_err_out: local_irq_restore (flag); return ret; } /* Brief: write EEPROm * Parameter: * addr * len * buf * Return: 0 ok * -EFAULT */ static int eeprom_write (u32 addr, unsigned char *buf, u32 len) { int ret = 0; u32 eff = 0; unsigned char write_buf[2]; //unsigned char rx_buf[EEPROM_PAGE_SIZE]; //Format: //CMD + ADDR + DATA (up to EEPROM_PAGE_SIZE) while (1) { eeprom_wip_over (); if ((ret = eeprom_wren ())) { AMAZON_EEPROM_EMSG ("eeprom_wren fails\n"); return ret; } write_buf[0] = EEPROM_WRITE | ((u8) ((addr & 0x100) >> 5)); write_buf[1] = (addr & 0xff); eff = EEPROM_PAGE_SIZE - (addr % EEPROM_PAGE_SIZE); eff = (eff < len) ? eff : len; AMAZON_EEPROM_DMSG ("EEPROM Write:\n"); #if 0 int i; for (i = 0; i < eff; i++) { printk ("%2x ", buf[i]); if (i % 16 == 15) printk ("\n"); } printk ("\n"); #endif if ((ret = ifx_ssc_cs_low (EEPROM_CS))) { return ret; } if ((ret = ifx_ssc_tx (write_buf, 2)) < 0) { AMAZON_EEPROM_EMSG ("ssc_tx fails %d\n", ret); ifx_ssc_cs_high (EEPROM_CS); return ret; } if ((ret = ifx_ssc_tx (buf, eff)) != eff) { AMAZON_EEPROM_EMSG ("ssc_tx fails %d\n", ret); ifx_ssc_cs_high (EEPROM_CS); return ret; } buf += ret; len -= ret; addr += ret; if ((ret = ifx_ssc_cs_high (EEPROM_CS))) { return ret; } #if 0 printk ("<=="); eeprom_read ((addr - eff), rx_buf, eff); for (i = 0; i < eff; i++) { printk ("[%x]", rx_buf[i]); } printk ("\n"); #endif if (len <= 0) break; } return ret; } int danube_eeprom_open (struct inode *inode, struct file *filp) { filp->f_pos = 0; return 0; } int danube_eeprom_close (struct inode *inode, struct file *filp) { return 0; } int danube_eeprom_ioctl (struct inode *inode, struct file *filp, unsigned int cmd, unsigned long data) { return 0; } ssize_t danube_eeprom_kread (char *buf, size_t len, u32 addr) { int ret = 0; u32 data; printk ("addr:=%d\n", addr); printk ("len:=%d\n", len); if ((addr + len) > EEPROM_SIZE) { AMAZON_EEPROM_EMSG ("invalid len\n"); addr = 0; len = EEPROM_SIZE / 2; } if ((ret = ifx_ssc_open ((struct inode *) 0, NULL))) { AMAZON_EEPROM_EMSG ("danube_eeprom_open fails\n"); goto read_err_out; } data = (u32) IFX_SSC_MODE_RXTX; if ((ret = ifx_ssc_ioctl ((struct inode *) 0, NULL, IFX_SSC_RXTX_MODE_SET, (unsigned long) &data))) { AMAZON_EEPROM_EMSG ("set RXTX mode fails\n"); goto read_err_out; } if ((ret = eeprom_wrsr ())) { AMAZON_EEPROM_EMSG ("EEPROM reset fails\n"); goto read_err_out; } if ((ret = eeprom_read (addr, buf, len))) { AMAZON_EEPROM_EMSG ("eeprom read fails\n"); goto read_err_out; } read_err_out: if (ifx_ssc_close ((struct inode *) 0, NULL)) { AMAZON_EEPROM_EMSG ("danube_eeprom_close fails\n"); } return len; } EXPORT_SYMBOL (danube_eeprom_kread); static ssize_t danube_eeprom_read (struct file *filp, char *ubuf, size_t len, loff_t * off) { int ret = 0; unsigned char ssc_rx_buf[EEPROM_SIZE]; long flag; if (*off >= EEPROM_SIZE) return 0; if (*off + len > EEPROM_SIZE) len = EEPROM_SIZE - *off; if (len == 0) return 0; local_irq_save (flag); if ((ret = danube_eeprom_kread (ssc_rx_buf, len, *off)) < 0) { AMAZON_EEPROM_EMSG ("read fails, err=%x\n", ret); local_irq_restore (flag); return ret; } if (copy_to_user ((void *) ubuf, ssc_rx_buf, ret) != 0) { local_irq_restore (flag); ret = -EFAULT; } local_irq_restore (flag); *off += len; return len; } ssize_t danube_eeprom_kwrite (char *buf, size_t len, u32 addr) { int ret = 0; u32 data; if ((ret = ifx_ssc_open ((struct inode *) 0, NULL))) { AMAZON_EEPROM_EMSG ("danube_eeprom_open fails\n"); goto write_err_out; } data = (u32) IFX_SSC_MODE_RXTX; if ((ret = ifx_ssc_ioctl ((struct inode *) 0, NULL, IFX_SSC_RXTX_MODE_SET, (unsigned long) &data))) { AMAZON_EEPROM_EMSG ("set RXTX mode fails\n"); goto write_err_out; } if ((ret = eeprom_wrsr ())) { AMAZON_EEPROM_EMSG ("EEPROM reset fails\n"); goto write_err_out; } if ((ret = eeprom_write (addr, buf, len))) { AMAZON_EEPROM_EMSG ("eeprom write fails\n"); goto write_err_out; } write_err_out: if (ifx_ssc_close ((struct inode *) 0, NULL)) { AMAZON_EEPROM_EMSG ("danube_eeprom_close fails\n"); } return ret; } EXPORT_SYMBOL (danube_eeprom_kwrite); static ssize_t danube_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 = danube_eeprom_kwrite (ssc_tx_buf, len, *off); if (ret > 0) { *off = ret; } return ret; } loff_t danube_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 danube_eeprom_fops = { owner:THIS_MODULE, llseek:danube_eeprom_llseek, /* llseek */ read:danube_eeprom_read, /* read */ write:danube_eeprom_write, /* write */ ioctl:danube_eeprom_ioctl, /* ioctl */ open:danube_eeprom_open, /* open */ release:danube_eeprom_close, /* release */ }; int __init danube_eeprom_init (void) { int ret = 0; if ((ret = register_chrdev (maj, "eeprom", &danube_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", &danube_eeprom_fops)) < 0) { printk ("Unable to register major 0 for the Infineon Amazon EEPROM\n"); goto errout; } } } if (maj == 0) maj = ret; errout: return ret; } void __exit danube_eeprom_cleanup_module (void) { if (unregister_chrdev (maj, "eeprom")) { printk ("Unable to unregister major %d for the EEPROM\n", maj); } } module_exit (danube_eeprom_cleanup_module); module_init (danube_eeprom_init); #ifndef MODULE static int __init danube_eeprom_set_maj (char *str) { maj = simple_strtol (str, NULL, 0); return 1; } __setup ("eeprom_maj=", danube_eeprom_set_maj); #endif /* !MODULE */