From dc507a9e7eba1c4e31e7b5205b9fc91f4cfe6864 Mon Sep 17 00:00:00 2001 From: John Crispin Date: Fri, 14 Dec 2007 23:23:10 +0000 Subject: [PATCH] fix up eeprom and add ssc driver ... this needs a lot of work SVN-Revision: 9761 --- target/linux/danube/config-2.6.23 | 1 + .../danube/files/arch/mips/danube/Kconfig | 4 + .../danube/files/drivers/char/danube_eeprom.c | 629 +++-- .../danube/files/drivers/char/danube_ssc.c | 2145 +++++++++++++++++ .../files/include/asm-mips/danube/danube.h | 11 +- .../include/asm-mips/danube/danube_irq.h | 4 + .../danube/ifx_peripheral_definitions.h | 119 + .../files/include/asm-mips/danube/ifx_ssc.h | 258 ++ .../include/asm-mips/danube/ifx_ssc_defines.h | 547 +++++ target/linux/danube/patches/110-drivers.patch | 5 +- 10 files changed, 3384 insertions(+), 339 deletions(-) create mode 100644 target/linux/danube/files/drivers/char/danube_ssc.c create mode 100644 target/linux/danube/files/include/asm-mips/danube/ifx_peripheral_definitions.h create mode 100644 target/linux/danube/files/include/asm-mips/danube/ifx_ssc.h create mode 100644 target/linux/danube/files/include/asm-mips/danube/ifx_ssc_defines.h diff --git a/target/linux/danube/config-2.6.23 b/target/linux/danube/config-2.6.23 index 07a019ec61..887519324f 100644 --- a/target/linux/danube/config-2.6.23 +++ b/target/linux/danube/config-2.6.23 @@ -46,6 +46,7 @@ CONFIG_DANUBE_GPIO=y CONFIG_DANUBE_LED=y CONFIG_DANUBE_MII0=y CONFIG_DANUBE_MII1=y +CONFIG_DANUBE_SSC=y CONFIG_DANUBE_WDT=y CONFIG_DEVPORT=y # CONFIG_DM9000 is not set diff --git a/target/linux/danube/files/arch/mips/danube/Kconfig b/target/linux/danube/files/arch/mips/danube/Kconfig index fded6d4f0d..1c5ad72ca5 100644 --- a/target/linux/danube/files/arch/mips/danube/Kconfig +++ b/target/linux/danube/files/arch/mips/danube/Kconfig @@ -24,6 +24,10 @@ config DANUBE_GPIO bool "Danube gpio" default y +config DANUBE_SSC + bool "Danube ssc" + default y + config DANUBE_EEPROM bool "Danube eeprom" default y diff --git a/target/linux/danube/files/drivers/char/danube_eeprom.c b/target/linux/danube/files/drivers/char/danube_eeprom.c index 96fd777274..db0f610370 100644 --- a/target/linux/danube/files/drivers/char/danube_eeprom.c +++ b/target/linux/danube/files/drivers/char/danube_eeprom.c @@ -1,57 +1,31 @@ -/****************************************************************************** -** -** 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 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 driver was originally based on the INCA-IP driver, but due to - * fundamental conceptual drawbacks there has been changed a lot. + * 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. * - * Based on INCA-IP driver Copyright (c) 2003 Gary Jennejohn - * Based on the VxWorks drivers Copyright (c) 2002, Infineon Technologies. + * 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. * - * 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 driver was originally based on the INCA-IP driver, but due to + * fundamental conceptual drawbacks there has been changed a lot. * - * 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. + * Based on INCA-IP driver Copyright (c) 2003 Gary Jennejohn + * Based on the VxWorks drivers Copyright (c) 2002, Infineon Technologies. * - * 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 + * Copyright (C) 2006 infineon + * Copyright (C) 2007 John Crispin * */ -#ifndef EXPORT_SYMTAB -#define EXPORT_SYMTAB -#endif -#include + +#define IFAP_EEPROM_DRV_VERSION "0.0.1" + #include #include #include @@ -61,7 +35,6 @@ #include #include #include -#include #include #include #include @@ -81,33 +54,15 @@ #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 int danube_eeprom_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, +static ssize_t danube_eeprom_fops_read (struct file *, char *, size_t, loff_t *); +static ssize_t danube_eeprom_fops_write (struct file *, const char *, size_t, loff_t *); static int danube_eeprom_ioctl (struct inode *, struct file *, unsigned int, unsigned long); @@ -124,284 +79,277 @@ extern int ifx_ssc_ioctl (struct inode *inode, struct file *filp, 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); +extern int ifx_ssc_cs_low (unsigned int pin); +extern int ifx_ssc_cs_high (unsigned int pin); +extern int ifx_ssc_txrx (char *tx_buf, unsigned int tx_len, char *rx_buf, unsigned int rx_len); +extern int ifx_ssc_tx (char *tx_buf, unsigned int tx_len); +extern int ifx_ssc_rx (char *rx_buf, unsigned int 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_WREN ((unsigned char)0x06) +#define EEPROM_WRDI ((unsigned char)0x04) +#define EEPROM_RDSR ((unsigned char)0x05) +#define EEPROM_WRSR ((unsigned char)0x01) +#define EEPROM_READ ((unsigned char)0x03) +#define EEPROM_WRITE ((unsigned char)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; + + local_irq_save(flag); + + if ((ret = ifx_ssc_cs_low (EEPROM_CS))) + { + local_irq_restore(flag); + goto out; } - 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_txrx (&cmd, 1, status, 1)) < 0) + { + ifx_ssc_cs_high(EEPROM_CS); + local_irq_restore(flag); + goto out; } - if ((ret = ifx_ssc_cs_high (EEPROM_CS))) { - local_irq_restore (flag); - return ret; + if ((ret = ifx_ssc_cs_high(EEPROM_CS))) + { + local_irq_restore(flag); + goto out; } - local_irq_restore (flag); + + local_irq_restore(flag); + +out: 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); + unsigned char status; + + while (1) + { + ret = eeprom_rdsr(&status); + printk("status %x \n", status); + + if (ret) + { + printk("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 { + + if (((status) & 1) != 0) + printk("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; + + local_irq_save(flag); + if ((ret = ifx_ssc_cs_low(EEPROM_CS))) + { + local_irq_restore(flag); + goto out; } - if ((ret = ifx_ssc_tx (&cmd, 1)) < 0) { - ifx_ssc_cs_high (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); + goto out; } - if ((ret = ifx_ssc_cs_high (EEPROM_CS))) { - local_irq_restore (flag); - return ret; + if ((ret = ifx_ssc_cs_high(EEPROM_CS))) + { + local_irq_restore(flag); + goto out; } - local_irq_restore (flag); - eeprom_wip_over (); - return ret; + local_irq_restore(flag); + eeprom_wip_over(); + +out: + return ret; } -/* Brief: EEPROM_WRSR command - * Return: 0 OK - * error code - */ static int eeprom_wrsr (void) { int ret = 0; unsigned char cmd[2]; + unsigned long flag; + 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 = eeprom_wren())) + { + printk ("eeprom_wren fails\n"); + goto out1; } - if ((ret = ifx_ssc_cs_low (EEPROM_CS))) { - goto eeprom_wrsr_err_out; + local_irq_save(flag); + + if ((ret = ifx_ssc_cs_low(EEPROM_CS))) + goto out; + + if ((ret = ifx_ssc_tx(cmd, 2)) < 0) { + ifx_ssc_cs_high(EEPROM_CS); + goto out; } - local_irq_restore (flag); - eeprom_wip_over (); + + if ((ret = ifx_ssc_cs_low(EEPROM_CS))) + goto out; + + local_irq_restore(flag); + eeprom_wip_over(); + return ret; - eeprom_wrsr_err_out: + +out: local_irq_restore (flag); eeprom_wip_over (); + +out1: return ret; } -/* Brief: read EEPROM - * Parameter: - * addr - * len - * buf - * Return: ok - * -EFAULT - */ static int -eeprom_read (u32 addr, unsigned char *buf, u32 len) +eeprom_read (unsigned int addr, unsigned char *buf, unsigned int len) { int ret = 0; unsigned char write_buf[2]; - u32 eff = 0; - + unsigned int eff = 0; unsigned long flag; - while (1) { - eeprom_wip_over (); + 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; - } + local_irq_save(flag); + + if ((ret = ifx_ssc_cs_low(EEPROM_CS)) < 0) + goto out; - write_buf[0] = EEPROM_READ | ((u8) ((addr & 0x100) >> 5)); + write_buf[0] = EEPROM_READ | ((unsigned char) ((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); + if ((ret = ifx_ssc_txrx (write_buf, 2, buf, eff)) != eff) + { + printk("ssc_txrx fails %d\n", ret); ifx_ssc_cs_high (EEPROM_CS); - goto eeprom_read_err_out; + goto 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; - } + + if ((ret = ifx_ssc_cs_high(EEPROM_CS))) + goto out; + + local_irq_restore(flag); + + if (len <= 0) + goto out2; } - return ret; - eeprom_read_err_out: +out: local_irq_restore (flag); +out2: return ret; } -/* Brief: write EEPROm - * Parameter: - * addr - * len - * buf - * Return: 0 ok - * -EFAULT - */ static int -eeprom_write (u32 addr, unsigned char *buf, u32 len) +eeprom_write (unsigned int addr, unsigned char *buf, unsigned int len) { int ret = 0; - u32 eff = 0; + unsigned int eff = 0; unsigned char write_buf[2]; + int i; + unsigned char rx_buf[EEPROM_PAGE_SIZE]; + + while (1) + { + eeprom_wip_over(); - //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; + if ((ret = eeprom_wren())) + { + printk("eeprom_wren fails\n"); + goto out; } - write_buf[0] = EEPROM_WRITE | ((u8) ((addr & 0x100) >> 5)); + + write_buf[0] = EEPROM_WRITE | ((unsigned char) ((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; + + printk("EEPROM Write:\n"); for (i = 0; i < eff; i++) { - printk ("%2x ", buf[i]); - if (i % 16 == 15) - printk ("\n"); + printk("%2x ", buf[i]); + if ((i % 16) == 15) + printk("\n"); } - printk ("\n"); -#endif + printk("\n"); - if ((ret = ifx_ssc_cs_low (EEPROM_CS))) { - return ret; - } + if ((ret = ifx_ssc_cs_low(EEPROM_CS))) + goto out; - 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 (write_buf, 2)) < 0) + { + printk("ssc_tx fails %d\n", ret); + ifx_ssc_cs_high(EEPROM_CS); + goto out; } - 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; + if ((ret = ifx_ssc_tx (buf, eff)) != eff) + { + printk("ssc_tx fails %d\n", ret); + ifx_ssc_cs_high(EEPROM_CS); + goto out; } buf += ret; len -= ret; addr += ret; - if ((ret = ifx_ssc_cs_high (EEPROM_CS))) { - return ret; - } -#if 0 + if ((ret = ifx_ssc_cs_high (EEPROM_CS))) + goto out; + printk ("<=="); - eeprom_read ((addr - eff), rx_buf, eff); - for (i = 0; i < eff; i++) { + 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; } +out: return ret; } @@ -419,139 +367,156 @@ danube_eeprom_close (struct inode *inode, struct file *filp) } int -danube_eeprom_ioctl (struct inode *inode, struct file *filp, unsigned int cmd, - unsigned long data) +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) +danube_eeprom_read (char *buf, size_t len, unsigned int 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"); + unsigned int data; + + printk("addr:=%d\n", addr); + printk("len:=%d\n", len); + + if ((addr + len) > EEPROM_SIZE) + { + printk("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; + if ((ret = ifx_ssc_open ((struct inode *) 0, NULL))) + { + printk("danube_eeprom_open fails\n"); + goto 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; - } + data = (unsigned int)IFX_SSC_MODE_RXTX; - if ((ret = eeprom_wrsr ())) { - AMAZON_EEPROM_EMSG ("EEPROM reset fails\n"); - goto read_err_out; + if ((ret = ifx_ssc_ioctl ((struct inode *) 0, NULL, IFX_SSC_RXTX_MODE_SET, (unsigned long) &data))) + { + printk("set RXTX mode fails\n"); + goto out; } - if ((ret = eeprom_read (addr, buf, len))) { - AMAZON_EEPROM_EMSG ("eeprom read fails\n"); - goto read_err_out; + if ((ret = eeprom_wrsr ())) + { + printk("EEPROM reset fails\n"); + goto out; } - read_err_out: - if (ifx_ssc_close ((struct inode *) 0, NULL)) { - AMAZON_EEPROM_EMSG ("danube_eeprom_close fails\n"); + if ((ret = eeprom_read (addr, buf, len))) + { + printk("eeprom read fails\n"); + goto out; } - return len; -} +out: + if (ifx_ssc_close ((struct inode *) 0, NULL)) + printk("danube_eeprom_close fails\n"); -EXPORT_SYMBOL (danube_eeprom_kread); + return len; +} +EXPORT_SYMBOL(danube_eeprom_read); static ssize_t -danube_eeprom_read (struct file *filp, char *ubuf, size_t len, loff_t * off) +danube_eeprom_fops_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); + + local_irq_save(flag); + + if ((ret = danube_eeprom_read(ssc_rx_buf, len, *off)) < 0) + { + printk("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); + + if (copy_to_user((void*)ubuf, ssc_rx_buf, ret) != 0) + { + local_irq_restore(flag); ret = -EFAULT; } - local_irq_restore (flag); + + local_irq_restore(flag); *off += len; + return len; } ssize_t -danube_eeprom_kwrite (char *buf, size_t len, u32 addr) +danube_eeprom_write (char *buf, size_t len, unsigned int 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; + unsigned int data; + + if ((ret = ifx_ssc_open ((struct inode *) 0, NULL))) + { + printk ("danube_eeprom_open fails\n"); + goto 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; + data = (unsigned int) IFX_SSC_MODE_RXTX; + + if ((ret = ifx_ssc_ioctl ((struct inode *) 0, NULL, IFX_SSC_RXTX_MODE_SET, (unsigned long) &data))) + { + printk ("set RXTX mode fails\n"); + goto out; } if ((ret = eeprom_wrsr ())) { - AMAZON_EEPROM_EMSG ("EEPROM reset fails\n"); - goto write_err_out; + printk ("EEPROM reset fails\n"); + goto out; } if ((ret = eeprom_write (addr, buf, len))) { - AMAZON_EEPROM_EMSG ("eeprom write fails\n"); - goto write_err_out; + printk ("eeprom write fails\n"); + goto out; } - write_err_out: - if (ifx_ssc_close ((struct inode *) 0, NULL)) { - AMAZON_EEPROM_EMSG ("danube_eeprom_close fails\n"); - } +out: + if (ifx_ssc_close ((struct inode *) 0, NULL)) + printk ("danube_eeprom_close fails\n"); + return ret; } - -EXPORT_SYMBOL (danube_eeprom_kwrite); +EXPORT_SYMBOL(danube_eeprom_write); static ssize_t -danube_eeprom_write (struct file *filp, const char *ubuf, size_t len, - loff_t * off) +danube_eeprom_fops_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))) { + + if ((ret = copy_from_user (ssc_tx_buf, ubuf, len))) return EFAULT; - } - ret = danube_eeprom_kwrite (ssc_tx_buf, len, *off); - if (ret > 0) { + + ret = danube_eeprom_write (ssc_tx_buf, len, *off); + + if (ret > 0) *off = ret; - } + return ret; } @@ -560,33 +525,34 @@ danube_eeprom_llseek (struct file * filp, loff_t off, int whence) { loff_t newpos; switch (whence) { - case 0: /*SEEK_SET */ + case SEEK_SET: newpos = off; break; - case 1: /*SEEK_CUR */ + + case 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 */ + read:danube_eeprom_fops_read, + write:danube_eeprom_fops_write, ioctl:danube_eeprom_ioctl, - /* ioctl */ - open:danube_eeprom_open, /* open */ + open:danube_eeprom_open, release:danube_eeprom_close, - /* release */ }; int __init @@ -594,46 +560,37 @@ 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; - } - } + danube_eeprom_maj = register_chrdev(0, "eeprom", &danube_eeprom_fops); + + if (danube_eeprom_maj < 0) + { + printk("failed to register eeprom device\n"); + ret = -EINVAL; + + goto out; } - if (maj == 0) - maj = ret; - errout: + + printk("danube_eeprom : /dev/eeprom mayor %d\n", danube_eeprom_maj); + +out: return ret; } void __exit danube_eeprom_cleanup_module (void) { - if (unregister_chrdev (maj, "eeprom")) { + /*if (unregister_chrdev (danube_eeprom_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; -} +MODULE_LICENSE ("GPL"); +MODULE_AUTHOR ("Peng Liu"); +MODULE_DESCRIPTION ("IFAP EEPROM driver"); +MODULE_SUPPORTED_DEVICE ("danube_eeprom"); + -__setup ("eeprom_maj=", danube_eeprom_set_maj); -#endif /* !MODULE */ diff --git a/target/linux/danube/files/drivers/char/danube_ssc.c b/target/linux/danube/files/drivers/char/danube_ssc.c new file mode 100644 index 0000000000..cd0b4d387a --- /dev/null +++ b/target/linux/danube/files/drivers/char/danube_ssc.c @@ -0,0 +1,2145 @@ +/* + * 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. + * + * Copyright (C) 2006 infineon + * Copyright (C) 2007 John Crispin + * + */ + +// ### TO DO: general issues: +// - power management +// - interrupt handling (direct/indirect) +// - pin/mux-handling (just overall concept due to project dependency) +// - multiple instances capability +// - slave functionality + +#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 + +#ifdef SSC_FRAME_INT_ENABLE +#undef SSC_FRAME_INT_ENABLE +#endif + +#define not_yet + +#define SPI_VINETIC + + + +/* allow the user to set the major device number */ +static int maj = 0; + +/* + * This is the per-channel data structure containing pointers, flags + * and variables for the port. This driver supports a maximum of PORT_CNT. + * isp is allocated in ifx_ssc_init() based on the chip version. + */ +static struct ifx_ssc_port *isp; + +/* prototypes for fops */ +static ssize_t ifx_ssc_read (struct file *, char *, size_t, loff_t *); +static ssize_t ifx_ssc_write (struct file *, const char *, size_t, loff_t *); +//static unsigned int ifx_ssc_poll(struct file *, struct poll_table_struct *); +int ifx_ssc_ioctl (struct inode *, struct file *, unsigned int, + unsigned long); +int ifx_ssc_open (struct inode *, struct file *); +int ifx_ssc_close (struct inode *, struct file *); + +/* other forward declarations */ +static unsigned int ifx_ssc_get_kernel_clk (struct ifx_ssc_port *info); +static void ifx_ssc_rx_int (int, void *, struct pt_regs *); +static void ifx_ssc_tx_int (int, void *, struct pt_regs *); +static void ifx_ssc_err_int (int, void *, struct pt_regs *); +#ifdef SSC_FRAME_INT_ENABLE +static void ifx_ssc_frm_int (int, void *, struct pt_regs *); +#endif +static void tx_int (struct ifx_ssc_port *); +static int ifx_ssc1_read_proc (char *, char **, off_t, int, int *, void *); +static void ifx_gpio_init (void); +/************************************************************************ + * Function declaration + ************************************************************************/ +//interrupt.c +extern unsigned int danube_get_fpi_hz (void); +extern void disable_danube_irq (unsigned int irq_nr); +extern void enable_danube_irq (unsigned int irq_nr); +extern void mask_and_ack_danube_irq (unsigned int irq_nr); + +/*****************************************************************/ +typedef struct { + int (*request) (unsigned int, irq_handler_t handler, + unsigned long, const char *, void *); + void (*free) (unsigned int irq, void *dev_id); + void (*enable) (unsigned int irq); + void (*disable) (unsigned int irq); + void (*clear) (unsigned int irq); +} ifx_int_wrapper_t; + +static ifx_int_wrapper_t ifx_int_wrapper = { + request:request_irq, // IM action: enable int + free:free_irq, // IM action: disable int + enable:enable_danube_irq, + disable:disable_danube_irq, + clear:mask_and_ack_danube_irq, + //end: +}; + +/* Fops-struct */ +static struct file_operations ifx_ssc_fops = { + owner:THIS_MODULE, + read:ifx_ssc_read, /* read */ + write:ifx_ssc_write, /* write */ +// poll: ifx_ssc_poll, /* poll */ + ioctl:ifx_ssc_ioctl, /* ioctl */ + open:ifx_ssc_open, /* open */ + release:ifx_ssc_close, /* release */ +}; + +static inline unsigned int +ifx_ssc_get_kernel_clk (struct ifx_ssc_port *info) +{ // ATTENTION: This function assumes that the CLC register is set with the + // appropriate value for RMC. + unsigned int rmc; + + rmc = (READ_PERIPHERAL_REGISTER (info->mapbase + IFX_SSC_CLC) & + IFX_CLC_RUN_DIVIDER_MASK) >> IFX_CLC_RUN_DIVIDER_OFFSET; + if (rmc == 0) { + printk ("ifx_ssc_get_kernel_clk rmc==0 \n"); + return (0); + } + return (danube_get_fpi_hz () / rmc); +} + +#ifndef not_yet +#ifdef IFX_SSC_INT_USE_BH +/* + * This routine is used by the interrupt handler to schedule + * processing in the software interrupt portion of the driver + * (also known as the "bottom half"). This can be called any + * number of times for any channel without harm. + */ +static inline void +ifx_ssc_sched_event (struct ifx_ssc_port *info, int event) +{ + info->event |= 1 << event; /* remember what kind of event and who */ + queue_task (&info->tqueue, &tq_cyclades); /* it belongs to */ + mark_bh (CYCLADES_BH); /* then trigger event */ +} /* ifx_ssc_sched_event */ + +/* + * This routine is used to handle the "bottom half" processing for the + * serial driver, known also the "software interrupt" processing. + * This processing is done at the kernel interrupt level, after the + * cy#/_interrupt() has returned, BUT WITH INTERRUPTS TURNED ON. This + * is where time-consuming activities which can not be done in the + * interrupt driver proper are done; the interrupt driver schedules + * them using ifx_ssc_sched_event(), and they get done here. + * + * This is done through one level of indirection--the task queue. + * When a hardware interrupt service routine wants service by the + * driver's bottom half, it enqueues the appropriate tq_struct (one + * per port) to the tq_cyclades work queue and sets a request flag + * via mark_bh for processing that queue. When the time is right, + * do_ifx_ssc_bh is called (because of the mark_bh) and it requests + * that the work queue be processed. + * + * Although this may seem unwieldy, it gives the system a way to + * pass an argument (in this case the pointer to the ifx_ssc_port + * structure) to the bottom half of the driver. Previous kernels + * had to poll every port to see if that port needed servicing. + */ +static void +do_ifx_ssc_bh (void) +{ + run_task_queue (&tq_cyclades); +} /* do_ifx_ssc_bh */ + +static void +do_softint (void *private_) +{ + struct ifx_ssc_port *info = (struct ifx_ssc_port *) private_; + + if (test_and_clear_bit (Cy_EVENT_HANGUP, &info->event)) { + wake_up_interruptible (&info->open_wait); + info->flags &= ~(ASYNC_NORMAL_ACTIVE | ASYNC_CALLOUT_ACTIVE); + } + if (test_and_clear_bit (Cy_EVENT_OPEN_WAKEUP, &info->event)) { + wake_up_interruptible (&info->open_wait); + } + if (test_and_clear_bit (Cy_EVENT_DELTA_WAKEUP, &info->event)) { + wake_up_interruptible (&info->delta_msr_wait); + } + if (test_and_clear_bit (Cy_EVENT_WRITE_WAKEUP, &info->event)) { + wake_up_interruptible (&tty->write_wait); + } +#ifdef Z_WAKE + if (test_and_clear_bit (Cy_EVENT_SHUTDOWN_WAKEUP, &info->event)) { + wake_up_interruptible (&info->shutdown_wait); + } +#endif +} /* do_softint */ +#endif /* IFX_SSC_INT_USE_BH */ +#endif // not_yet + +inline static void +rx_int (struct ifx_ssc_port *info) +{ + int fifo_fill_lev, bytes_in_buf, i; + unsigned long tmp_val; + unsigned long *tmp_ptr; + unsigned int rx_valid_cnt; + /* number of words waiting in the RX FIFO */ + fifo_fill_lev = (READ_PERIPHERAL_REGISTER (info->mapbase + + IFX_SSC_FSTAT) & + IFX_SSC_FSTAT_RECEIVED_WORDS_MASK) >> + IFX_SSC_FSTAT_RECEIVED_WORDS_OFFSET; + // Note: There are always 32 bits in a fifo-entry except for the last + // word of a contigous transfer block and except for not in rx-only + // mode and CON.ENBV set. But for this case it should be a convention + // in software which helps: + // In tx or rx/tx mode all transfers from the buffer to the FIFO are + // 32-bit wide, except for the last three bytes, which could be a + // combination of 16- and 8-bit access. + // => The whole block is received as 32-bit words as a contigous stream, + // even if there was a gap in tx which has the fifo run out of data! + // Just the last fifo entry *may* be partially filled (0, 1, 2 or 3 bytes)! + + /* free space in the RX buffer */ + bytes_in_buf = info->rxbuf_end - info->rxbuf_ptr; + // transfer with 32 bits per entry + while ((bytes_in_buf >= 4) && (fifo_fill_lev > 0)) { + tmp_ptr = (unsigned long *) info->rxbuf_ptr; + *tmp_ptr = + READ_PERIPHERAL_REGISTER (info->mapbase + IFX_SSC_RB); + info->rxbuf_ptr += 4; + info->stats.rxBytes += 4; + fifo_fill_lev--; + bytes_in_buf -= 4; + } // while ((bytes_in_buf >= 4) && (fifo_fill_lev > 0)) + // now do the rest as mentioned in STATE.RXBV + while ((bytes_in_buf > 0) && (fifo_fill_lev > 0)) { + rx_valid_cnt = + (READ_PERIPHERAL_REGISTER + (info->mapbase + + IFX_SSC_STATE) & IFX_SSC_STATE_RX_BYTE_VALID_MASK) + >> IFX_SSC_STATE_RX_BYTE_VALID_OFFSET; + if (rx_valid_cnt == 0) + break; + if (rx_valid_cnt > bytes_in_buf) { + // ### TO DO: warning message: not block aligned data, other data + // in this entry will be lost + rx_valid_cnt = bytes_in_buf; + } + tmp_val = + READ_PERIPHERAL_REGISTER (info->mapbase + IFX_SSC_RB); + + for (i = 0; i < rx_valid_cnt; i++) { + *info->rxbuf_ptr = + (tmp_val >> (8 * (rx_valid_cnt - i - 1))) & + 0xff; +/* + *info->rxbuf_ptr = tmp_val & 0xff; + tmp_val >>= 8; +*/ + bytes_in_buf--; + + info->rxbuf_ptr++; + } + info->stats.rxBytes += rx_valid_cnt; + } // while ((bytes_in_buf > 0) && (fifo_fill_lev > 0)) + + // check if transfer is complete + if (info->rxbuf_ptr >= info->rxbuf_end) { + ifx_int_wrapper.disable (info->rxirq); + /* wakeup any processes waiting in read() */ + wake_up_interruptible (&info->rwait); + /* and in poll() */ + //wake_up_interruptible(&info->pwait); + } + else if ((info->opts.modeRxTx == IFX_SSC_MODE_RX) && + (READ_PERIPHERAL_REGISTER (info->mapbase + IFX_SSC_RXCNT) == + 0)) { + // if buffer not filled completely and rx request done initiate new transfer +/* + if (info->rxbuf_end - info->rxbuf_ptr < 65536) +*/ + if (info->rxbuf_end - info->rxbuf_ptr < + IFX_SSC_RXREQ_BLOCK_SIZE) + WRITE_PERIPHERAL_REGISTER ((info->rxbuf_end - + info-> + rxbuf_ptr) << + IFX_SSC_RXREQ_RXCOUNT_OFFSET, + info->mapbase + + IFX_SSC_RXREQ); + else + WRITE_PERIPHERAL_REGISTER (IFX_SSC_RXREQ_BLOCK_SIZE << + IFX_SSC_RXREQ_RXCOUNT_OFFSET, + info->mapbase + + IFX_SSC_RXREQ); + } +} // rx_int + +inline static void +tx_int (struct ifx_ssc_port *info) +{ + + int fifo_space, fill, i; + fifo_space = ((READ_PERIPHERAL_REGISTER (info->mapbase + IFX_SSC_ID) & + IFX_SSC_PERID_TXFS_MASK) >> IFX_SSC_PERID_TXFS_OFFSET) + - + ((READ_PERIPHERAL_REGISTER (info->mapbase + IFX_SSC_FSTAT) & + IFX_SSC_FSTAT_TRANSMIT_WORDS_MASK) >> + IFX_SSC_FSTAT_TRANSMIT_WORDS_OFFSET); + + if (fifo_space == 0) + return; + + fill = info->txbuf_end - info->txbuf_ptr; + + if (fill > fifo_space * 4) + fill = fifo_space * 4; + + for (i = 0; i < fill / 4; i++) { + // at first 32 bit access + WRITE_PERIPHERAL_REGISTER (*(UINT32 *) info->txbuf_ptr, + info->mapbase + IFX_SSC_TB); + info->txbuf_ptr += 4; + } + + fifo_space -= fill / 4; + info->stats.txBytes += fill & ~0x3; + fill &= 0x3; + if ((fifo_space > 0) & (fill > 1)) { + // trailing 16 bit access + WRITE_PERIPHERAL_REGISTER_16 (*(UINT16 *) info->txbuf_ptr, + info->mapbase + IFX_SSC_TB); + info->txbuf_ptr += 2; + info->stats.txBytes += 2; + fifo_space--; +/* added by bingtao */ + fill -= 2; + } + if ((fifo_space > 0) & (fill > 0)) { + // trailing 8 bit access + WRITE_PERIPHERAL_REGISTER_8 (*(UINT8 *) info->txbuf_ptr, + info->mapbase + IFX_SSC_TB); + info->txbuf_ptr++; + info->stats.txBytes++; +/* + fifo_space --; +*/ + } + + // check if transmission complete + if (info->txbuf_ptr >= info->txbuf_end) { + ifx_int_wrapper.disable (info->txirq); + kfree (info->txbuf); + info->txbuf = NULL; + /* wake up any process waiting in poll() */ + //wake_up_interruptible(&info->pwait); + } + +} // tx_int + +static void +ifx_ssc_rx_int (int irq, void *dev_id, struct pt_regs *regs) +{ + struct ifx_ssc_port *info = (struct ifx_ssc_port *) dev_id; + //WRITE_PERIPHERAL_REGISTER(IFX_SSC_R_BIT, info->mapbase + IFX_SSC_IRN_CR); + rx_int (info); +} + +static void +ifx_ssc_tx_int (int irq, void *dev_id, struct pt_regs *regs) +{ + struct ifx_ssc_port *info = (struct ifx_ssc_port *) dev_id; + //WRITE_PERIPHERAL_REGISTER(IFX_SSC_T_BIT, info->mapbase + IFX_SSC_IRN_CR); + tx_int (info); +} + +static void +ifx_ssc_err_int (int irq, void *dev_id, struct pt_regs *regs) +{ + struct ifx_ssc_port *info = (struct ifx_ssc_port *) dev_id; + unsigned int state; + unsigned int write_back = 0; + unsigned long flags; + + local_irq_save (flags); + state = READ_PERIPHERAL_REGISTER (info->mapbase + IFX_SSC_STATE); + + if ((state & IFX_SSC_STATE_RX_UFL) != 0) { + info->stats.rxUnErr++; + write_back |= IFX_SSC_WHBSTATE_CLR_RX_UFL_ERROR; + } + if ((state & IFX_SSC_STATE_RX_OFL) != 0) { + info->stats.rxOvErr++; + write_back |= IFX_SSC_WHBSTATE_CLR_RX_OFL_ERROR; + } + if ((state & IFX_SSC_STATE_TX_OFL) != 0) { + info->stats.txOvErr++; + write_back |= IFX_SSC_WHBSTATE_CLR_TX_OFL_ERROR; + } + if ((state & IFX_SSC_STATE_TX_UFL) != 0) { + info->stats.txUnErr++; + write_back |= IFX_SSC_WHBSTATE_CLR_TX_UFL_ERROR; + } +// if ((state & IFX_SSC_STATE_ABORT_ERR) != 0) { +// info->stats.abortErr++; +// write_back |= IFX_SSC_WHBSTATE_CLR_ABORT_ERROR; +// } + if ((state & IFX_SSC_STATE_MODE_ERR) != 0) { + info->stats.modeErr++; + write_back |= IFX_SSC_WHBSTATE_CLR_MODE_ERROR; + } + + if (write_back) + WRITE_PERIPHERAL_REGISTER (write_back, + info->mapbase + IFX_SSC_WHBSTATE); + + local_irq_restore (flags); +} + +#ifdef SSC_FRAME_INT_ENABLE +static void +ifx_ssc_frm_int (int irq, void *dev_id, struct pt_regs *regs) +{ + // ### TO DO: wake up framing wait-queue in conjunction with batch execution +} +#endif + +static void +ifx_ssc_abort (struct ifx_ssc_port *info) +{ + unsigned long flags; + bool enabled; + + local_irq_save (flags); + + // disable all int's + ifx_int_wrapper.disable (info->rxirq); + ifx_int_wrapper.disable (info->txirq); + ifx_int_wrapper.disable (info->errirq); +/* + ifx_int_wrapper.disable(info->frmirq); +*/ + local_irq_restore (flags); + + // disable SSC (also aborts a receive request!) + // ### TO DO: Perhaps it's better to abort after the receiption of a + // complete word. The disable cuts the transmission immediatly and + // releases the chip selects. This could result in unpredictable + // behavior of connected external devices! + enabled = (READ_PERIPHERAL_REGISTER (info->mapbase + IFX_SSC_STATE) + & IFX_SSC_STATE_IS_ENABLED) != 0; + WRITE_PERIPHERAL_REGISTER (IFX_SSC_WHBSTATE_CLR_ENABLE, + info->mapbase + IFX_SSC_WHBSTATE); + + // flush fifos + WRITE_PERIPHERAL_REGISTER (IFX_SSC_XFCON_FIFO_FLUSH, + info->mapbase + IFX_SSC_TXFCON); + WRITE_PERIPHERAL_REGISTER (IFX_SSC_XFCON_FIFO_FLUSH, + info->mapbase + IFX_SSC_RXFCON); + + // free txbuf + if (info->txbuf != NULL) { + kfree (info->txbuf); + info->txbuf = NULL; + } + + // wakeup read process + if (info->rxbuf != NULL) + wake_up_interruptible (&info->rwait); + + // clear pending int's + ifx_int_wrapper.clear (info->rxirq); + ifx_int_wrapper.clear (info->txirq); + ifx_int_wrapper.clear (info->errirq); +/* + ifx_int_wrapper.clear(info->frmirq); +*/ + + // clear error flags + WRITE_PERIPHERAL_REGISTER (IFX_SSC_WHBSTATE_CLR_ALL_ERROR, + info->mapbase + IFX_SSC_WHBSTATE); + + //printk("IFX SSC%d: Transmission aborted\n", info->port_nr); + // enable SSC + if (enabled) + WRITE_PERIPHERAL_REGISTER (IFX_SSC_WHBSTATE_SET_ENABLE, + info->mapbase + IFX_SSC_WHBSTATE); + +} // ifx_ssc_abort + +/* + * This routine is called whenever a port is opened. It enforces + * exclusive opening of a port and enables interrupts, etc. + */ +int +ifx_ssc_open (struct inode *inode, struct file *filp) +{ + struct ifx_ssc_port *info; + int line; + int from_kernel = 0; + + if ((inode == (struct inode *) 0) || (inode == (struct inode *) 1)) { + from_kernel = 1; + line = (int) inode; + } + else { + line = MINOR (filp->f_dentry->d_inode->i_rdev); + filp->f_op = &ifx_ssc_fops; + } + + /* don't open more minor devices than we can support */ + if (line < 0 || line >= PORT_CNT) + return -ENXIO; + + info = &isp[line]; + + /* exclusive open */ + if (info->port_is_open != 0) + return -EBUSY; + info->port_is_open++; + + ifx_int_wrapper.disable (info->rxirq); + ifx_int_wrapper.disable (info->txirq); + ifx_int_wrapper.disable (info->errirq); +/* + ifx_int_wrapper.disable(info->frmirq); +*/ + + /* Flush and enable TX/RX FIFO */ + WRITE_PERIPHERAL_REGISTER ((IFX_SSC_DEF_TXFIFO_FL << + IFX_SSC_XFCON_ITL_OFFSET) | + IFX_SSC_XFCON_FIFO_FLUSH | + IFX_SSC_XFCON_FIFO_ENABLE, + info->mapbase + IFX_SSC_TXFCON); + WRITE_PERIPHERAL_REGISTER ((IFX_SSC_DEF_RXFIFO_FL << + IFX_SSC_XFCON_ITL_OFFSET) | + IFX_SSC_XFCON_FIFO_FLUSH | + IFX_SSC_XFCON_FIFO_ENABLE, + info->mapbase + IFX_SSC_RXFCON); + + /* logically flush the software FIFOs */ + info->rxbuf_ptr = 0; + info->txbuf_ptr = 0; + + /* clear all error bits */ + WRITE_PERIPHERAL_REGISTER (IFX_SSC_WHBSTATE_CLR_ALL_ERROR, + info->mapbase + IFX_SSC_WHBSTATE); + + // clear pending interrupts + ifx_int_wrapper.clear (info->rxirq); + ifx_int_wrapper.clear (info->txirq); + ifx_int_wrapper.clear (info->errirq); +/* + ifx_int_wrapper.clear(info->frmirq); +*/ + + // enable SSC + WRITE_PERIPHERAL_REGISTER (IFX_SSC_WHBSTATE_SET_ENABLE, + info->mapbase + IFX_SSC_WHBSTATE); + + return 0; +} /* ifx_ssc_open */ + +EXPORT_SYMBOL (ifx_ssc_open); + +/* + * This routine is called when a particular device is closed. + */ +int +ifx_ssc_close (struct inode *inode, struct file *filp) +{ + struct ifx_ssc_port *info; + int idx; + + if ((inode == (struct inode *) 0) || (inode == (struct inode *) 1)) + idx = (int) inode; + else + idx = MINOR (filp->f_dentry->d_inode->i_rdev); + + if (idx < 0 || idx >= PORT_CNT) + return -ENXIO; + + info = &isp[idx]; + if (!info) + return -ENXIO; + + // disable SSC + WRITE_PERIPHERAL_REGISTER (IFX_SSC_WHBSTATE_CLR_ENABLE, + info->mapbase + IFX_SSC_WHBSTATE); + + // call abort function to disable int's, flush fifos... + ifx_ssc_abort (info); + + info->port_is_open--; + + return 0; +} /* ifx_ssc_close */ + +EXPORT_SYMBOL (ifx_ssc_close); + +/* added by bingtao */ +/* helper routine to handle reads from the kernel or user-space */ +/* info->rxbuf : never kfree and contains valid data */ +/* should be points to NULL after copying data !!! */ +static ssize_t +ifx_ssc_read_helper_poll (struct ifx_ssc_port *info, char *buf, size_t len, + int from_kernel) +{ + ssize_t ret_val; + unsigned long flags; + + if (info->opts.modeRxTx == IFX_SSC_MODE_TX) + return -EFAULT; + local_irq_save (flags); + info->rxbuf_ptr = info->rxbuf; + info->rxbuf_end = info->rxbuf + len; + local_irq_restore (flags); + /* Vinetic driver always works in IFX_SSC_MODE_RXTX */ + /* TXRX in poll mode */ + while (info->rxbuf_ptr < info->rxbuf_end) { + /* This is the key point, if you don't check this condition + kfree (NULL) will happen + because tx only need write into FIFO, it's much fast than rx + So when rx still waiting , tx already finish and release buf + */ + if (info->txbuf_ptr < info->txbuf_end) { + tx_int (info); + } + + rx_int (info); + }; + + ret_val = info->rxbuf_ptr - info->rxbuf; + return (ret_val); +} // ifx_ssc_read_helper_poll + +/* helper routine to handle reads from the kernel or user-space */ +/* info->rx_buf : never kfree and contains valid data */ +/* should be points to NULL after copying data !!! */ +static ssize_t +ifx_ssc_read_helper (struct ifx_ssc_port *info, char *buf, size_t len, + int from_kernel) +{ + ssize_t ret_val; + unsigned long flags; + DECLARE_WAITQUEUE (wait, current); + + if (info->opts.modeRxTx == IFX_SSC_MODE_TX) + return -EFAULT; + local_irq_save (flags); + info->rxbuf_ptr = info->rxbuf; + info->rxbuf_end = info->rxbuf + len; + if (info->opts.modeRxTx == IFX_SSC_MODE_RXTX) { + if ((info->txbuf == NULL) || + (info->txbuf != info->txbuf_ptr) || + (info->txbuf_end != len + info->txbuf)) { + local_irq_restore (flags); + printk ("IFX SSC - %s: write must be called before calling " "read in combined RX/TX!\n", __FUNCTION__); + return -EFAULT; + } + local_irq_restore (flags); + /* should enable tx, right? */ + tx_int (info); + if (info->txbuf_ptr < info->txbuf_end) { + ifx_int_wrapper.enable (info->txirq); + } + + ifx_int_wrapper.enable (info->rxirq); + } + else { // rx mode + local_irq_restore (flags); + if (READ_PERIPHERAL_REGISTER (info->mapbase + IFX_SSC_RXCNT) & + IFX_SSC_RXCNT_TODO_MASK) + return -EBUSY; + ifx_int_wrapper.enable (info->rxirq); + // rx request limited to ' bytes +/* + if (len < 65536) +*/ + if (len < IFX_SSC_RXREQ_BLOCK_SIZE) + WRITE_PERIPHERAL_REGISTER (len << + IFX_SSC_RXREQ_RXCOUNT_OFFSET, + info->mapbase + + IFX_SSC_RXREQ); + else + WRITE_PERIPHERAL_REGISTER (IFX_SSC_RXREQ_BLOCK_SIZE << + IFX_SSC_RXREQ_RXCOUNT_OFFSET, + info->mapbase + + IFX_SSC_RXREQ); + } + + __add_wait_queue (&info->rwait, &wait); + set_current_state (TASK_INTERRUPTIBLE); + // wakeup done in rx_int + + do { + local_irq_save (flags); + if (info->rxbuf_ptr >= info->rxbuf_end) + break; + local_irq_restore (flags); + +// if (filp->f_flags & O_NONBLOCK) +// { +// N = -EAGAIN; +// goto out; +// } + if (signal_pending (current)) { + ret_val = -ERESTARTSYS; + goto out; + } + schedule (); + } while (1); + + ret_val = info->rxbuf_ptr - info->rxbuf; // should be equal to len + local_irq_restore (flags); + + out: + current->state = TASK_RUNNING; + __remove_wait_queue (&info->rwait, &wait); + return (ret_val); +} // ifx_ssc_read_helper + +#if 0 +/* helper routine to handle reads from the kernel or user-space */ +/* appropriate in interrupt context */ +static ssize_t +ifx_ssc_read_helper (struct ifx_ssc_port *info, char *buf, size_t len, + int from_kernel) +{ + ssize_t ret_val; + unsigned long flags; + DECLARE_WAITQUEUE (wait, current); + + if (info->opts.modeRxTx == IFX_SSC_MODE_TX) + return -EFAULT; + local_irq_save (flags); + info->rxbuf_ptr = info->rxbuf; + info->rxbuf_end = info->rxbuf + len; + if (info->opts.modeRxTx == IFX_SSC_MODE_RXTX) { + if ((info->txbuf == NULL) || + (info->txbuf != info->txbuf_ptr) || + (info->txbuf_end != len + info->txbuf)) { + local_irq_restore (flags); + printk ("IFX SSC - %s: write must be called before calling " "read in combined RX/TX!\n", __FUNCTION__); + return -EFAULT; + } + local_irq_restore (flags); + /* should enable tx, right? */ + tx_int (info); + if (!in_irq ()) { + if (info->txbuf_ptr < info->txbuf_end) { + ifx_int_wrapper.enable (info->txirq); + } + ifx_int_wrapper.enable (info->rxirq); + } + } + else { // rx mode + local_irq_restore (flags); + if (READ_PERIPHERAL_REGISTER (info->mapbase + IFX_SSC_RXCNT) & + IFX_SSC_RXCNT_TODO_MASK) + return -EBUSY; + if (!in_irq ()) { + ifx_int_wrapper.enable (info->rxirq); + } + + if (len < IFX_SSC_RXREQ_BLOCK_SIZE) + WRITE_PERIPHERAL_REGISTER (len << + IFX_SSC_RXREQ_RXCOUNT_OFFSET, + info->mapbase + + IFX_SSC_RXREQ); + else + WRITE_PERIPHERAL_REGISTER (IFX_SSC_RXREQ_BLOCK_SIZE << + IFX_SSC_RXREQ_RXCOUNT_OFFSET, + info->mapbase + + IFX_SSC_RXREQ); + } + if (in_irq ()) { + do { + rx_int (info); + if (info->opts.modeRxTx == IFX_SSC_MODE_RXTX) { + tx_int (info); + } + + if (info->rxbuf_ptr >= info->rxbuf_end) + break; + } while (1); + ret_val = info->rxbuf_ptr - info->rxbuf; + } + else { + __add_wait_queue (&info->rwait, &wait); + set_current_state (TASK_INTERRUPTIBLE); + // wakeup done in rx_int + + do { + local_irq_save (flags); + if (info->rxbuf_ptr >= info->rxbuf_end) + break; + local_irq_restore (flags); + + if (signal_pending (current)) { + ret_val = -ERESTARTSYS; + goto out; + } + schedule (); + } while (1); + + ret_val = info->rxbuf_ptr - info->rxbuf; // should be equal to len + local_irq_restore (flags); + + out: + current->state = TASK_RUNNING; + __remove_wait_queue (&info->rwait, &wait); + } + return (ret_val); +} // ifx_ssc_read_helper +#endif + +/* helper routine to handle writes to the kernel or user-space */ +/* info->txbuf has two cases: + * 1) return value < 0 (-EFAULT), not touched at all + * 2) kfree and points to NULL in interrupt routine (but maybe later ) + */ +static ssize_t +ifx_ssc_write_helper (struct ifx_ssc_port *info, const char *buf, + size_t len, int from_kernel) +{ + // check if in tx or tx/rx mode + if (info->opts.modeRxTx == IFX_SSC_MODE_RX) + return -EFAULT; + + info->txbuf_ptr = info->txbuf; + info->txbuf_end = len + info->txbuf; + /* start the transmission (not in rx/tx, see read helper) */ + if (info->opts.modeRxTx == IFX_SSC_MODE_TX) { + tx_int (info); + if (info->txbuf_ptr < info->txbuf_end) { + ifx_int_wrapper.enable (info->txirq); + } + } + //local_irq_restore(flags); + return len; +} + +/* + * kernel interfaces for read and write. + * The caller must set port to: n for SSC with n=m-1 (e.g. n=0 for SSC1) + */ +ssize_t +ifx_ssc_kread (int port, char *kbuf, size_t len) +{ + struct ifx_ssc_port *info; + ssize_t ret_val; + + if (port < 0 || port >= PORT_CNT) + return -ENXIO; + + if (len == 0) + return 0; + + info = &isp[port]; + + // check if reception in progress + if (info->rxbuf != NULL) { + printk ("SSC device busy\n"); + return -EBUSY; + } + + info->rxbuf = kbuf; + if (info->rxbuf == NULL) { + printk ("SSC device error\n"); + return -EINVAL; + } + +/* changed by bingtao */ + /* change by TaiCheng */ + //if (!in_irq()){ + if (0) { + ret_val = ifx_ssc_read_helper (info, kbuf, len, 1); + } + else { + ret_val = ifx_ssc_read_helper_poll (info, kbuf, len, 1); + }; + info->rxbuf = NULL; + + // ### TO DO: perhaps warn if ret_val != len + ifx_int_wrapper.disable (info->rxirq); + + return (ret_val); +} // ifx_ssc_kread + +EXPORT_SYMBOL (ifx_ssc_kread); + +ssize_t +ifx_ssc_kwrite (int port, const char *kbuf, size_t len) +{ + struct ifx_ssc_port *info; + ssize_t ret_val; + + if (port < 0 || port >= PORT_CNT) + return -ENXIO; + + if (len == 0) + return 0; + + info = &isp[port]; + + // check if transmission in progress + if (info->txbuf != NULL) + return -EBUSY; + info->txbuf = (char *) kbuf; + + ret_val = ifx_ssc_write_helper (info, info->txbuf, len, 1); + if (ret_val < 0) { + info->txbuf = NULL; + } + return ret_val; +} + +EXPORT_SYMBOL (ifx_ssc_kwrite); + +/* + * user interfaces to read and write + */ +static ssize_t +ifx_ssc_read (struct file *filp, char *ubuf, size_t len, loff_t * off) +{ + ssize_t ret_val; + int idx; + struct ifx_ssc_port *info; + +/* + if (len == 0) + return (0); +*/ + idx = MINOR (filp->f_dentry->d_inode->i_rdev); + info = &isp[idx]; + + // check if reception in progress + if (info->rxbuf != NULL) + return -EBUSY; + + info->rxbuf = kmalloc (len + 3, GFP_KERNEL); + if (info->rxbuf == NULL) + return -ENOMEM; + + ret_val = ifx_ssc_read_helper (info, info->rxbuf, len, 0); + // ### TO DO: perhaps warn if ret_val != len + if (copy_to_user ((void *) ubuf, info->rxbuf, ret_val) != 0) + ret_val = -EFAULT; + + ifx_int_wrapper.disable (info->rxirq); + + kfree (info->rxbuf); + info->rxbuf = NULL; + return (ret_val); +} // ifx_ssc_read + +/* + * As many bytes as we have free space for are copied from the user + * into txbuf and the actual byte count is returned. The transmission is + * always kicked off by calling the appropriate TX routine. + */ +static ssize_t +ifx_ssc_write (struct file *filp, const char *ubuf, size_t len, loff_t * off) +{ + int idx; + struct ifx_ssc_port *info; + int ret_val; + + if (len == 0) + return (0); + + idx = MINOR (filp->f_dentry->d_inode->i_rdev); + info = &isp[idx]; + + // check if transmission in progress + if (info->txbuf != NULL) + return -EBUSY; + + info->txbuf = kmalloc (len + 3, GFP_KERNEL); + if (info->txbuf == NULL) + return -ENOMEM; + + ret_val = copy_from_user (info->txbuf, ubuf, len); + if (ret_val == 0) + ret_val = ifx_ssc_write_helper (info, info->txbuf, len, 0); + else + ret_val = -EFAULT; + if (ret_val < 0) { + kfree (info->txbuf); // otherwise will be done in ISR + info->txbuf = NULL; + } + return (ret_val); +} /* ifx_ssc_write */ + +/* + * ------------------------------------------------------------ + * ifx_ssc_ioctl() and friends + * ------------------------------------------------------------ + */ + +/*----------------------------------------------------------------------------- + FUNC-NAME : ifx_ssc_frm_status_get + LONG-NAME : framing status get + PURPOSE : Get the actual status of the framing. + + PARAMETER : *info pointer to the port-specific structure ifx_ssc_port. + + RESULT : pointer to a structure ifx_ssc_frm_status which holds busy and + count values. + + REMARKS : Returns a register value independent of framing is enabled or + not! Changes structure inside of info, so the return value isn't + needed at all, but could be used for simple access. +-----------------------------------------------------------------------------*/ +static struct ifx_ssc_frm_status * +ifx_ssc_frm_status_get (struct ifx_ssc_port *info) +{ + unsigned long tmp; + + tmp = READ_PERIPHERAL_REGISTER (info->mapbase + IFX_SSC_SFSTAT); + info->frm_status.DataBusy = (tmp & IFX_SSC_SFSTAT_IN_DATA) > 0; + info->frm_status.PauseBusy = (tmp & IFX_SSC_SFSTAT_IN_PAUSE) > 0; + info->frm_status.DataCount = (tmp & IFX_SSC_SFSTAT_DATA_COUNT_MASK) + >> IFX_SSC_SFSTAT_DATA_COUNT_OFFSET; + info->frm_status.PauseCount = (tmp & IFX_SSC_SFSTAT_PAUSE_COUNT_MASK) + >> IFX_SSC_SFSTAT_PAUSE_COUNT_OFFSET; + tmp = READ_PERIPHERAL_REGISTER (info->mapbase + IFX_SSC_SFCON); + info->frm_status.EnIntAfterData = + (tmp & IFX_SSC_SFCON_FIR_ENABLE_BEFORE_PAUSE) > 0; + info->frm_status.EnIntAfterPause = + (tmp & IFX_SSC_SFCON_FIR_ENABLE_AFTER_PAUSE) > 0; + return (&info->frm_status); +} // ifx_ssc_frm_status_get + +/*----------------------------------------------------------------------------- + FUNC-NAME : ifx_ssc_frm_control_get + LONG-NAME : framing control get + PURPOSE : Get the actual control values of the framing. + + PARAMETER : *info pointer to the port-specific structure ifx_ssc_port. + + RESULT : pointer to a structure ifx_ssc_frm_opts which holds control bits + and count reload values. + + REMARKS : Changes structure inside of info, so the return value isn't + needed at all, but could be used for simple access. +-----------------------------------------------------------------------------*/ +static struct ifx_ssc_frm_opts * +ifx_ssc_frm_control_get (struct ifx_ssc_port *info) +{ + unsigned long tmp; + + tmp = READ_PERIPHERAL_REGISTER (info->mapbase + IFX_SSC_SFCON); + info->frm_opts.FrameEnable = (tmp & IFX_SSC_SFCON_SF_ENABLE) > 0; + info->frm_opts.DataLength = (tmp & IFX_SSC_SFCON_DATA_LENGTH_MASK) + >> IFX_SSC_SFCON_DATA_LENGTH_OFFSET; + info->frm_opts.PauseLength = (tmp & IFX_SSC_SFCON_PAUSE_LENGTH_MASK) + >> IFX_SSC_SFCON_PAUSE_LENGTH_OFFSET; + info->frm_opts.IdleData = (tmp & IFX_SSC_SFCON_PAUSE_DATA_MASK) + >> IFX_SSC_SFCON_PAUSE_DATA_OFFSET; + info->frm_opts.IdleClock = (tmp & IFX_SSC_SFCON_PAUSE_CLOCK_MASK) + >> IFX_SSC_SFCON_PAUSE_CLOCK_OFFSET; + info->frm_opts.StopAfterPause = + (tmp & IFX_SSC_SFCON_STOP_AFTER_PAUSE) > 0; + return (&info->frm_opts); +} // ifx_ssc_frm_control_get + +/*----------------------------------------------------------------------------- + FUNC-NAME : ifx_ssc_frm_control_set + LONG-NAME : framing control set + PURPOSE : Set the actual control values of the framing. + + PARAMETER : *info pointer to the port-specific structure ifx_ssc_port. + + RESULT : pointer to a structure ifx_ssc_frm_opts which holds control bits + and count reload values. + + REMARKS : +-----------------------------------------------------------------------------*/ +static int +ifx_ssc_frm_control_set (struct ifx_ssc_port *info) +{ + unsigned long tmp; + + // check parameters + if ((info->frm_opts.DataLength > IFX_SSC_SFCON_DATA_LENGTH_MAX) || + (info->frm_opts.DataLength < 1) || + (info->frm_opts.PauseLength > IFX_SSC_SFCON_PAUSE_LENGTH_MAX) || + (info->frm_opts.PauseLength < 1) || + ((info->frm_opts.IdleData & ~(IFX_SSC_SFCON_PAUSE_DATA_MASK >> + IFX_SSC_SFCON_PAUSE_DATA_OFFSET)) != + 0) + || + ((info->frm_opts. + IdleClock & ~(IFX_SSC_SFCON_PAUSE_CLOCK_MASK >> + IFX_SSC_SFCON_PAUSE_CLOCK_OFFSET)) != 0)) + return -EINVAL; + + // read interrupt bits (they're not changed here) + tmp = READ_PERIPHERAL_REGISTER (info->mapbase + IFX_SSC_SFCON) & + (IFX_SSC_SFCON_FIR_ENABLE_BEFORE_PAUSE | + IFX_SSC_SFCON_FIR_ENABLE_AFTER_PAUSE); + + // set all values with respect to it's bit position (for data and pause + // length set N-1) + tmp = (info->frm_opts.DataLength - + 1) << IFX_SSC_SFCON_DATA_LENGTH_OFFSET; + tmp |= (info->frm_opts.PauseLength - + 1) << IFX_SSC_SFCON_PAUSE_LENGTH_OFFSET; + tmp |= info->frm_opts.IdleData << IFX_SSC_SFCON_PAUSE_DATA_OFFSET; + tmp |= info->frm_opts.IdleClock << IFX_SSC_SFCON_PAUSE_CLOCK_OFFSET; + tmp |= info->frm_opts.FrameEnable * IFX_SSC_SFCON_SF_ENABLE; + tmp |= info->frm_opts.StopAfterPause * IFX_SSC_SFCON_STOP_AFTER_PAUSE; + + WRITE_PERIPHERAL_REGISTER (tmp, info->mapbase + IFX_SSC_SFCON); + + return 0; +} // ifx_ssc_frm_control_set + +/*----------------------------------------------------------------------------- + FUNC-NAME : ifx_ssc_rxtx_mode_set + LONG-NAME : rxtx mode set + PURPOSE : Set the transmission mode. + + PARAMETER : *info pointer to the port-specific structure ifx_ssc_port. + + RESULT : Returns error code + + REMARKS : Assumes that SSC not used (SSC disabled, device not opened yet + or just closed) +-----------------------------------------------------------------------------*/ +static int +ifx_ssc_rxtx_mode_set (struct ifx_ssc_port *info, unsigned int val) +{ + unsigned long tmp; + + // check parameters + if (!(info) || (val & ~(IFX_SSC_MODE_MASK))) + return -EINVAL; + /*check BUSY and RXCNT */ + if (READ_PERIPHERAL_REGISTER (info->mapbase + IFX_SSC_STATE) & + IFX_SSC_STATE_BUSY + || READ_PERIPHERAL_REGISTER (info->mapbase + + IFX_SSC_RXCNT) & + IFX_SSC_RXCNT_TODO_MASK) + return -EBUSY; + // modify + tmp = (READ_PERIPHERAL_REGISTER (info->mapbase + IFX_SSC_CON) & + ~(IFX_SSC_CON_RX_OFF | IFX_SSC_CON_TX_OFF)) | (val); + WRITE_PERIPHERAL_REGISTER (tmp, info->mapbase + IFX_SSC_CON); + info->opts.modeRxTx = val; +/* + printk(KERN_DEBUG "IFX SSC%d: Setting mode to %s%s\n", + info->port_nr, + ((val & IFX_SSC_CON_RX_OFF) == 0) ? "rx ":"", + ((val & IFX_SSC_CON_TX_OFF) == 0) ? "tx":""); +*/ + return 0; +} // ifx_ssc_rxtx_mode_set + +void +ifx_gpio_init (void) +{ + /* TODO: P0.9 SPI_CS4, P0.10 SPI_CS5, P 0.11 SPI_CS6, because of ASC0 */ + /* p0.15 SPI_CS1(EEPROM), P0.13 SPI_CS3, */ + /* Set p0.15 to alternative 01, others to 00 (In/OUT) */ + *(DANUBE_GPIO_P0_DIR) = (*DANUBE_GPIO_P0_DIR) | (0xA000); + *(DANUBE_GPIO_P0_ALTSEL0) = + (((*DANUBE_GPIO_P0_ALTSEL0) | (0x8000)) & (~(0x2000))); + *(DANUBE_GPIO_P0_ALTSEL1) = + (((*DANUBE_GPIO_P0_ALTSEL1) & (~0x8000)) & (~(0x2000))); + *(DANUBE_GPIO_P0_OD) = (*DANUBE_GPIO_P0_OD) | 0xA000; + + /* p1.6 SPI_CS2(SFLASH), p1.0 SPI_DIN, p1.1 SPI_DOUT, p1.2 SPI_CLK */ + *(DANUBE_GPIO_P1_DIR) = ((*DANUBE_GPIO_P1_DIR) | (0x46)) & (~1); + *(DANUBE_GPIO_P1_ALTSEL0) = ((*DANUBE_GPIO_P1_ALTSEL0) | (0x47)); + *(DANUBE_GPIO_P1_ALTSEL1) = (*DANUBE_GPIO_P1_ALTSEL1) & (~0x47); + *(DANUBE_GPIO_P1_OD) = (*DANUBE_GPIO_P1_OD) | 0x0046; + + /*CS3 */ + /*TODO: CS4 CS5 CS6 */ + *DANUBE_GPIO_P0_OUT = ((*DANUBE_GPIO_P0_OUT) | 0x2000); + +} + +/* + * This routine intializes the SSC appropriately depending + * on slave/master and full-/half-duplex mode. + * It assumes that the SSC is disabled and the fifo's and buffers + * are flushes later on. + */ +static int +ifx_ssc_sethwopts (struct ifx_ssc_port *info) +{ + unsigned long flags, bits; + struct ifx_ssc_hwopts *opts = &info->opts; + + /* sanity checks */ + if ((opts->dataWidth < IFX_SSC_MIN_DATA_WIDTH) || + (opts->dataWidth > IFX_SSC_MAX_DATA_WIDTH)) { + printk ("%s: sanity check failed\n", __FUNCTION__); + return -EINVAL; + } + bits = (opts->dataWidth - 1) << IFX_SSC_CON_DATA_WIDTH_OFFSET; + bits |= IFX_SSC_CON_ENABLE_BYTE_VALID; + // if (opts->abortErrDetect) + // bits |= IFX_SSC_CON_ABORT_ERR_CHECK; + if (opts->rxOvErrDetect) + bits |= IFX_SSC_CON_RX_OFL_CHECK; + if (opts->rxUndErrDetect) + bits |= IFX_SSC_CON_RX_UFL_CHECK; + if (opts->txOvErrDetect) + bits |= IFX_SSC_CON_TX_OFL_CHECK; + if (opts->txUndErrDetect) + bits |= IFX_SSC_CON_TX_UFL_CHECK; + if (opts->loopBack) + bits |= IFX_SSC_CON_LOOPBACK_MODE; + if (opts->echoMode) + bits |= IFX_SSC_CON_ECHO_MODE_ON; + if (opts->headingControl) + bits |= IFX_SSC_CON_MSB_FIRST; + if (opts->clockPhase) + bits |= IFX_SSC_CON_LATCH_THEN_SHIFT; + if (opts->clockPolarity) + bits |= IFX_SSC_CON_CLOCK_FALL; + switch (opts->modeRxTx) { + case IFX_SSC_MODE_TX: + bits |= IFX_SSC_CON_RX_OFF; + break; + case IFX_SSC_MODE_RX: + bits |= IFX_SSC_CON_TX_OFF; + break; + } // switch (opts->modeRxT) + local_irq_save (flags); + WRITE_PERIPHERAL_REGISTER (bits, info->mapbase + IFX_SSC_CON); + WRITE_PERIPHERAL_REGISTER ((info->opts. + gpoCs << IFX_SSC_GPOCON_ISCSB0_POS) | + (info->opts. + gpoInv << IFX_SSC_GPOCON_INVOUT0_POS), + info->mapbase + IFX_SSC_GPOCON); + /*TODO: disable cs */ + WRITE_PERIPHERAL_REGISTER (info->opts. + gpoCs << IFX_SSC_WHBGPOSTAT_SETOUT0_POS, + info->mapbase + IFX_SSC_WHBGPOSTAT); + + //master mode + if (opts->masterSelect) { + WRITE_PERIPHERAL_REGISTER (IFX_SSC_WHBSTATE_SET_MASTER_SELECT, + info->mapbase + IFX_SSC_WHBSTATE); + } + else { + WRITE_PERIPHERAL_REGISTER (IFX_SSC_WHBSTATE_CLR_MASTER_SELECT, + info->mapbase + IFX_SSC_WHBSTATE); + } + // init serial framing + WRITE_PERIPHERAL_REGISTER (0, info->mapbase + IFX_SSC_SFCON); + /* set up the port pins */ + //check for general requirements to switch (external) pad/pin characteristics + ifx_gpio_init (); + local_irq_restore (flags); + + return 0; +} // ifx_ssc_sethwopts + +static int +ifx_ssc_set_baud (struct ifx_ssc_port *info, unsigned int baud) +{ + unsigned int ifx_ssc_clock; + unsigned int br; + unsigned long flags; + bool enabled; + + ifx_ssc_clock = ifx_ssc_get_kernel_clk (info); + if (ifx_ssc_clock == 0) + return -EINVAL; + local_irq_save (flags); + /* have to disable the SSC to set the baudrate */ + enabled = (READ_PERIPHERAL_REGISTER (info->mapbase + IFX_SSC_STATE) + & IFX_SSC_STATE_IS_ENABLED) != 0; + WRITE_PERIPHERAL_REGISTER (IFX_SSC_WHBSTATE_CLR_ENABLE, + info->mapbase + IFX_SSC_WHBSTATE); + + // compute divider + br = (((ifx_ssc_clock >> 1) + baud / 2) / baud) - 1; + asm ("SYNC"); + if (br > 0xffff || + ((br == 0) && + ((READ_PERIPHERAL_REGISTER (info->mapbase + IFX_SSC_STATE) & + IFX_SSC_STATE_IS_MASTER) == 0))) { + local_irq_restore (flags); + printk ("%s: illegal baudrate %u\n", __FUNCTION__, baud); + return -EINVAL; + } + WRITE_PERIPHERAL_REGISTER (br, info->mapbase + IFX_SSC_BR); + if (enabled) + WRITE_PERIPHERAL_REGISTER (IFX_SSC_WHBSTATE_SET_ENABLE, + info->mapbase + IFX_SSC_WHBSTATE); + + local_irq_restore (flags); + return 0; +} // ifx_ssc_set_baud + +static int +ifx_ssc_hwinit (struct ifx_ssc_port *info) +{ + unsigned long flags; + bool enabled; + + /* have to disable the SSC */ + enabled = (READ_PERIPHERAL_REGISTER (info->mapbase + IFX_SSC_STATE) + & IFX_SSC_STATE_IS_ENABLED) != 0; + WRITE_PERIPHERAL_REGISTER (IFX_SSC_WHBSTATE_CLR_ENABLE, + info->mapbase + IFX_SSC_WHBSTATE); + + if (ifx_ssc_sethwopts (info) < 0) { + printk ("%s: setting the hardware options failed\n", + __FUNCTION__); + return -EINVAL; + } + + if (ifx_ssc_set_baud (info, info->baud) < 0) { + printk ("%s: setting the baud rate failed\n", __FUNCTION__); + return -EINVAL; + } + local_irq_save (flags); + /* TX FIFO */ + WRITE_PERIPHERAL_REGISTER ((IFX_SSC_DEF_TXFIFO_FL << + IFX_SSC_XFCON_ITL_OFFSET) | + IFX_SSC_XFCON_FIFO_ENABLE, + info->mapbase + IFX_SSC_TXFCON); + /* RX FIFO */ + WRITE_PERIPHERAL_REGISTER ((IFX_SSC_DEF_RXFIFO_FL << + IFX_SSC_XFCON_ITL_OFFSET) | + IFX_SSC_XFCON_FIFO_ENABLE, + info->mapbase + IFX_SSC_RXFCON); + local_irq_restore (flags); + if (enabled) + WRITE_PERIPHERAL_REGISTER (IFX_SSC_WHBSTATE_SET_ENABLE, + info->mapbase + IFX_SSC_WHBSTATE); + return 0; +} // ifx_ssc_hwinit + +/*----------------------------------------------------------------------------- + FUNC-NAME : ifx_ssc_batch_exec + LONG-NAME : + PURPOSE : + + PARAMETER : *info pointer to the port-specific structure ifx_ssc_port. + + RESULT : Returns error code + + REMARKS : +-----------------------------------------------------------------------------*/ +static int +ifx_ssc_batch_exec (struct ifx_ssc_port *info, + struct ifx_ssc_batch_list *batch_anchor) +{ + // ### TO DO: implement user space batch execution + // first, copy the whole linked list from user to kernel space + // save some hardware options + // execute list + // restore hardware options if selected + return -EFAULT; +} // ifx_ssc_batch_exec + +/* + * This routine allows the driver to implement device- + * specific ioctl's. If the ioctl number passed in cmd is + * not recognized by the driver, it should return ENOIOCTLCMD. + */ +int +ifx_ssc_ioctl (struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long data) +{ + struct ifx_ssc_port *info; + int line, ret_val = 0; + unsigned long flags; + unsigned long tmp; + int from_kernel = 0; + + if ((inode == (struct inode *) 0) || (inode == (struct inode *) 1)) { + from_kernel = 1; + line = (int) inode; + } + else + line = MINOR (filp->f_dentry->d_inode->i_rdev); + + /* don't use more minor devices than we can support */ + if (line < 0 || line >= PORT_CNT) + return -ENXIO; + + info = &isp[line]; + + switch (cmd) { + case IFX_SSC_STATS_READ: + /* data must be a pointer to a struct ifx_ssc_statistics */ + if (from_kernel) + memcpy ((void *) data, (void *) &info->stats, + sizeof (struct ifx_ssc_statistics)); + else if (copy_to_user ((void *) data, + (void *) &info->stats, + sizeof (struct ifx_ssc_statistics))) + ret_val = -EFAULT; + break; + case IFX_SSC_STATS_RESET: + /* just resets the statistics counters */ + memset ((void *) &info->stats, 0, + sizeof (struct ifx_ssc_statistics)); + break; + case IFX_SSC_BAUD_SET: + /* if the buffers are not empty then the port is */ + /* busy and we shouldn't change things on-the-fly! */ + if (!info->txbuf || !info->rxbuf || + (READ_PERIPHERAL_REGISTER (info->mapbase + IFX_SSC_STATE) + & IFX_SSC_STATE_BUSY)) { + ret_val = -EBUSY; + break; + } + /* misuse flags */ + if (from_kernel) + flags = *((unsigned long *) data); + else if (copy_from_user ((void *) &flags, + (void *) data, sizeof (flags))) { + ret_val = -EFAULT; + break; + } + if (flags == 0) { + ret_val = -EINVAL; + break; + } + if (ifx_ssc_set_baud (info, flags) < 0) { + ret_val = -EINVAL; + break; + } + info->baud = flags; + break; + case IFX_SSC_BAUD_GET: + if (from_kernel) + *((unsigned int *) data) = info->baud; + else if (copy_to_user ((void *) data, + (void *) &info->baud, + sizeof (unsigned long))) + ret_val = -EFAULT; + break; + case IFX_SSC_RXTX_MODE_SET: + if (from_kernel) + tmp = *((unsigned long *) data); + else if (copy_from_user ((void *) &tmp, + (void *) data, sizeof (tmp))) { + ret_val = -EFAULT; + break; + } + ret_val = ifx_ssc_rxtx_mode_set (info, tmp); + break; + case IFX_SSC_RXTX_MODE_GET: + tmp = READ_PERIPHERAL_REGISTER (info->mapbase + IFX_SSC_CON) & + (~(IFX_SSC_CON_RX_OFF | IFX_SSC_CON_TX_OFF)); + if (from_kernel) + *((unsigned int *) data) = tmp; + else if (copy_to_user ((void *) data, + (void *) &tmp, sizeof (tmp))) + ret_val = -EFAULT; + break; + + case IFX_SSC_ABORT: + ifx_ssc_abort (info); + break; + + case IFX_SSC_GPO_OUT_SET: + if (from_kernel) + tmp = *((unsigned long *) data); + else if (copy_from_user ((void *) &tmp, + (void *) data, sizeof (tmp))) { + ret_val = -EFAULT; + break; + } + if (tmp > IFX_SSC_MAX_GPO_OUT) + ret_val = -EINVAL; + else + WRITE_PERIPHERAL_REGISTER + (1 << (tmp + IFX_SSC_WHBGPOSTAT_SETOUT0_POS), + info->mapbase + IFX_SSC_WHBGPOSTAT); + break; + case IFX_SSC_GPO_OUT_CLR: + if (from_kernel) + tmp = *((unsigned long *) data); + else if (copy_from_user ((void *) &tmp, + (void *) data, sizeof (tmp))) { + ret_val = -EFAULT; + break; + } + if (tmp > IFX_SSC_MAX_GPO_OUT) + ret_val = -EINVAL; + else { + WRITE_PERIPHERAL_REGISTER + (1 << (tmp + IFX_SSC_WHBGPOSTAT_CLROUT0_POS), + info->mapbase + IFX_SSC_WHBGPOSTAT); + } + break; + case IFX_SSC_GPO_OUT_GET: + tmp = READ_PERIPHERAL_REGISTER + (info->mapbase + IFX_SSC_GPOSTAT); + if (from_kernel) + *((unsigned int *) data) = tmp; + else if (copy_to_user ((void *) data, + (void *) &tmp, sizeof (tmp))) + ret_val = -EFAULT; + break; + case IFX_SSC_FRM_STATUS_GET: + ifx_ssc_frm_status_get (info); + if (from_kernel) + memcpy ((void *) data, (void *) &info->frm_status, + sizeof (struct ifx_ssc_frm_status)); + else if (copy_to_user ((void *) data, + (void *) &info->frm_status, + sizeof (struct ifx_ssc_frm_status))) + ret_val = -EFAULT; + break; + case IFX_SSC_FRM_CONTROL_GET: + ifx_ssc_frm_control_get (info); + if (from_kernel) + memcpy ((void *) data, (void *) &info->frm_opts, + sizeof (struct ifx_ssc_frm_opts)); + else if (copy_to_user ((void *) data, + (void *) &info->frm_opts, + sizeof (struct ifx_ssc_frm_opts))) + ret_val = -EFAULT; + break; + case IFX_SSC_FRM_CONTROL_SET: + if (from_kernel) + memcpy ((void *) &info->frm_opts, (void *) data, + sizeof (struct ifx_ssc_frm_opts)); + else if (copy_to_user ((void *) &info->frm_opts, + (void *) data, + sizeof (struct ifx_ssc_frm_opts))) { + ret_val = -EFAULT; + break; + } + ret_val = ifx_ssc_frm_control_set (info); + break; + case IFX_SSC_HWOPTS_SET: + /* data must be a pointer to a struct ifx_ssc_hwopts */ + /* if the buffers are not empty then the port is */ + /* busy and we shouldn't change things on-the-fly! */ + if (!info->txbuf || !info->rxbuf || + (READ_PERIPHERAL_REGISTER (info->mapbase + IFX_SSC_STATE) + & IFX_SSC_STATE_BUSY)) { + ret_val = -EBUSY; + break; + } + if (from_kernel) + memcpy ((void *) &info->opts, (void *) data, + sizeof (struct ifx_ssc_hwopts)); + else if (copy_from_user ((void *) &info->opts, + (void *) data, + sizeof (struct ifx_ssc_hwopts))) { + ret_val = -EFAULT; + break; + } + if (ifx_ssc_hwinit (info) < 0) { + ret_val = -EIO; + } + break; + case IFX_SSC_HWOPTS_GET: + /* data must be a pointer to a struct ifx_ssc_hwopts */ + if (from_kernel) + memcpy ((void *) data, (void *) &info->opts, + sizeof (struct ifx_ssc_hwopts)); + else if (copy_to_user ((void *) data, + (void *) &info->opts, + sizeof (struct ifx_ssc_hwopts))) + ret_val = -EFAULT; + break; + default: + ret_val = -ENOIOCTLCMD; + } + + return ret_val; +} /* ifx_ssc_ioctl */ + +EXPORT_SYMBOL (ifx_ssc_ioctl); + +///* the poll routine */ +//static unsigned int +//ifx_ssc_poll(struct file *filp, struct poll_table_struct *pts) +//{ +// int unit = MINOR(filp->f_dentry->d_inode->i_rdev); +// struct ifx_ssc_port *info; +// unsigned int mask = 0; +// int spc; +// +// info = &isp[unit]; +// +// /* add event to the wait queues */ +// /* DO NOT FORGET TO DO A WAKEUP ON THESE !!!! */ +// poll_wait(filp, &info->pwait, pts); +// +// /* are there bytes in the RX SW-FIFO? */ +// if (info->rxrp != info->rxwp) +// mask |= POLLIN | POLLRDNORM; +// +// /* free space in the TX SW-FIFO */ +// spc = info->txrp - info->txwp - 1; +// if (spc < 0) +// spc += TX_BUFSIZE; +//#ifdef IFX_SSC_USEDMA +// /* writing always works, except in the DMA case when all descriptors */ +// /* are used up */ +// if (unit == 1 && info->dma_freecnt == 0) +// spc = 0; +//#endif +// if (spc > 0) +// mask |= POLLOUT | POLLWRNORM; +// +// return (mask); +//} + +static int +ifx_ssc1_read_proc (char *page, char **start, off_t offset, int count, + int *eof, void *data) +{ + int off = 0; + unsigned long flags; + + /* don't want any interrupts here */ + local_save_flags(flags); + local_irq_disable(); + + /* print statistics */ + off += sprintf (page + off, + "Statistics for Infineon Synchronous Serial Controller SSC1\n"); + off += sprintf (page + off, "RX overflow errors %d\n", + isp[0].stats.rxOvErr); + off += sprintf (page + off, "RX underflow errors %d\n", + isp[0].stats.rxUnErr); + off += sprintf (page + off, "TX overflow errors %d\n", + isp[0].stats.txOvErr); + off += sprintf (page + off, "TX underflow errors %d\n", + isp[0].stats.txUnErr); + off += sprintf (page + off, "Abort errors %d\n", + isp[0].stats.abortErr); + off += sprintf (page + off, "Mode errors %d\n", isp[0].stats.modeErr); + off += sprintf (page + off, "RX Bytes %d\n", isp[0].stats.rxBytes); + off += sprintf (page + off, "TX Bytes %d\n", isp[0].stats.txBytes); + + local_irq_restore(flags); /* XXXXX */ + *eof = 1; + return (off); +} + +/* + * This routine prints out the appropriate serial driver version number + */ +static inline void +show_version (void) +{ +#if 0 + printk ("Infineon Technologies Synchronous Serial Controller (SSC) driver\n" " version %s - built %s %s\n", IFX_SSC_DRV_VERSION, __DATE__, __TIME__); +#endif +} /* show_version */ + +/* + * Due to the fact that a port can be dynamically switched between slave + * and master mode using an IOCTL the hardware is not initialized here, + * but in ifx_ssc_hwinit() as a result of an IOCTL. + */ +int __init +ifx_ssc_init (void) +{ + struct ifx_ssc_port *info; + int i, nbytes; + unsigned long flags; + int ret_val; + + // ### TO DO: dynamic port count evaluation due to pin multiplexing + + ret_val = -ENOMEM; + nbytes = PORT_CNT * sizeof (struct ifx_ssc_port); + isp = (struct ifx_ssc_port *) kmalloc (nbytes, GFP_KERNEL); + if (isp == NULL) { + printk ("%s: no memory for isp\n", __FUNCTION__); + return (ret_val); + } + memset (isp, 0, nbytes); + + show_version (); + + /* register the device */ + ret_val = -ENXIO; +/* + i = maj; +*/ + if ((i = register_chrdev (maj, "ssc", &ifx_ssc_fops)) < 0) { + printk ("Unable to register major %d for the Infineon SSC\n", + maj); + if (maj == 0) { + goto errout; + } + else { + maj = 0; + if ((i = + register_chrdev (maj, "ssc", + &ifx_ssc_fops)) < 0) { + printk ("Unable to register major %d for the Infineon SSC\n", maj); + goto errout; + } + } + } + if (maj == 0) + maj = i; + //printk("registered major %d for Infineon SSC\n", maj); + + /* set default values in ifx_ssc_port */ + for (i = 0; i < PORT_CNT; i++) { + info = &isp[i]; + info->port_nr = i; + /* default values for the HwOpts */ + info->opts.AbortErrDetect = IFX_SSC_DEF_ABRT_ERR_DETECT; + info->opts.rxOvErrDetect = IFX_SSC_DEF_RO_ERR_DETECT; + info->opts.rxUndErrDetect = IFX_SSC_DEF_RU_ERR_DETECT; + info->opts.txOvErrDetect = IFX_SSC_DEF_TO_ERR_DETECT; + info->opts.txUndErrDetect = IFX_SSC_DEF_TU_ERR_DETECT; + info->opts.loopBack = IFX_SSC_DEF_LOOP_BACK; + info->opts.echoMode = IFX_SSC_DEF_ECHO_MODE; + info->opts.idleValue = IFX_SSC_DEF_IDLE_DATA; + info->opts.clockPolarity = IFX_SSC_DEF_CLOCK_POLARITY; + info->opts.clockPhase = IFX_SSC_DEF_CLOCK_PHASE; + info->opts.headingControl = IFX_SSC_DEF_HEADING_CONTROL; + info->opts.dataWidth = IFX_SSC_DEF_DATA_WIDTH; + info->opts.modeRxTx = IFX_SSC_DEF_MODE_RXTX; + info->opts.gpoCs = IFX_SSC_DEF_GPO_CS; + info->opts.gpoInv = IFX_SSC_DEF_GPO_INV; + info->opts.masterSelect = IFX_SSC_DEF_MASTERSLAVE; + info->baud = IFX_SSC_DEF_BAUDRATE; + info->rxbuf = NULL; + info->txbuf = NULL; + /* values specific to SSC1 */ + if (i == 0) { + info->mapbase = DANUBE_SSC1_BASE_ADDR; + // ### TO DO: power management + + // setting interrupt vectors + info->txirq = DANUBE_SSC_TIR; + info->rxirq = DANUBE_SSC_RIR; + info->errirq = DANUBE_SSC_EIR; +/* + info->frmirq = IFX_SSC_FIR; +*/ + } + /* activate SSC */ + /* CLC.DISS = 0 */ + WRITE_PERIPHERAL_REGISTER (IFX_SSC_DEF_RMC << + IFX_CLC_RUN_DIVIDER_OFFSET, + info->mapbase + IFX_SSC_CLC); + +// ### TO DO: multiple instances + + init_waitqueue_head (&info->rwait); + //init_waitqueue_head(&info->pwait); + + local_irq_save (flags); + + // init serial framing register + WRITE_PERIPHERAL_REGISTER (IFX_SSC_DEF_SFCON, + info->mapbase + IFX_SSC_SFCON); + + /* try to get the interrupts */ + // ### TO DO: interrupt handling with multiple instances + ret_val = + ifx_int_wrapper.request (info->txirq, ifx_ssc_tx_int, SA_INTERRUPT, "ifx_ssc_tx", info); + if (ret_val) { + printk ("%s: unable to get irq %d\n", __FUNCTION__, + info->txirq); + local_irq_restore (flags); + goto errout; + } + ret_val = + ifx_int_wrapper.request (info->rxirq, ifx_ssc_rx_int, SA_INTERRUPT, "ifx_ssc_rx", info); + if (ret_val) { + printk ("%s: unable to get irq %d\n", __FUNCTION__, + info->rxirq); + local_irq_restore (flags); + goto irqerr; + } + ret_val = + ifx_int_wrapper.request (info->errirq, ifx_ssc_err_int, SA_INTERRUPT,"ifx_ssc_err", info); + if (ret_val) { + printk ("%s: unable to get irq %d\n", __FUNCTION__, + info->errirq); + local_irq_restore (flags); + goto irqerr; + } +/* + ret_val = ifx_int_wrapper.request(info->frmirq, ifx_ssc_frm_int, + 0, "ifx_ssc_frm", info); + if (ret_val){ + printk("%s: unable to get irq %d\n", __FUNCTION__, + info->frmirq); + local_irq_restore(flags); + goto irqerr; + } + +*/ + WRITE_PERIPHERAL_REGISTER (IFX_SSC_DEF_IRNEN, + info->mapbase + IFX_SSC_IRN_EN); + ifx_int_wrapper.enable (info->txirq); + ifx_int_wrapper.enable (info->rxirq); + ifx_int_wrapper.enable (info->errirq); + + local_irq_restore (flags); + } // for (i = 0; i < PORT_CNT; i++) + + /* init the SSCs with default values */ + for (i = 0; i < PORT_CNT; i++) { + info = &isp[i]; + if (ifx_ssc_hwinit (info) < 0) { + printk ("%s: hardware init failed for port %d\n", + __FUNCTION__, i); + goto irqerr; + } + } + + /* register /proc read handler */ + // ### TO DO: multiple instances + /* for SSC1, which is always present */ + create_proc_read_entry ("driver/ssc1", 0, NULL, ifx_ssc1_read_proc, + NULL); + return 0; + + irqerr: + // ### TO DO: multiple instances + ifx_int_wrapper.free (isp[0].txirq, &isp[0]); + ifx_int_wrapper.free (isp[0].rxirq, &isp[0]); + ifx_int_wrapper.free (isp[0].errirq, &isp[0]); +/* + ifx_int_wrapper.free(isp[0].frmirq, &isp[0]); +*/ + errout: + /* free up any allocated memory in the error case */ + kfree (isp); + return (ret_val); +} /* ifx_ssc_init */ + +void +ifx_ssc_cleanup_module (void) +{ + int i; + + /* free up any allocated memory */ + for (i = 0; i < PORT_CNT; i++) { + /* disable the SSC */ + WRITE_PERIPHERAL_REGISTER (IFX_SSC_WHBSTATE_CLR_ENABLE, + isp[i].mapbase + IFX_SSC_WHBSTATE); + /* free the interrupts */ + ifx_int_wrapper.free (isp[i].txirq, &isp[i]); + ifx_int_wrapper.free (isp[i].rxirq, &isp[i]); + ifx_int_wrapper.free (isp[i].errirq, &isp[i]); +/* + ifx_int_wrapper.free(isp[i].frmirq, &isp[i]); + + if (isp[i].rxbuf != NULL) + kfree(isp[i].rxbuf); + if (isp[i].txbuf != NULL) + kfree(isp[i].txbuf); +*/ + } + kfree (isp); + /* unregister the device */ +// if (unregister_chrdev (maj, "ssc")) { +// printk ("Unable to unregister major %d for the SSC\n", maj); +// } + /* delete /proc read handler */ + remove_proc_entry ("driver/ssc1", NULL); + remove_proc_entry ("driver/ssc2", NULL); +} /* ifx_ssc_cleanup_module */ + +module_exit (ifx_ssc_cleanup_module); + +/* Module entry-points */ +module_init (ifx_ssc_init); + +#ifndef MODULE +static int __init +ifx_ssc_set_maj (char *str) +{ + maj = simple_strtol (str, NULL, 0); + return 1; +} + +__setup ("ssc_maj=", ifx_ssc_set_maj); +#endif /* !MODULE */ + +#define DANUBE_SSC_EMSG(fmt,arg...) printk("%s: "fmt,__FUNCTION__, ##arg) +/* Brief: chip select enable + */ +inline int +ifx_ssc_cs_low (u32 pin) +{ + int ret = 0; + if ((ret = + ifx_ssc_ioctl ((struct inode *) 0, NULL, IFX_SSC_GPO_OUT_CLR, + (unsigned long) &pin))) { + DANUBE_SSC_EMSG ("clear CS %d fails\n", pin); + } + wmb (); + return ret; +} + +EXPORT_SYMBOL (ifx_ssc_cs_low); +/* Brief: chip select disable + */ +inline int +ifx_ssc_cs_high (u32 pin) +{ + int ret = 0; + if ((ret = + ifx_ssc_ioctl ((struct inode *) 0, NULL, IFX_SSC_GPO_OUT_SET, + (unsigned long) &pin))) { + DANUBE_SSC_EMSG ("set CS %d fails\n", pin); + } + wmb (); + return ret; +} + +EXPORT_SYMBOL (ifx_ssc_cs_high); +/* Brief: one SSC session + * Parameter: + * tx_buf + * tx_len + * rx_buf + * rx_len + * session_mode: IFX_SSC_MODE_RXTX or IFX_SSC_MODE_TX + * Return: >=0 number of bytes received (if rx_buf != 0) or transmitted + * <0 error code + * Description: + * 0. copy data to internal buffer + * 1. Write command + * 2a. If SSC_SESSION_MODE_TXONLY, read tx_len data + * 2b. If not Read back (tx_len + rx_len) data + * 3. copy internal buffer to rx buf if necessary + */ +static int +ssc_session (char *tx_buf, u32 tx_len, char *rx_buf, u32 rx_len) +{ + int ret = 0; + + char *ssc_tx_buf = NULL; + char *ssc_rx_buf = NULL; + +// volatile char ssc_tx_buf[128]={0}; +// volatile char ssc_rx_buf[128]={0}; + + int eff_size = 0; + u8 mode = 0; + + if (tx_buf == NULL && tx_len == 0 && rx_buf == NULL && rx_len == 0) { + DANUBE_SSC_EMSG ("invalid parameters\n"); + ret = -EINVAL; + goto ssc_session_exit; + } + else if (tx_buf == NULL || tx_len == 0) { + if (rx_buf != NULL && rx_len != 0) { + mode = IFX_SSC_MODE_RX; + } + else { + DANUBE_SSC_EMSG ("invalid parameters\n"); + ret = -EINVAL; + goto ssc_session_exit; + } + } + else if (rx_buf == NULL || rx_len == 0) { + if (tx_buf != NULL && tx_len != 0) { + mode = IFX_SSC_MODE_TX; + } + else { + DANUBE_SSC_EMSG ("invalid parameters\n"); + ret = -EINVAL; + goto ssc_session_exit; + } + } + else { + mode = IFX_SSC_MODE_RXTX; + } + + if (mode == IFX_SSC_MODE_RXTX) { + eff_size = tx_len + rx_len; + } + else if (mode == IFX_SSC_MODE_RX) { + eff_size = rx_len; + } + else { + eff_size = tx_len; + } + + //4 bytes alignment, required by driver + /* change by TaiCheng */ + //if (in_irq()){ + if (1) { + ssc_tx_buf = + (char *) kmalloc (sizeof (char) * + ((eff_size + 3) & (~3)), + GFP_ATOMIC); + ssc_rx_buf = + (char *) kmalloc (sizeof (char) * + ((eff_size + 3) & (~3)), + GFP_ATOMIC); + } + else { + ssc_tx_buf = + (char *) kmalloc (sizeof (char) * + ((eff_size + 3) & (~3)), + GFP_KERNEL); + ssc_rx_buf = + (char *) kmalloc (sizeof (char) * + ((eff_size + 3) & (~3)), + GFP_KERNEL); + } + if (ssc_tx_buf == NULL || ssc_rx_buf == NULL) { + DANUBE_SSC_EMSG ("no memory for size of %d\n", eff_size); + ret = -ENOMEM; + goto ssc_session_exit; + } + memset ((void *) ssc_tx_buf, 0, eff_size); + memset ((void *) ssc_rx_buf, 0, eff_size); + + if (tx_len > 0) { + memcpy (ssc_tx_buf, tx_buf, tx_len); + } + + ret = ifx_ssc_kwrite (0, ssc_tx_buf, eff_size); + + if (ret > 0) { + ssc_tx_buf = NULL; //should be freed by ifx_ssc_kwrite + } + + if (ret != eff_size) { + DANUBE_SSC_EMSG ("ifx_ssc_write return %d\n", ret); + goto ssc_session_exit; + } + ret = ifx_ssc_kread (0, ssc_rx_buf, eff_size); + if (ret != eff_size) { + DANUBE_SSC_EMSG ("ifx_ssc_read return %d\n", ret); + goto ssc_session_exit; + } + + memcpy (rx_buf, ssc_rx_buf + tx_len, rx_len); + + if (mode == IFX_SSC_MODE_TX) { + ret = tx_len; + } + else { + ret = rx_len; + } + ssc_session_exit: + + if (ssc_tx_buf != NULL) + kfree (ssc_tx_buf); + if (ssc_rx_buf != NULL) + kfree (ssc_rx_buf); + + if (ret < 0) { + printk ("ssc session fails\n"); + } + return ret; +} + +/* Brief: TX-RX session + * Parameter: + * tx_buf + * tx_len + * rx_buf + * rx_len + * Return: >=0 number of bytes received + * <0 error code + * Description: + * 1. TX session + * 2. RX session + */ +int +ifx_ssc_txrx (char *tx_buf, u32 tx_len, char *rx_buf, u32 rx_len) +{ + return ssc_session (tx_buf, tx_len, rx_buf, rx_len); +} + +EXPORT_SYMBOL (ifx_ssc_txrx); +/* Brief: TX only session + * Parameter: + * tx_buf + * tx_len + * Return: >=0 number of bytes transmitted + * <0 error code + */ +int +ifx_ssc_tx (char *tx_buf, u32 tx_len) +{ + return ssc_session (tx_buf, tx_len, NULL, 0); +} + +EXPORT_SYMBOL (ifx_ssc_tx); +/* Brief: RX only session + * Parameter: + * rx_buf + * rx_len + * Return: >=0 number of bytes received + * <0 error code + */ +int +ifx_ssc_rx (char *rx_buf, u32 rx_len) +{ + return ssc_session (NULL, 0, rx_buf, rx_len); +} + +EXPORT_SYMBOL (ifx_ssc_rx); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("John Crispin "); +MODULE_DESCRIPTION("danube ssc driver"); + diff --git a/target/linux/danube/files/include/asm-mips/danube/danube.h b/target/linux/danube/files/include/asm-mips/danube/danube.h index f73b255ea7..5bad876e1f 100644 --- a/target/linux/danube/files/include/asm-mips/danube/danube.h +++ b/target/linux/danube/files/include/asm-mips/danube/danube.h @@ -322,7 +322,6 @@ #define DANUBE_LED_EDGE_MASK (1 << 26) - /*------------ GPIO */ #define DANUBE_GPIO_BASE_ADDR (0xBE100B00) @@ -346,4 +345,14 @@ #define DANUBE_GPIO_P0_PUDEN ((u32*)(DANUBE_GPIO_BASE_ADDR + 0x0030)) #define DANUBE_GPIO_P1_PUDEN ((u32*)(DANUBE_GPIO_BASE_ADDR + 0x0060)) + +/*------------ SSC */ + +#define DANUBE_SSC1_BASE_ADDR (KSEG1 + 0x1e100800) + + + + + + #endif diff --git a/target/linux/danube/files/include/asm-mips/danube/danube_irq.h b/target/linux/danube/files/include/asm-mips/danube/danube_irq.h index 1912e1f323..45796831fe 100644 --- a/target/linux/danube/files/include/asm-mips/danube/danube_irq.h +++ b/target/linux/danube/files/include/asm-mips/danube/danube_irq.h @@ -32,6 +32,10 @@ #define DANUBEASC1_RIR (INT_NUM_IM3_IRL0 + 9) #define DANUBEASC1_EIR (INT_NUM_IM3_IRL0 + 10) +#define DANUBE_SSC_TIR (INT_NUM_IM0_IRL0 + 15) +#define DANUBE_SSC_RIR (INT_NUM_IM0_IRL0 + 14) +#define DANUBE_SSC_EIR (INT_NUM_IM0_IRL0 + 16) + #define DANUBE_TIMER6_INT (INT_NUM_IM1_IRL0 + 23) #define MIPS_CPU_TIMER_IRQ 7 diff --git a/target/linux/danube/files/include/asm-mips/danube/ifx_peripheral_definitions.h b/target/linux/danube/files/include/asm-mips/danube/ifx_peripheral_definitions.h new file mode 100644 index 0000000000..5bd788fff6 --- /dev/null +++ b/target/linux/danube/files/include/asm-mips/danube/ifx_peripheral_definitions.h @@ -0,0 +1,119 @@ +//************************************************************************* +//* Summary of definitions which are used in each peripheral * +//************************************************************************* + +#ifndef peripheral_definitions_h +#define peripheral_definitions_h + +////#include "cpu.h" +// +///* These files have to be included by each peripheral */ +//#include +//#include +//#include +//#include +//#include +//#include "SRAM_address_map.h" +// +///* common header files for all CPU's */ +//#include "iiu.h" +//#include "bcu.h" +//#include "FPI_address_map.h" +//#include "direct_interrupts.h" + +///////////////////////////////////////////////////////////////////////// + +//extern int _clz(); +//extern void _nop(); +//extern void _sleep(); +//extern void sys_enable_int(); + +typedef unsigned char UINT8; +typedef signed char INT8; +typedef unsigned short UINT16; +typedef signed short INT16; +typedef unsigned int UINT32; +typedef signed int INT32; +typedef unsigned long long UINT64; +typedef signed long long INT64; + +#define REG8( addr ) (*(volatile UINT8 *) (addr)) +#define REG16( addr ) (*(volatile UINT16 *)(addr)) +#define REG32( addr ) (*(volatile UINT32 *)(addr)) +#define REG64( addr ) (*(volatile UINT64 *)(addr)) + +/* define routine to set FPI access in Supervisor Mode */ +#define IFX_SUPERVISOR_ON() REG32(FB0_CFG) = 0x01 +/* Supervisor mode ends, following functions will be done in User mode */ +#define IFX_SUPERVISOR_OFF() REG32(FB0_CFG) = 0x00 +/* Supervisor mode ends, following functions will be done in User mode */ +#define IFX_SUPERVISOR_MODE() REG32(FB0_CFG) +/* Supervisor mode ends, following functions will be done in User mode */ +#define IFX_SUPERVISOR_SET(svm) REG32(FB0_CFG) = svm +/* enable all Interrupts in IIU */ +//#define IFX_ENABLE_IRQ(irq_mask, im_base) REG32(im_base | IIU_MASK) = irq_mask +///* get all high priority interrupt bits in IIU */ +//#define IFX_GET_IRQ_MASKED(im_base) REG32(im_base | IIU_IRMASKED) +///* signal ends of interrupt to IIU */ +//#define IFX_CLEAR_DIRECT_IRQ(irq_bit, im_base) REG32(im_base | IIU_IR) = irq_bit +///* force IIU interrupt register */ +//#define IFX_FORCE_IIU_REGISTER(data, im_base) REG32(im_base | IIU_IRDEBUG) = data +///* get all bits of interrupt register */ +//#define IFX_GET_IRQ_UNMASKED(im_base) REG32(im_base | IIU_IR) +/* insert a NOP instruction */ +#define NOP _nop() +/* CPU goes to power down mode until interrupt occurs */ +#define IFX_CPU_SLEEP _sleep() +/* enable all interrupts to CPU */ +#define IFX_CPU_ENABLE_ALL_INTERRUPT sys_enable_int() +/* get all low priority interrupt bits in peripheral */ +#define IFX_GET_LOW_PRIO_IRQ(int_reg) REG32(int_reg) +/* clear low priority interrupt bit in peripheral */ +#define IFX_CLEAR_LOW_PRIO_IRQ(irq_bit, int_reg) REG32(int_reg) = irq_bit +/* write FPI bus */ +#define WRITE_FPI_BYTE(data, addr) REG8(addr) = data +#define WRITE_FPI_16BIT(data, addr) REG16(addr) = data +#define WRITE_FPI_32BIT(data, addr) REG32(addr) = data +/* read FPI bus */ +#define READ_FPI_BYTE(addr) REG8(addr) +#define READ_FPI_16BIT(addr) REG16(addr) +#define READ_FPI_32BIT(addr) REG32(addr) +/* write peripheral register */ +#define WRITE_PERIPHERAL_REGISTER(data, addr) REG32(addr) = data + +#ifdef CONFIG_CPU_LITTLE_ENDIAN +#define WRITE_PERIPHERAL_REGISTER_16(data, addr) REG16(addr) = data +#define WRITE_PERIPHERAL_REGISTER_8(data, addr) REG8(addr) = data +#else //not CONFIG_CPU_LITTLE_ENDIAN +#define WRITE_PERIPHERAL_REGISTER_16(data, addr) REG16(addr+2) = data +#define WRITE_PERIPHERAL_REGISTER_8(data, addr) REG8(addr+3) = data +#endif //CONFIG_CPU_LITTLE_ENDIAN + +/* read peripheral register */ +#define READ_PERIPHERAL_REGISTER(addr) REG32(addr) + +/* read/modify(or)/write peripheral register */ +#define RMW_OR_PERIPHERAL_REGISTER(data, addr) REG32(addr) = REG32(addr) | data +/* read/modify(and)/write peripheral register */ +#define RMW_AND_PERIPHERAL_REGISTER(data, addr) REG32(addr) = REG32(addr) & (UINT32)data + +/* CPU-independent mnemonic constants */ +/* CLC register bits */ +#define IFX_CLC_ENABLE 0x00000000 +#define IFX_CLC_DISABLE 0x00000001 +#define IFX_CLC_DISABLE_STATUS 0x00000002 +#define IFX_CLC_SUSPEND_ENABLE 0x00000004 +#define IFX_CLC_CLOCK_OFF_DISABLE 0x00000008 +#define IFX_CLC_OVERWRITE_SPEN_FSOE 0x00000010 +#define IFX_CLC_FAST_CLOCK_SWITCH_OFF 0x00000020 +#define IFX_CLC_RUN_DIVIDER_MASK 0x0000FF00 +#define IFX_CLC_RUN_DIVIDER_OFFSET 8 +#define IFX_CLC_SLEEP_DIVIDER_MASK 0x00FF0000 +#define IFX_CLC_SLEEP_DIVIDER_OFFSET 16 +#define IFX_CLC_SPECIFIC_DIVIDER_MASK 0x00FF0000 +#define IFX_CLC_SPECIFIC_DIVIDER_OFFSET 24 + +/* number of cycles to wait for interrupt service routine to be called */ +#define WAIT_CYCLES 50 + +#endif /* PERIPHERAL_DEFINITIONS_H not yet defined */ diff --git a/target/linux/danube/files/include/asm-mips/danube/ifx_ssc.h b/target/linux/danube/files/include/asm-mips/danube/ifx_ssc.h new file mode 100644 index 0000000000..e541c8ec3b --- /dev/null +++ b/target/linux/danube/files/include/asm-mips/danube/ifx_ssc.h @@ -0,0 +1,258 @@ +/* + * ifx_ssc.h defines some data sructures used in ifx_ssc.c + * + * Copyright (C) 2004 Michael Schoenenborn (IFX COM TI BT) + * + * + */ + +#ifndef __IFX_SSC_H +#define __IFX_SSC_H +#ifdef __KERNEL__ +#include +#endif //__KERNEL__ + +#define PORT_CNT 1 // assume default value + +/* symbolic constants to be used in SSC routines */ + +// ### TO DO: bad performance +#define IFX_SSC_TXFIFO_ITL 1 +#define IFX_SSC_RXFIFO_ITL 1 + +struct ifx_ssc_statistics { + unsigned int abortErr; /* abort error */ + unsigned int modeErr; /* master/slave mode error */ + unsigned int txOvErr; /* TX Overflow error */ + unsigned int txUnErr; /* TX Underrun error */ + unsigned int rxOvErr; /* RX Overflow error */ + unsigned int rxUnErr; /* RX Underrun error */ + unsigned int rxBytes; + unsigned int txBytes; +}; + +struct ifx_ssc_hwopts { + unsigned int AbortErrDetect:1; /* Abort Error detection (in slave mode) */ + unsigned int rxOvErrDetect:1; /* Receive Overflow Error detection */ + unsigned int rxUndErrDetect:1; /* Receive Underflow Error detection */ + unsigned int txOvErrDetect:1; /* Transmit Overflow Error detection */ + unsigned int txUndErrDetect:1; /* Transmit Underflow Error detection */ + unsigned int echoMode:1; /* Echo mode */ + unsigned int loopBack:1; /* Loopback mode */ + unsigned int idleValue:1; /* Idle value */ + unsigned int clockPolarity:1; /* Idle clock is high or low */ + unsigned int clockPhase:1; /* Tx on trailing or leading edge */ + unsigned int headingControl:1; /* LSB first or MSB first */ + unsigned int dataWidth:6; /* from 2 up to 32 bits */ + unsigned int masterSelect:1; /* Master or Slave mode */ + unsigned int modeRxTx:2; /* rx/tx mode */ + unsigned int gpoCs:8; /* choose outputs to use for chip select */ + unsigned int gpoInv:8; /* invert GPO outputs */ +}; + +struct ifx_ssc_frm_opts { + bool FrameEnable; // SFCON.SFEN + unsigned int DataLength; // SFCON.DLEN + unsigned int PauseLength; // SFCON.PLEN + unsigned int IdleData; // SFCON.IDAT + unsigned int IdleClock; // SFCON.ICLK + bool StopAfterPause; // SFCON.STOP +}; + +struct ifx_ssc_frm_status { + bool DataBusy; // SFSTAT.DBSY + bool PauseBusy; // SFSTAT.PBSY + unsigned int DataCount; // SFSTAT.DCNT + unsigned int PauseCount; // SFSTAT.PCNT + bool EnIntAfterData; // SFCON.IBEN + bool EnIntAfterPause; // SFCON.IAEN +}; + +typedef struct { + char *buf; + size_t len; +} ifx_ssc_buf_item_t; + +// data structures for batch execution +typedef union { + struct { + bool save_options; + } init; + ifx_ssc_buf_item_t read; + ifx_ssc_buf_item_t write; + ifx_ssc_buf_item_t rd_wr; + unsigned int set_baudrate; + struct ifx_ssc_frm_opts set_frm; + unsigned int set_gpo; + struct ifx_ssc_hwopts set_hwopts; +} ifx_ssc_batch_cmd_param; + +struct ifx_ssc_batch_list { + unsigned int cmd; + ifx_ssc_batch_cmd_param cmd_param; + struct ifx_ssc_batch_list *next; +}; + +#ifdef __KERNEL__ +#define IFX_SSC_IS_MASTER(p) ((p)->opts.masterSelect == SSC_MASTER_MODE) + +struct ifx_ssc_port { + unsigned long mapbase; + struct ifx_ssc_hwopts opts; + struct ifx_ssc_statistics stats; + struct ifx_ssc_frm_status frm_status; + struct ifx_ssc_frm_opts frm_opts; + /* wait queue for ifx_ssc_read() */ + wait_queue_head_t rwait, pwait; + int port_nr; + char port_is_open; /* exclusive open - boolean */ +// int no_of_bits; /* number of _valid_ bits */ +// int elem_size; /* shift for element (no of bytes)*/ + /* buffer and pointers to the read/write position */ + char *rxbuf; /* buffer for RX */ + char *rxbuf_end; /* buffer end pointer for RX */ + volatile char *rxbuf_ptr; /* buffer write pointer for RX */ + char *txbuf; /* buffer for TX */ + char *txbuf_end; /* buffer end pointer for TX */ + volatile char *txbuf_ptr; /* buffer read pointer for TX */ + unsigned int baud; + /* each channel has its own interrupts */ + /* (transmit/receive/error/frame) */ + unsigned int txirq, rxirq, errirq, frmirq; +}; +/* default values for SSC configuration */ +// values of CON +#define IFX_SSC_DEF_IDLE_DATA 1 /* enable */ +#define IFX_SSC_DEF_BYTE_VALID_CTL 1 /* enable */ +#define IFX_SSC_DEF_DATA_WIDTH 32 /* bits */ +#define IFX_SSC_DEF_ABRT_ERR_DETECT 0 /* disable */ +#define IFX_SSC_DEF_RO_ERR_DETECT 1 /* enable */ +#define IFX_SSC_DEF_RU_ERR_DETECT 0 /* disable */ +#define IFX_SSC_DEF_TO_ERR_DETECT 0 /* disable */ +#define IFX_SSC_DEF_TU_ERR_DETECT 0 /* disable */ +#define IFX_SSC_DEF_LOOP_BACK 0 /* disable */ +#define IFX_SSC_DEF_ECHO_MODE 0 /* disable */ +#define IFX_SSC_DEF_CLOCK_POLARITY 0 /* low */ +#define IFX_SSC_DEF_CLOCK_PHASE 1 /* 0: shift on leading edge, latch on trailling edge, 1, otherwise */ +#define IFX_SSC_DEF_HEADING_CONTROL IFX_SSC_MSB_FIRST +#define IFX_SSC_DEF_MODE_RXTX IFX_SSC_MODE_RXTX +// other values +#define IFX_SSC_DEF_MASTERSLAVE IFX_SSC_MASTER_MODE /* master */ +#ifdef CONFIG_USE_EMULATOR +#define IFX_SSC_DEF_BAUDRATE 10000 +#else +#define IFX_SSC_DEF_BAUDRATE 2000000 +#endif +#define IFX_SSC_DEF_RMC 0x10 + +#define IFX_SSC_DEF_TXFIFO_FL 8 +#define IFX_SSC_DEF_RXFIFO_FL 1 + +#if 1 //TODO +#define IFX_SSC_DEF_GPO_CS 0x3 /* no chip select */ +#define IFX_SSC_DEF_GPO_INV 0 /* no chip select */ +#else +#error "what is ur Chip Select???" +#endif +#define IFX_SSC_DEF_SFCON 0 /* no serial framing */ +#if 0 +#define IFX_SSC_DEF_IRNEN IFX_SSC_T_BIT | /* enable all int's */\ + IFX_SSC_R_BIT | IFX_SSC_E_BIT | IFX_SSC_F_BIT +#endif +#define IFX_SSC_DEF_IRNEN IFX_SSC_T_BIT | /* enable all int's */\ + IFX_SSC_R_BIT | IFX_SSC_E_BIT +#endif /* __KERNEL__ */ + +// batch execution commands +#define IFX_SSC_BATCH_CMD_INIT 1 +#define IFX_SSC_BATCH_CMD_READ 2 +#define IFX_SSC_BATCH_CMD_WRITE 3 +#define IFX_SSC_BATCH_CMD_RD_WR 4 +#define IFX_SSC_BATCH_CMD_SET_BAUDRATE 5 +#define IFX_SSC_BATCH_CMD_SET_HWOPTS 6 +#define IFX_SSC_BATCH_CMD_SET_FRM 7 +#define IFX_SSC_BATCH_CMD_SET_GPO 8 +#define IFX_SSC_BATCH_CMD_FIFO_FLUSH 9 +//#define IFX_SSC_BATCH_CMD_ +//#define IFX_SSC_BATCH_CMD_ +#define IFX_SSC_BATCH_CMD_END_EXEC 0 + +/* Macros to configure SSC hardware */ +/* headingControl: */ +#define IFX_SSC_LSB_FIRST 0 +#define IFX_SSC_MSB_FIRST 1 +/* dataWidth: */ +#define IFX_SSC_MIN_DATA_WIDTH 2 +#define IFX_SSC_MAX_DATA_WIDTH 32 +/* master/slave mode select */ +#define IFX_SSC_MASTER_MODE 1 +#define IFX_SSC_SLAVE_MODE 0 +/* rx/tx mode */ +// ### TO DO: !!! ATTENTION! Hardware dependency => move to ifx_ssc_defines.h +#define IFX_SSC_MODE_RXTX 0 +#define IFX_SSC_MODE_RX 1 +#define IFX_SSC_MODE_TX 2 +#define IFX_SSC_MODE_OFF 3 +#define IFX_SSC_MODE_MASK IFX_SSC_MODE_RX | IFX_SSC_MODE_TX + +/* GPO values */ +#define IFX_SSC_MAX_GPO_OUT 7 + +#define IFX_SSC_RXREQ_BLOCK_SIZE 32768 + +/***********************/ +/* defines for ioctl's */ +/***********************/ +#define IFX_SSC_IOCTL_MAGIC 'S' +/* read out the statistics */ +#define IFX_SSC_STATS_READ _IOR(IFX_SSC_IOCTL_MAGIC, 1, struct ifx_ssc_statistics) +/* clear the statistics */ +#define IFX_SSC_STATS_RESET _IO(IFX_SSC_IOCTL_MAGIC, 2) +/* set the baudrate */ +#define IFX_SSC_BAUD_SET _IOW(IFX_SSC_IOCTL_MAGIC, 3, unsigned int) +/* get the current baudrate */ +#define IFX_SSC_BAUD_GET _IOR(IFX_SSC_IOCTL_MAGIC, 4, unsigned int) +/* set hardware options */ +#define IFX_SSC_HWOPTS_SET _IOW(IFX_SSC_IOCTL_MAGIC, 5, struct ifx_ssc_hwopts) +/* get the current hardware options */ +#define IFX_SSC_HWOPTS_GET _IOR(IFX_SSC_IOCTL_MAGIC, 6, struct ifx_ssc_hwopts) +/* set transmission mode */ +#define IFX_SSC_RXTX_MODE_SET _IOW(IFX_SSC_IOCTL_MAGIC, 7, unsigned int) +/* get the current transmission mode */ +#define IFX_SSC_RXTX_MODE_GET _IOR(IFX_SSC_IOCTL_MAGIC, 8, unsigned int) +/* abort transmission */ +#define IFX_SSC_ABORT _IO(IFX_SSC_IOCTL_MAGIC, 9) +#define IFX_SSC_FIFO_FLUSH _IO(IFX_SSC_IOCTL_MAGIC, 9) + +/* set general purpose outputs */ +#define IFX_SSC_GPO_OUT_SET _IOW(IFX_SSC_IOCTL_MAGIC, 32, unsigned int) +/* clear general purpose outputs */ +#define IFX_SSC_GPO_OUT_CLR _IOW(IFX_SSC_IOCTL_MAGIC, 33, unsigned int) +/* get general purpose outputs */ +#define IFX_SSC_GPO_OUT_GET _IOR(IFX_SSC_IOCTL_MAGIC, 34, unsigned int) + +/*** serial framing ***/ +/* get status of serial framing */ +#define IFX_SSC_FRM_STATUS_GET _IOR(IFX_SSC_IOCTL_MAGIC, 48, struct ifx_ssc_frm_status) +/* get counter reload values and control bits */ +#define IFX_SSC_FRM_CONTROL_GET _IOR(IFX_SSC_IOCTL_MAGIC, 49, struct ifx_ssc_frm_opts) +/* set counter reload values and control bits */ +#define IFX_SSC_FRM_CONTROL_SET _IOW(IFX_SSC_IOCTL_MAGIC, 50, struct ifx_ssc_frm_opts) + +/*** batch execution ***/ +/* do batch execution */ +#define IFX_SSC_BATCH_EXEC _IOW(IFX_SSC_IOCTL_MAGIC, 64, struct ifx_ssc_batch_list) + +#ifdef __KERNEL__ +// routines from ifx_ssc.c +// ### TO DO +/* kernel interface for read and write */ +ssize_t ifx_ssc_kread (int, char *, size_t); +ssize_t ifx_ssc_kwrite (int, const char *, size_t); + +#ifdef CONFIG_IFX_VP_KERNEL_TEST +void ifx_ssc_tc (void); +#endif // CONFIG_IFX_VP_KERNEL_TEST + +#endif //__KERNEL__ +#endif // __IFX_SSC_H diff --git a/target/linux/danube/files/include/asm-mips/danube/ifx_ssc_defines.h b/target/linux/danube/files/include/asm-mips/danube/ifx_ssc_defines.h new file mode 100644 index 0000000000..805d48ad84 --- /dev/null +++ b/target/linux/danube/files/include/asm-mips/danube/ifx_ssc_defines.h @@ -0,0 +1,547 @@ +#ifndef IFX_SSC_DEFINES_H +#define IFX_SSC_DEFINES_H + +#include "ifx_peripheral_definitions.h" + +/* maximum SSC FIFO size */ +#define IFX_SSC_MAX_FIFO_SIZE 32 + +/* register map of SSC */ + +/* address of the Clock Control Register of the SSC */ +#define IFX_SSC_CLC 0x00000000 +/* IFX_SSC_CLC register is significant in bits 23 downto 8 and in bits 5, 3, 2, 0 + bit 1 is hardware modified*/ +#define IFX_SSC_CLC_readmask 0x00FFFFEF +#define IFX_SSC_CLC_writemask 0x00FFFF3D +#define IFX_SSC_CLC_hwmask 0x00000002 +#define IFX_SSC_CLC_dontcare (IFX_SSC_CLC_readmask & IFX_SSC_CLC_writemask & ~IFX_SSC_CLC_hwmask) + +/* address of Port Input Select Register of the SSC */ +#define IFX_SSC_PISEL 0x00000004 +/* IFX_SSC_PISEL register is significant in lowest three bits only */ +#define IFX_SSC_PISEL_readmask 0x00000007 +#define IFX_SSC_PISEL_writemask 0x00000007 +#define IFX_SSC_PISEL_hwmask 0x00000000 +#define IFX_SSC_PISEL_dontcare (IFX_SSC_PISEL_readmask & IFX_SSC_PISEL_writemask & ~IFX_SSC_PISEL_hwmask) + +/* address of Identification Register of the SSC */ +#define IFX_SSC_ID 0x00000008 +/* IFX_SSC_ID register is significant in no bit */ +#define IFX_SSC_ID_readmask 0x0000FF3F +#define IFX_SSC_ID_writemask 0x00000000 +#define IFX_SSC_ID_hwmask 0x00000000 +#define IFX_SSC_ID_dontcare (IFX_SSC_ID_readmask & IFX_SSC_ID_writemask & ~IFX_SSC_ID_hwmask) + +/* address of the Control Register of the SSC */ +#define IFX_SSC_CON 0x00000010 +/* IFX_SSC_CON register is significant in bits 23:22, 20:16 and 12:0 */ +#define IFX_SSC_CON_readmask 0x01DF1FFF +#define IFX_SSC_CON_writemask 0x01DF1FFF +#define IFX_SSC_CON_hwmask 0x00000000 +#define IFX_SSC_CON_dontcare (IFX_SSC_CON_readmask & IFX_SSC_CON_writemask & ~IFX_SSC_CON_hwmask) + +/* address of the Status Register of the SSC */ +#define IFX_SSC_STATE 0x00000014 +/* IFX_SSC_STATE register is readable in bits 30:28, 26:24, 20:16, 12:7 and 2:0 + all bits except 1:0 are hardware modified */ +#define IFX_SSC_STATE_readmask 0x771F3F87 +#define IFX_SSC_STATE_writemask 0x00000000 +#define IFX_SSC_STATE_hwmask 0x771F3F84 +#define IFX_SSC_STATE_dontcare (IFX_SSC_STATE_readmask & IFX_SSC_STATE_writemask & ~IFX_SSC_STATE_hwmask) + +/* address of the Write Hardware Modified Control Register Bits of the SSC */ +#define IFX_SSC_WHBSTATE 0x00000018 +/* IFX_SSC_WHBSTATE register is write only */ +#define IFX_SSC_WHBSTATE_readmask 0x00000000 +#define IFX_SSC_WHBSTATE_writemask 0x0000FFFF +#define IFX_SSC_WHBSTATE_hwmask 0x00000000 +#define IFX_SSC_WHBSTATE_dontcare (IFX_SSC_WHBSTATE_readmask & IFX_SSC_WHBSTATE_writemask & ~IFX_SSC_WHBSTATE_hwmask) + +/* address of the Baudrate Timer Reload Register of the SSC */ +#define IFX_SSC_BR 0x00000040 +/* IFX_SSC_BR register is significant in bit 15 downto 0*/ +#define IFX_SSC_BR_readmask 0x0000FFFF +#define IFX_SSC_BR_writemask 0x0000FFFF +#define IFX_SSC_BR_hwmask 0x00000000 +#define IFX_SSC_BR_dontcare (IFX_SSC_BR_readmask & IFX_SSC_BR_writemask & ~IFX_SSC_BR_hwmask) + +/* address of the Baudrate Timer Status Register of the SSC */ +#define IFX_SSC_BRSTAT 0x00000044 +/* IFX_SSC_BRSTAT register is significant in bit 15 downto 0*/ +#define IFX_SSC_BRSTAT_readmask 0x0000FFFF +#define IFX_SSC_BRSTAT_writemask 0x00000000 +#define IFX_SSC_BRSTAT_hwmask 0x0000FFFF +#define IFX_SSC_BRSTAT_dontcare (IFX_SSC_BRSTAT_readmask & IFX_SSC_BRSTAT_writemask & ~IFX_SSC_BRSTAT_hwmask) + +/* address of the Transmitter Buffer Register of the SSC */ +#define IFX_SSC_TB 0x00000020 +/* IFX_SSC_TB register is significant in bit 31 downto 0*/ +#define IFX_SSC_TB_readmask 0xFFFFFFFF +#define IFX_SSC_TB_writemask 0xFFFFFFFF +#define IFX_SSC_TB_hwmask 0x00000000 +#define IFX_SSC_TB_dontcare (IFX_SSC_TB_readmask & IFX_SSC_TB_writemask & ~IFX_SSC_TB_hwmask) + +/* address of the Reciver Buffer Register of the SSC */ +#define IFX_SSC_RB 0x00000024 +/* IFX_SSC_RB register is significant in no bits*/ +#define IFX_SSC_RB_readmask 0xFFFFFFFF +#define IFX_SSC_RB_writemask 0x00000000 +#define IFX_SSC_RB_hwmask 0xFFFFFFFF +#define IFX_SSC_RB_dontcare (IFX_SSC_RB_readmask & IFX_SSC_RB_writemask & ~IFX_SSC_RB_hwmask) + +/* address of the Receive FIFO Control Register of the SSC */ +#define IFX_SSC_RXFCON 0x00000030 +/* IFX_SSC_RXFCON register is significant in bit 13 downto 8 and bit 1 downto 0 */ +#define IFX_SSC_RXFCON_readmask 0x00003F03 +#define IFX_SSC_RXFCON_writemask 0x00003F03 +#define IFX_SSC_RXFCON_hwmask 0x00000000 +#define IFX_SSC_RXFCON_dontcare (IFX_SSC_RXFCON_readmask & IFX_SSC_RXFCON_writemask & ~IFX_SSC_RXFCON_hwmask) + +/* address of the Transmit FIFO Control Register of the SSC */ +#define IFX_SSC_TXFCON 0x00000034 +/* IFX_SSC_TXFCON register is significant in bit 13 downto 8 and bit 1 downto 0 */ +#define IFX_SSC_TXFCON_readmask 0x00003F03 +#define IFX_SSC_TXFCON_writemask 0x00003F03 +#define IFX_SSC_TXFCON_hwmask 0x00000000 +#define IFX_SSC_TXFCON_dontcare (IFX_SSC_TXFCON_readmask & IFX_SSC_TXFCON_writemask & ~IFX_SSC_TXFCON_hwmask) + +/* address of the FIFO Status Register of the SSC */ +#define IFX_SSC_FSTAT 0x00000038 +/* IFX_SSC_FSTAT register is significant in no bit*/ +#define IFX_SSC_FSTAT_readmask 0x00003F3F +#define IFX_SSC_FSTAT_writemask 0x00000000 +#define IFX_SSC_FSTAT_hwmask 0x00003F3F +#define IFX_SSC_FSTAT_dontcare (IFX_SSC_FSTAT_readmask & IFX_SSC_FSTAT_writemask & ~IFX_SSC_FSTAT_hwmask) + +/* address of the Data Frame Control register of the SSC */ +#define IFX_SSC_SFCON 0x00000060 +#define IFX_SSC_SFCON_readmask 0xFFDFFFFD +#define IFX_SSC_SFCON_writemask 0xFFDFFFFD +#define IFX_SSC_SFCON_hwmask 0x00000000 +#define IFX_SSC_SFCON_dontcare (IFX_SSC_SFCON_readmask & IFX_SSC_SFCON_writemask & ~IFX_SSC_SFCON_hwmask) + +/* address of the Data Frame Status register of the SSC */ +#define IFX_SSC_SFSTAT 0x00000064 +#define IFX_SSC_SFSTAT_readmask 0xFFC0FFF3 +#define IFX_SSC_SFSTAT_writemask 0x00000000 +#define IFX_SSC_SFSTAT_hwmask 0xFFC0FFF3 +#define IFX_SSC_SFSTAT_dontcare (IFX_SSC_SFSTAT_readmask & IFX_SSC_SFSTAT_writemask & ~IFX_SSC_SFSTAT_hwmask) + +/* address of the General Purpose Output Control register of the SSC */ +#define IFX_SSC_GPOCON 0x00000070 +#define IFX_SSC_GPOCON_readmask 0x0000FFFF +#define IFX_SSC_GPOCON_writemask 0x0000FFFF +#define IFX_SSC_GPOCON_hwmask 0x00000000 +#define IFX_SSC_GPOCON_dontcare (IFX_SSC_GPOCON_readmask & IFX_SSC_GPOCON_writemask & ~IFX_SSC_GPOCON_hwmask) + +/* address of the General Purpose Output Status register of the SSC */ +#define IFX_SSC_GPOSTAT 0x00000074 +#define IFX_SSC_GPOSTAT_readmask 0x000000FF +#define IFX_SSC_GPOSTAT_writemask 0x00000000 +#define IFX_SSC_GPOSTAT_hwmask 0x00000000 +#define IFX_SSC_GPOSTAT_dontcare (IFX_SSC_GPOSTAT_readmask & IFX_SSC_GPOSTAT_writemask & ~IFX_SSC_GPOSTAT_hwmask) + +/* address of the Force GPO Status register of the SSC */ +#define IFX_SSC_WHBGPOSTAT 0x00000078 +#define IFX_SSC_WHBGPOSTAT_readmask 0x00000000 +#define IFX_SSC_WHBGPOSTAT_writemask 0x0000FFFF +#define IFX_SSC_WHBGPOSTAT_hwmask 0x00000000 +#define IFX_SSC_WHBGPOSTAT_dontcare (IFX_SSC_WHBGPOSTAT_readmask & IFX_SSC_WHBGPOSTAT_writemask & ~IFX_SSC_WHBGPOSTAT_hwmask) + +/* address of the Receive Request Register of the SSC */ +#define IFX_SSC_RXREQ 0x00000080 +#define IFX_SSC_RXREQ_readmask 0x0000FFFF +#define IFX_SSC_RXREQ_writemask 0x0000FFFF +#define IFX_SSC_RXREQ_hwmask 0x00000000 +#define IFX_SSC_RXREQ_dontcare (IFX_SSC_RXREQ_readmask & IFX_SSC_RXREQ_writemask & ~IFX_SSC_RXREQ_hwmask) + +/* address of the Receive Count Register of the SSC */ +#define IFX_SSC_RXCNT 0x00000084 +#define IFX_SSC_RXCNT_readmask 0x0000FFFF +#define IFX_SSC_RXCNT_writemask 0x00000000 +#define IFX_SSC_RXCNT_hwmask 0x0000FFFF +#define IFX_SSC_RXCNT_dontcare (IFX_SSC_RXCNT_readmask & IFX_SSC_RXCNT_writemask & ~IFX_SSC_RXCNT_hwmask) + +/* address of the DMA Configuration Register of the SSC */ +#define IFX_SSC_DMACON 0x000000EC +#define IFX_SSC_DMACON_readmask 0x0000FFFF +#define IFX_SSC_DMACON_writemask 0x00000000 +#define IFX_SSC_DMACON_hwmask 0x0000FFFF +#define IFX_SSC_DMACON_dontcare (IFX_SSC_DMACON_readmask & IFX_SSC_DMACON_writemask & ~IFX_SSC_DMACON_hwmask) + +//------------------------------------------------------ +// interrupt register for enabling interrupts, mask register of irq_reg +#define IFX_SSC_IRN_EN 0xF4 +// read/write +#define IFX_SSC_IRN_EN_readmask 0x0000000F +#define IFX_SSC_IRN_EN_writemask 0x0000000F +#define IFX_SSC_IRN_EN_hwmask 0x00000000 +#define IFX_SSC_IRN_EN_dontcare (IFX_SSC_IRN_EN_readmask & IFX_SSC_IRN_EN_writemask & ~IFX_SSC_IRN_EN_hwmask) + +// interrupt register for accessing interrupts +#define IFX_SSC_IRN_CR 0xF8 +// read/write +#define IFX_SSC_IRN_CR_readmask 0x0000000F +#define IFX_SSC_IRN_CR_writemask 0x0000000F +#define IFX_SSC_IRN_CR_hwmask 0x0000000F +#define IFX_SSC_IRN_CR_dontcare (IFX_SSC_IRN_CR_readmask & IFX_SSC_IRN_CR_writemask & ~IFX_SSC_IRN_CR_hwmask) + +// interrupt register for stimulating interrupts +#define IFX_SSC_IRN_ICR 0xFC +// read/write +#define IFX_SSC_IRN_ICR_readmask 0x0000000F +#define IFX_SSC_IRN_ICR_writemask 0x0000000F +#define IFX_SSC_IRN_ICR_hwmask 0x00000000 +#define IFX_SSC_IRN_ICR_dontcare (IFX_SSC_IRN_ICR_readmask & IFX_SSC_IRN_ICR_writemask & ~IFX_SSC_IRN_ICR_hwmask) + +//--------------------------------------------------------------------- +// Number of IRQs and bitposition of IRQ +#define IFX_SSC_NUM_IRQ 4 +#define IFX_SSC_T_BIT 0x00000001 +#define IFX_SSC_R_BIT 0x00000002 +#define IFX_SSC_E_BIT 0x00000004 +#define IFX_SSC_F_BIT 0x00000008 + +/* bit masks for SSC registers */ + +/* ID register */ +#define IFX_SSC_PERID_REV_MASK 0x0000001F +#define IFX_SSC_PERID_CFG_MASK 0x00000020 +#define IFX_SSC_PERID_ID_MASK 0x0000FF00 +#define IFX_SSC_PERID_REV_OFFSET 0 +#define IFX_SSC_PERID_CFG_OFFSET 5 +#define IFX_SSC_PERID_ID_OFFSET 8 +#define IFX_SSC_PERID_ID 0x45 +#define IFX_SSC_PERID_DMA_ON 0x00000020 +#define IFX_SSC_PERID_RXFS_MASK 0x003F0000 +#define IFX_SSC_PERID_RXFS_OFFSET 16 +#define IFX_SSC_PERID_TXFS_MASK 0x3F000000 +#define IFX_SSC_PERID_TXFS_OFFSET 24 + +/* PISEL register */ +#define IFX_SSC_PISEL_MASTER_IN_A 0x0000 +#define IFX_SSC_PISEL_MASTER_IN_B 0x0001 +#define IFX_SSC_PISEL_SLAVE_IN_A 0x0000 +#define IFX_SSC_PISEL_SLAVE_IN_B 0x0002 +#define IFX_SSC_PISEL_CLOCK_IN_A 0x0000 +#define IFX_SSC_PISEL_CLOCK_IN_B 0x0004 + +/* IFX_SSC_CON register */ +#define IFX_SSC_CON_ECHO_MODE_ON 0x01000000 +#define IFX_SSC_CON_ECHO_MODE_OFF 0x00000000 +#define IFX_SSC_CON_IDLE_HIGH 0x00800000 +#define IFX_SSC_CON_IDLE_LOW 0x00000000 +#define IFX_SSC_CON_ENABLE_BYTE_VALID 0x00400000 +#define IFX_SSC_CON_DISABLE_BYTE_VALID 0x00000000 +#define IFX_SSC_CON_DATA_WIDTH_OFFSET 16 +#define IFX_SSC_CON_DATA_WIDTH_MASK 0x001F0000 +#define IFX_SSC_ENCODE_DATA_WIDTH(width) (((width - 1) << IFX_SSC_CON_DATA_WIDTH_OFFSET) & IFX_SSC_CON_DATA_WIDTH_MASK) + +#define IFX_SSC_CON_RESET_ON_BAUDERR 0x00002000 +#define IFX_SSC_CON_GO_ON_ON_BAUDERR 0x00000000 + +#define IFX_SSC_CON_RX_UFL_CHECK 0x00001000 +#define IFX_SSC_CON_RX_UFL_IGNORE 0x00000000 +#define IFX_SSC_CON_TX_UFL_CHECK 0x00000800 +#define IFX_SSC_CON_TX_UFL_IGNORE 0x00000000 +#define IFX_SSC_CON_ABORT_ERR_CHECK 0x00000400 +#define IFX_SSC_CON_ABORT_ERR_IGNORE 0x00000000 +#define IFX_SSC_CON_RX_OFL_CHECK 0x00000200 +#define IFX_SSC_CON_RX_OFL_IGNORE 0x00000000 +#define IFX_SSC_CON_TX_OFL_CHECK 0x00000100 +#define IFX_SSC_CON_TX_OFL_IGNORE 0x00000000 +#define IFX_SSC_CON_ALL_ERR_CHECK 0x00001F00 +#define IFX_SSC_CON_ALL_ERR_IGNORE 0x00000000 + +#define IFX_SSC_CON_LOOPBACK_MODE 0x00000080 +#define IFX_SSC_CON_NO_LOOPBACK 0x00000000 +#define IFX_SSC_CON_HALF_DUPLEX 0x00000080 +#define IFX_SSC_CON_FULL_DUPLEX 0x00000000 +#define IFX_SSC_CON_CLOCK_FALL 0x00000040 +#define IFX_SSC_CON_CLOCK_RISE 0x00000000 +#define IFX_SSC_CON_SHIFT_THEN_LATCH 0x00000000 +#define IFX_SSC_CON_LATCH_THEN_SHIFT 0x00000020 +#define IFX_SSC_CON_MSB_FIRST 0x00000010 +#define IFX_SSC_CON_LSB_FIRST 0x00000000 +#define IFX_SSC_CON_ENABLE_CSB 0x00000008 +#define IFX_SSC_CON_DISABLE_CSB 0x00000000 +#define IFX_SSC_CON_INVERT_CSB 0x00000004 +#define IFX_SSC_CON_TRUE_CSB 0x00000000 +#define IFX_SSC_CON_RX_OFF 0x00000002 +#define IFX_SSC_CON_RX_ON 0x00000000 +#define IFX_SSC_CON_TX_OFF 0x00000001 +#define IFX_SSC_CON_TX_ON 0x00000000 + +/* IFX_SSC_STATE register */ +#define IFX_SSC_STATE_RX_BYTE_VALID_OFFSET 28 +#define IFX_SSC_STATE_RX_BYTE_VALID_MASK 0x70000000 +#define IFX_SSC_DECODE_RX_BYTE_VALID(con_state) ((con_state & IFX_SSC_STATE_RX_BYTE_VALID_MASK) >> IFX_SSC_STATE_RX_BYTE_VALID_OFFSET) +#define IFX_SSC_STATE_TX_BYTE_VALID_OFFSET 24 +#define IFX_SSC_STATE_TX_BYTE_VALID_MASK 0x07000000 +#define IFX_SSC_DECODE_TX_BYTE_VALID(con_state) ((con_state & IFX_SSC_STATE_TX_BYTE_VALID_MASK) >> IFX_SSC_STATE_TX_BYTE_VALID_OFFSET) +#define IFX_SSC_STATE_BIT_COUNT_OFFSET 16 +#define IFX_SSC_STATE_BIT_COUNT_MASK 0x001F0000 +#define IFX_SSC_DECODE_DATA_WIDTH(con_state) (((con_state & IFX_SSC_STATE_BIT_COUNT_MASK) >> IFX_SSC_STATE_BIT_COUNT_OFFSET) + 1) +#define IFX_SSC_STATE_BUSY 0x00002000 +#define IFX_SSC_STATE_RX_UFL 0x00001000 +#define IFX_SSC_STATE_TX_UFL 0x00000800 +#define IFX_SSC_STATE_ABORT_ERR 0x00000400 +#define IFX_SSC_STATE_RX_OFL 0x00000200 +#define IFX_SSC_STATE_TX_OFL 0x00000100 +#define IFX_SSC_STATE_MODE_ERR 0x00000080 +#define IFX_SSC_STATE_SLAVE_IS_SELECTED 0x00000004 +#define IFX_SSC_STATE_IS_MASTER 0x00000002 +#define IFX_SSC_STATE_IS_ENABLED 0x00000001 + +/* WHBSTATE register */ +#define IFX_SSC_WHBSTATE_DISABLE_SSC 0x0001 +#define IFX_SSC_WHBSTATE_CONFIGURATION_MODE 0x0001 +#define IFX_SSC_WHBSTATE_CLR_ENABLE 0x0001 + +#define IFX_SSC_WHBSTATE_ENABLE_SSC 0x0002 +#define IFX_SSC_WHBSTATE_RUN_MODE 0x0002 +#define IFX_SSC_WHBSTATE_SET_ENABLE 0x0002 + +#define IFX_SSC_WHBSTATE_SLAVE_MODE 0x0004 +#define IFX_SSC_WHBSTATE_CLR_MASTER_SELECT 0x0004 + +#define IFX_SSC_WHBSTATE_MASTER_MODE 0x0008 +#define IFX_SSC_WHBSTATE_SET_MASTER_SELECT 0x0008 + +#define IFX_SSC_WHBSTATE_CLR_RX_UFL_ERROR 0x0010 +#define IFX_SSC_WHBSTATE_SET_RX_UFL_ERROR 0x0020 + +#define IFX_SSC_WHBSTATE_CLR_MODE_ERROR 0x0040 +#define IFX_SSC_WHBSTATE_SET_MODE_ERROR 0x0080 + +#define IFX_SSC_WHBSTATE_CLR_TX_OFL_ERROR 0x0100 +#define IFX_SSC_WHBSTATE_CLR_RX_OFL_ERROR 0x0200 +#define IFX_SSC_WHBSTATE_CLR_ABORT_ERROR 0x0400 +#define IFX_SSC_WHBSTATE_CLR_TX_UFL_ERROR 0x0800 +#define IFX_SSC_WHBSTATE_SET_TX_OFL_ERROR 0x1000 +#define IFX_SSC_WHBSTATE_SET_RX_OFL_ERROR 0x2000 +#define IFX_SSC_WHBSTATE_SET_ABORT_ERROR 0x4000 +#define IFX_SSC_WHBSTATE_SET_TX_UFL_ERROR 0x8000 +#define IFX_SSC_WHBSTATE_CLR_ALL_ERROR 0x0F50 +#define IFX_SSC_WHBSTATE_SET_ALL_ERROR 0xF0A0 + +/* BR register */ +#define IFX_SSC_BR_BAUDRATE_OFFSET 0 +#define IFX_SSC_BR_BAUDRATE_MASK 0xFFFF + +/* BR_STAT register */ +#define IFX_SSC_BRSTAT_BAUDTIMER_OFFSET 0 +#define IFX_SSC_BRSTAT_BAUDTIMER_MASK 0xFFFF + +/* TB register */ +#define IFX_SSC_TB_DATA_OFFSET 0 +#define IFX_SSC_TB_DATA_MASK 0xFFFFFFFF + +/* RB register */ +#define IFX_SSC_RB_DATA_OFFSET 0 +#define IFX_SSC_RB_DATA_MASK 0xFFFFFFFF + +/* RXFCON and TXFCON registers */ +#define IFX_SSC_XFCON_FIFO_DISABLE 0x0000 +#define IFX_SSC_XFCON_FIFO_ENABLE 0x0001 +#define IFX_SSC_XFCON_FIFO_FLUSH 0x0002 +#define IFX_SSC_XFCON_ITL_MASK 0x00003F00 +#define IFX_SSC_XFCON_ITL_OFFSET 8 + +/* FSTAT register */ +#define IFX_SSC_FSTAT_RECEIVED_WORDS_OFFSET 0 +#define IFX_SSC_FSTAT_RECEIVED_WORDS_MASK 0x003F +#define IFX_SSC_FSTAT_TRANSMIT_WORDS_OFFSET 8 +#define IFX_SSC_FSTAT_TRANSMIT_WORDS_MASK 0x3F00 + +/* GPOCON register */ +#define IFX_SSC_GPOCON_INVOUT0_POS 0 +#define IFX_SSC_GPOCON_INV_OUT0 0x00000001 +#define IFX_SSC_GPOCON_TRUE_OUT0 0x00000000 +#define IFX_SSC_GPOCON_INVOUT1_POS 1 +#define IFX_SSC_GPOCON_INV_OUT1 0x00000002 +#define IFX_SSC_GPOCON_TRUE_OUT1 0x00000000 +#define IFX_SSC_GPOCON_INVOUT2_POS 2 +#define IFX_SSC_GPOCON_INV_OUT2 0x00000003 +#define IFX_SSC_GPOCON_TRUE_OUT2 0x00000000 +#define IFX_SSC_GPOCON_INVOUT3_POS 3 +#define IFX_SSC_GPOCON_INV_OUT3 0x00000008 +#define IFX_SSC_GPOCON_TRUE_OUT3 0x00000000 +#define IFX_SSC_GPOCON_INVOUT4_POS 4 +#define IFX_SSC_GPOCON_INV_OUT4 0x00000010 +#define IFX_SSC_GPOCON_TRUE_OUT4 0x00000000 +#define IFX_SSC_GPOCON_INVOUT5_POS 5 +#define IFX_SSC_GPOCON_INV_OUT5 0x00000020 +#define IFX_SSC_GPOCON_TRUE_OUT5 0x00000000 +#define IFX_SSC_GPOCON_INVOUT6_POS 6 +#define IFX_SSC_GPOCON_INV_OUT6 0x00000040 +#define IFX_SSC_GPOCON_TRUE_OUT6 0x00000000 +#define IFX_SSC_GPOCON_INVOUT7_POS 7 +#define IFX_SSC_GPOCON_INV_OUT7 0x00000080 +#define IFX_SSC_GPOCON_TRUE_OUT7 0x00000000 +#define IFX_SSC_GPOCON_INV_OUT_ALL 0x000000FF +#define IFX_SSC_GPOCON_TRUE_OUT_ALL 0x00000000 + +#define IFX_SSC_GPOCON_ISCSB0_POS 8 +#define IFX_SSC_GPOCON_IS_CSB0 0x00000100 +#define IFX_SSC_GPOCON_IS_GPO0 0x00000000 +#define IFX_SSC_GPOCON_ISCSB1_POS 9 +#define IFX_SSC_GPOCON_IS_CSB1 0x00000200 +#define IFX_SSC_GPOCON_IS_GPO1 0x00000000 +#define IFX_SSC_GPOCON_ISCSB2_POS 10 +#define IFX_SSC_GPOCON_IS_CSB2 0x00000400 +#define IFX_SSC_GPOCON_IS_GPO2 0x00000000 +#define IFX_SSC_GPOCON_ISCSB3_POS 11 +#define IFX_SSC_GPOCON_IS_CSB3 0x00000800 +#define IFX_SSC_GPOCON_IS_GPO3 0x00000000 +#define IFX_SSC_GPOCON_ISCSB4_POS 12 +#define IFX_SSC_GPOCON_IS_CSB4 0x00001000 +#define IFX_SSC_GPOCON_IS_GPO4 0x00000000 +#define IFX_SSC_GPOCON_ISCSB5_POS 13 +#define IFX_SSC_GPOCON_IS_CSB5 0x00002000 +#define IFX_SSC_GPOCON_IS_GPO5 0x00000000 +#define IFX_SSC_GPOCON_ISCSB6_POS 14 +#define IFX_SSC_GPOCON_IS_CSB6 0x00004000 +#define IFX_SSC_GPOCON_IS_GPO6 0x00000000 +#define IFX_SSC_GPOCON_ISCSB7_POS 15 +#define IFX_SSC_GPOCON_IS_CSB7 0x00008000 +#define IFX_SSC_GPOCON_IS_GPO7 0x00000000 +#define IFX_SSC_GPOCON_IS_CSB_ALL 0x0000FF00 +#define IFX_SSC_GPOCON_IS_GPO_ALL 0x00000000 + +/* GPOSTAT register */ +#define IFX_SSC_GPOSTAT_OUT0 0x00000001 +#define IFX_SSC_GPOSTAT_OUT1 0x00000002 +#define IFX_SSC_GPOSTAT_OUT2 0x00000004 +#define IFX_SSC_GPOSTAT_OUT3 0x00000008 +#define IFX_SSC_GPOSTAT_OUT4 0x00000010 +#define IFX_SSC_GPOSTAT_OUT5 0x00000020 +#define IFX_SSC_GPOSTAT_OUT6 0x00000040 +#define IFX_SSC_GPOSTAT_OUT7 0x00000080 +#define IFX_SSC_GPOSTAT_OUT_ALL 0x000000FF + +/* WHBGPOSTAT register */ +#define IFX_SSC_WHBGPOSTAT_CLROUT0_POS 0 +#define IFX_SSC_WHBGPOSTAT_CLR_OUT0 0x00000001 +#define IFX_SSC_WHBGPOSTAT_CLROUT1_POS 1 +#define IFX_SSC_WHBGPOSTAT_CLR_OUT1 0x00000002 +#define IFX_SSC_WHBGPOSTAT_CLROUT2_POS 2 +#define IFX_SSC_WHBGPOSTAT_CLR_OUT2 0x00000004 +#define IFX_SSC_WHBGPOSTAT_CLROUT3_POS 3 +#define IFX_SSC_WHBGPOSTAT_CLR_OUT3 0x00000008 +#define IFX_SSC_WHBGPOSTAT_CLROUT4_POS 4 +#define IFX_SSC_WHBGPOSTAT_CLR_OUT4 0x00000010 +#define IFX_SSC_WHBGPOSTAT_CLROUT5_POS 5 +#define IFX_SSC_WHBGPOSTAT_CLR_OUT5 0x00000020 +#define IFX_SSC_WHBGPOSTAT_CLROUT6_POS 6 +#define IFX_SSC_WHBGPOSTAT_CLR_OUT6 0x00000040 +#define IFX_SSC_WHBGPOSTAT_CLROUT7_POS 7 +#define IFX_SSC_WHBGPOSTAT_CLR_OUT7 0x00000080 +#define IFX_SSC_WHBGPOSTAT_CLR_OUT_ALL 0x000000FF + +#define IFX_SSC_WHBGPOSTAT_OUT0_POS 0 +#define IFX_SSC_WHBGPOSTAT_OUT1_POS 1 +#define IFX_SSC_WHBGPOSTAT_OUT2_POS 2 +#define IFX_SSC_WHBGPOSTAT_OUT3_POS 3 +#define IFX_SSC_WHBGPOSTAT_OUT4_POS 4 +#define IFX_SSC_WHBGPOSTAT_OUT5_POS 5 +#define IFX_SSC_WHBGPOSTAT_OUT6_POS 6 +#define IFX_SSC_WHBGPOSTAT_OUT7_POS 7 + +#define IFX_SSC_WHBGPOSTAT_SETOUT0_POS 8 +#define IFX_SSC_WHBGPOSTAT_SET_OUT0 0x00000100 +#define IFX_SSC_WHBGPOSTAT_SETOUT1_POS 9 +#define IFX_SSC_WHBGPOSTAT_SET_OUT1 0x00000200 +#define IFX_SSC_WHBGPOSTAT_SETOUT2_POS 10 +#define IFX_SSC_WHBGPOSTAT_SET_OUT2 0x00000400 +#define IFX_SSC_WHBGPOSTAT_SETOUT3_POS 11 +#define IFX_SSC_WHBGPOSTAT_SET_OUT3 0x00000800 +#define IFX_SSC_WHBGPOSTAT_SETOUT4_POS 12 +#define IFX_SSC_WHBGPOSTAT_SET_OUT4 0x00001000 +#define IFX_SSC_WHBGPOSTAT_SETOUT5_POS 13 +#define IFX_SSC_WHBGPOSTAT_SET_OUT5 0x00002000 +#define IFX_SSC_WHBGPOSTAT_SETOUT6_POS 14 +#define IFX_SSC_WHBGPOSTAT_SET_OUT6 0x00004000 +#define IFX_SSC_WHBGPOSTAT_SETOUT7_POS 15 +#define IFX_SSC_WHBGPOSTAT_SET_OUT7 0x00008000 +#define IFX_SSC_WHBGPOSTAT_SET_OUT_ALL 0x0000FF00 + +/* SFCON register */ +#define IFX_SSC_SFCON_SF_ENABLE 0x00000001 +#define IFX_SSC_SFCON_SF_DISABLE 0x00000000 +#define IFX_SSC_SFCON_FIR_ENABLE_BEFORE_PAUSE 0x00000004 +#define IFX_SSC_SFCON_FIR_DISABLE_BEFORE_PAUSE 0x00000000 +#define IFX_SSC_SFCON_FIR_ENABLE_AFTER_PAUSE 0x00000008 +#define IFX_SSC_SFCON_FIR_DISABLE_AFTER_PAUSE 0x00000000 +#define IFX_SSC_SFCON_DATA_LENGTH_MASK 0x0000FFF0 +#define IFX_SSC_SFCON_DATA_LENGTH_OFFSET 4 +#define IFX_SSC_SFCON_PAUSE_DATA_MASK 0x00030000 +#define IFX_SSC_SFCON_PAUSE_DATA_OFFSET 16 +#define IFX_SSC_SFCON_PAUSE_DATA_0 0x00000000 +#define IFX_SSC_SFCON_PAUSE_DATA_1 0x00010000 +#define IFX_SSC_SFCON_PAUSE_DATA_IDLE 0x00020000 +#define IFX_SSC_SFCON_PAUSE_CLOCK_MASK 0x000C0000 +#define IFX_SSC_SFCON_PAUSE_CLOCK_OFFSET 18 +#define IFX_SSC_SFCON_PAUSE_CLOCK_0 0x00000000 +#define IFX_SSC_SFCON_PAUSE_CLOCK_1 0x00040000 +#define IFX_SSC_SFCON_PAUSE_CLOCK_IDLE 0x00080000 +#define IFX_SSC_SFCON_PAUSE_CLOCK_RUN 0x000C0000 +#define IFX_SSC_SFCON_STOP_AFTER_PAUSE 0x00100000 +#define IFX_SSC_SFCON_CONTINUE_AFTER_PAUSE 0x00000000 +#define IFX_SSC_SFCON_PAUSE_LENGTH_MASK 0xFFC00000 +#define IFX_SSC_SFCON_PAUSE_LENGTH_OFFSET 22 +#define IFX_SSC_SFCON_DATA_LENGTH_MAX 4096 +#define IFX_SSC_SFCON_PAUSE_LENGTH_MAX 1024 + +#define IFX_SSC_SFCON_EXTRACT_DATA_LENGTH(sfcon) ((sfcon & IFX_SSC_SFCON_DATA_LENGTH_MASK) >> IFX_SSC_SFCON_DATA_LENGTH_OFFSET) +#define IFX_SSC_SFCON_EXTRACT_PAUSE_LENGTH(sfcon) ((sfcon & IFX_SSC_SFCON_PAUSE_LENGTH_MASK) >> IFX_SSC_SFCON_PAUSE_LENGTH_OFFSET) +#define IFX_SSC_SFCON_SET_DATA_LENGTH(value) ((value << IFX_SSC_SFCON_DATA_LENGTH_OFFSET) & IFX_SSC_SFCON_DATA_LENGTH_MASK) +#define IFX_SSC_SFCON_SET_PAUSE_LENGTH(value) ((value << IFX_SSC_SFCON_PAUSE_LENGTH_OFFSET) & IFX_SSC_SFCON_PAUSE_LENGTH_MASK) + +/* SFSTAT register */ +#define IFX_SSC_SFSTAT_IN_DATA 0x00000001 +#define IFX_SSC_SFSTAT_IN_PAUSE 0x00000002 +#define IFX_SSC_SFSTAT_DATA_COUNT_MASK 0x0000FFF0 +#define IFX_SSC_SFSTAT_DATA_COUNT_OFFSET 4 +#define IFX_SSC_SFSTAT_PAUSE_COUNT_MASK 0xFFF00000 +#define IFX_SSC_SFSTAT_PAUSE_COUNT_OFFSET 20 + +#define IFX_SSC_SFSTAT_EXTRACT_DATA_COUNT(sfstat) ((sfstat & IFX_SSC_SFSTAT_DATA_COUNT_MASK) >> IFX_SSC_SFSTAT_DATA_COUNT_OFFSET) +#define IFX_SSC_SFSTAT_EXTRACT_PAUSE_COUNT(sfstat) ((sfstat & IFX_SSC_SFSTAT_PAUSE_COUNT_MASK) >> IFX_SSC_SFSTAT_PAUSE_COUNT_OFFSET) + +/* RXREQ register */ +#define IFX_SSC_RXREQ_RXCOUNT_MASK 0x0000FFFF +#define IFX_SSC_RXREQ_RXCOUNT_OFFSET 0 + +/* RXCNT register */ +#define IFX_SSC_RXCNT_TODO_MASK 0x0000FFFF +#define IFX_SSC_RXCNT_TODO_OFFSET 0 + +/* DMACON register */ +#define IFX_SSC_DMACON_RXON 0x00000001 +#define IFX_SSC_DMACON_RXOFF 0x00000000 +#define IFX_SSC_DMACON_TXON 0x00000002 +#define IFX_SSC_DMACON_TXOFF 0x00000000 +#define IFX_SSC_DMACON_DMAON 0x00000003 +#define IFX_SSC_DMACON_DMAOFF 0x00000000 +#define IFX_SSC_DMACON_CLASS_MASK 0x0000000C +#define IFX_SSC_DMACON_CLASS_OFFSET 2 + +/* register access macros */ +#define ifx_ssc_fstat_received_words(status) (status & 0x003F) +#define ifx_ssc_fstat_words_to_transmit(status) ((status & 0x3F00) >> 8) + +#define ifx_ssc_change_status(data, addr) WRITE_PERIPHERAL_REGISTER(data, (PHYS_OFFSET + addr + IFX_SSC_WHBSTATE)) +#define ifx_ssc_set_config(data, addr) WRITE_PERIPHERAL_REGISTER(data, (PHYS_OFFSET + addr + IFX_SSC_CON)) +#define ifx_ssc_get_config(addr) READ_PERIPHERAL_REGISTER((PHYS_OFFSET + addr + IFX_SSC_CON)) +#define ifx_ssc_get_status(addr) READ_PERIPHERAL_REGISTER((PHYS_OFFSET + addr + IFX_SSC_STATE)) +#define ifx_ssc_receive(addr) READ_PERIPHERAL_REGISTER((PHYS_OFFSET + addr + IFX_SSC_RB)) +#define ifx_ssc_transmit(data, addr) WRITE_PERIPHERAL_REGISTER(data, (PHYS_OFFSET + addr + IFX_SSC_TB)) +#define ifx_ssc_fifo_status(addr) READ_PERIPHERAL_REGISTER((PHYS_OFFSET + addr + IFX_SSC_FSTAT)) +#define ifx_ssc_set_baudrate(data, addr) WRITE_PERIPHERAL_REGISTER(data, (PHYS_OFFSET + addr + IFX_SSC_BR)) + +#define ifx_ssc_extract_rx_fifo_size(id) ((id & IFX_SSC_PERID_RXFS_MASK) >> IFX_SSC_PERID_RXFS_OFFSET) +#define ifx_ssc_extract_tx_fifo_size(id) ((id & IFX_SSC_PERID_TXFS_MASK) >> IFX_SSC_PERID_TXFS_OFFSET) + +#endif diff --git a/target/linux/danube/patches/110-drivers.patch b/target/linux/danube/patches/110-drivers.patch index 862504ec01..35a2024bb6 100644 --- a/target/linux/danube/patches/110-drivers.patch +++ b/target/linux/danube/patches/110-drivers.patch @@ -81,12 +81,13 @@ Index: linux-2.6.23/drivers/char/watchdog/Makefile Index: linux-2.6.23/drivers/char/Makefile =================================================================== --- linux-2.6.23.orig/drivers/char/Makefile 2007-12-13 20:41:42.000000000 +0100 -+++ linux-2.6.23/drivers/char/Makefile 2007-12-14 22:30:59.000000000 +0100 -@@ -135,3 +135,7 @@ ++++ linux-2.6.23/drivers/char/Makefile 2007-12-14 22:41:07.000000000 +0100 +@@ -135,3 +135,8 @@ rm $@.tmp endif + +obj-$(CONFIG_DANUBE_LED) += danube_led.o +obj-$(CONFIG_DANUBE_GPIO) += danube_gpio.o ++obj-$(CONFIG_DANUBE_SSC) += danube_ssc.o +obj-$(CONFIG_DANUBE_EEPROM) += danube_eeprom.o -- 2.30.2