diff options
Diffstat (limited to 'arch/cris/arch-v10/drivers')
-rw-r--r-- | arch/cris/arch-v10/drivers/Kconfig | 181 | ||||
-rw-r--r-- | arch/cris/arch-v10/drivers/Makefile | 12 | ||||
-rw-r--r-- | arch/cris/arch-v10/drivers/axisflashmap.c | 181 | ||||
-rw-r--r-- | arch/cris/arch-v10/drivers/ds1302.c | 4 | ||||
-rw-r--r-- | arch/cris/arch-v10/drivers/eeprom.c | 75 | ||||
-rw-r--r-- | arch/cris/arch-v10/drivers/gpio.c | 634 | ||||
-rw-r--r-- | arch/cris/arch-v10/drivers/i2c.c | 81 | ||||
-rw-r--r-- | arch/cris/arch-v10/drivers/pcf8563.c | 393 | ||||
-rw-r--r-- | arch/cris/arch-v10/drivers/sync_serial.c | 1441 |
9 files changed, 1961 insertions, 1041 deletions
diff --git a/arch/cris/arch-v10/drivers/Kconfig b/arch/cris/arch-v10/drivers/Kconfig index 96740ef497d..58f5864a668 100644 --- a/arch/cris/arch-v10/drivers/Kconfig +++ b/arch/cris/arch-v10/drivers/Kconfig @@ -9,37 +9,6 @@ config ETRAX_ETHERNET This option enables the ETRAX 100LX built-in 10/100Mbit Ethernet controller. -choice - prompt "Network LED behavior" - depends on ETRAX_ETHERNET - default ETRAX_NETWORK_LED_ON_WHEN_ACTIVITY - -config ETRAX_NETWORK_LED_ON_WHEN_LINK - bool "LED_on_when_link" - help - Selecting LED_on_when_link will light the LED when there is a - connection and will flash off when there is activity. - - Selecting LED_on_when_activity will light the LED only when - there is activity. - - This setting will also affect the behaviour of other activity LEDs - e.g. Bluetooth. - -config ETRAX_NETWORK_LED_ON_WHEN_ACTIVITY - bool "LED_on_when_activity" - help - Selecting LED_on_when_link will light the LED when there is a - connection and will flash off when there is activity. - - Selecting LED_on_when_activity will light the LED only when - there is activity. - - This setting will also affect the behaviour of other activity LEDs - e.g. Bluetooth. - -endchoice - config ETRAX_SERIAL bool "Serial-port support" depends on ETRAX_ARCH_V10 @@ -84,32 +53,6 @@ config ETRAX_SERIAL_PORT0 the same DMA channels. choice - prompt "Ser0 DMA out assignment" - depends on ETRAX_SERIAL_PORT0 - default ETRAX_SERIAL_PORT0_DMA6_OUT - -config ETRAX_SERIAL_PORT0_NO_DMA_OUT - bool "No DMA out" - -config ETRAX_SERIAL_PORT0_DMA6_OUT - bool "DMA 6" - -endchoice - -choice - prompt "Ser0 DMA in assignment" - depends on ETRAX_SERIAL_PORT0 - default ETRAX_SERIAL_PORT0_DMA7_IN - -config ETRAX_SERIAL_PORT0_NO_DMA_IN - bool "No DMA in" - -config ETRAX_SERIAL_PORT0_DMA7_IN - bool "DMA 7" - -endchoice - -choice prompt "Ser0 DTR, RI, DSR and CD assignment" depends on ETRAX_SERIAL_PORT0 default ETRAX_SER0_DTR_RI_DSR_CD_ON_NONE @@ -198,32 +141,6 @@ config ETRAX_SERIAL_PORT1 Enables the ETRAX 100 serial driver for ser1 (ttyS1). choice - prompt "Ser1 DMA out assignment" - depends on ETRAX_SERIAL_PORT1 - default ETRAX_SERIAL_PORT1_DMA8_OUT - -config ETRAX_SERIAL_PORT1_NO_DMA_OUT - bool "No DMA out" - -config ETRAX_SERIAL_PORT1_DMA8_OUT - bool "DMA 8" - -endchoice - -choice - prompt "Ser1 DMA in assignment" - depends on ETRAX_SERIAL_PORT1 - default ETRAX_SERIAL_PORT1_DMA9_IN - -config ETRAX_SERIAL_PORT1_NO_DMA_IN - bool "No DMA in" - -config ETRAX_SERIAL_PORT1_DMA9_IN - bool "DMA 9" - -endchoice - -choice prompt "Ser1 DTR, RI, DSR and CD assignment" depends on ETRAX_SERIAL_PORT1 default ETRAX_SER1_DTR_RI_DSR_CD_ON_NONE @@ -315,32 +232,6 @@ config ETRAX_SERIAL_PORT2 Enables the ETRAX 100 serial driver for ser2 (ttyS2). choice - prompt "Ser2 DMA out assignment" - depends on ETRAX_SERIAL_PORT2 - default ETRAX_SERIAL_PORT2_DMA2_OUT - -config ETRAX_SERIAL_PORT2_NO_DMA_OUT - bool "No DMA out" - -config ETRAX_SERIAL_PORT2_DMA2_OUT - bool "DMA 2" - -endchoice - -choice - prompt "Ser2 DMA in assignment" - depends on ETRAX_SERIAL_PORT2 - default ETRAX_SERIAL_PORT2_DMA3_IN - -config ETRAX_SERIAL_PORT2_NO_DMA_IN - bool "No DMA in" - -config ETRAX_SERIAL_PORT2_DMA3_IN - bool "DMA 3" - -endchoice - -choice prompt "Ser2 DTR, RI, DSR and CD assignment" depends on ETRAX_SERIAL_PORT2 default ETRAX_SER2_DTR_RI_DSR_CD_ON_NONE @@ -429,32 +320,6 @@ config ETRAX_SERIAL_PORT3 Enables the ETRAX 100 serial driver for ser3 (ttyS3). choice - prompt "Ser3 DMA out assignment" - depends on ETRAX_SERIAL_PORT3 - default ETRAX_SERIAL_PORT3_DMA4_OUT - -config ETRAX_SERIAL_PORT3_NO_DMA_OUT - bool "No DMA out" - -config ETRAX_SERIAL_PORT3_DMA4_OUT - bool "DMA 4" - -endchoice - -choice - prompt "Ser3 DMA in assignment" - depends on ETRAX_SERIAL_PORT3 - default ETRAX_SERIAL_PORT3_DMA5_IN - -config ETRAX_SERIAL_PORT3_NO_DMA_IN - bool "No DMA in" - -config ETRAX_SERIAL_PORT3_DMA5_IN - bool "DMA 5" - -endchoice - -choice prompt "Ser3 DTR, RI, DSR and CD assignment" depends on ETRAX_SERIAL_PORT3 default ETRAX_SER3_DTR_RI_DSR_CD_ON_NONE @@ -563,21 +428,6 @@ config ETRAX_USB_HOST_PORT2 depends on ETRAX_USB_HOST default n -config ETRAX_AXISFLASHMAP - bool "Axis flash-map support" - depends on ETRAX_ARCH_V10 - select MTD - select MTD_CFI - select MTD_CFI_AMDSTD - select MTD_CHAR - select MTD_BLOCK - select MTD_PARTITIONS - select MTD_CONCAT - select MTD_COMPLEX_MAPPINGS - help - This option enables MTD mapping of flash devices. Needed to use - flash memories. If unsure, say Y. - config ETRAX_PTABLE_SECTOR int "Byte-offset of partition table sector" depends on ETRAX_AXISFLASHMAP @@ -731,37 +581,6 @@ config ETRAX_PB_CHANGEABLE_BITS Bit set = changeable. You probably want 00 here. -config ETRAX_RTC - bool "Real Time Clock support" - depends on ETRAX_ARCH_V10 - help - Enables drivers for the Real-Time Clock battery-backed chips on - some products. The kernel reads the time when booting, and - the date can be set using ioctl(fd, RTC_SET_TIME, &rt) with rt a - rtc_time struct (see <file:include/asm-cris/rtc.h>) on the /dev/rtc - device, major 121. You can check the time with cat /proc/rtc, but - normal time reading should be done using libc function time and - friends. - -choice - prompt "RTC chip" - depends on ETRAX_RTC - default ETRAX_DS1302 - -config ETRAX_DS1302 - bool "DS1302" - help - Enables the driver for the DS1302 Real-Time Clock battery-backed - chip on some products. - -config ETRAX_PCF8563 - bool "PCF8563" - help - Enables the driver for the PCF8563 Real-Time Clock battery-backed - chip on some products. - -endchoice - config ETRAX_DS1302_RST_ON_GENERIC_PORT bool "DS1302 RST on Generic Port" depends on ETRAX_DS1302 diff --git a/arch/cris/arch-v10/drivers/Makefile b/arch/cris/arch-v10/drivers/Makefile index 20258e36f38..44bf2e88c26 100644 --- a/arch/cris/arch-v10/drivers/Makefile +++ b/arch/cris/arch-v10/drivers/Makefile @@ -2,11 +2,11 @@ # Makefile for Etrax-specific drivers # -obj-$(CONFIG_ETRAX_AXISFLASHMAP) += axisflashmap.o -obj-$(CONFIG_ETRAX_I2C) += i2c.o -obj-$(CONFIG_ETRAX_I2C_EEPROM) += eeprom.o -obj-$(CONFIG_ETRAX_GPIO) += gpio.o -obj-$(CONFIG_ETRAX_DS1302) += ds1302.o +obj-$(CONFIG_ETRAX_AXISFLASHMAP) += axisflashmap.o +obj-$(CONFIG_ETRAX_I2C) += i2c.o +obj-$(CONFIG_ETRAX_I2C_EEPROM) += eeprom.o +obj-$(CONFIG_ETRAX_GPIO) += gpio.o +obj-$(CONFIG_ETRAX_DS1302) += ds1302.o obj-$(CONFIG_ETRAX_PCF8563) += pcf8563.o - +obj-$(CONFIG_ETRAX_SYNCHRONOUS_SERIAL) += sync_serial.o diff --git a/arch/cris/arch-v10/drivers/axisflashmap.c b/arch/cris/arch-v10/drivers/axisflashmap.c index ea3cf2e39a1..b3bdda93ffe 100644 --- a/arch/cris/arch-v10/drivers/axisflashmap.c +++ b/arch/cris/arch-v10/drivers/axisflashmap.c @@ -10,129 +10,6 @@ * tells us what other partitions to define. If there isn't, we use a default * partition split defined below. * - * $Log: axisflashmap.c,v $ - * Revision 1.11 2004/11/15 10:27:14 starvik - * Corrected typo (Thanks to Milton Miller <miltonm@bga.com>). - * - * Revision 1.10 2004/08/16 12:37:22 starvik - * Merge of Linux 2.6.8 - * - * Revision 1.8 2004/05/14 07:58:03 starvik - * Merge of changes from 2.4 - * - * Revision 1.6 2003/07/04 08:27:37 starvik - * Merge of Linux 2.5.74 - * - * Revision 1.5 2002/12/11 13:13:57 starvik - * Added arch/ to v10 specific includes - * Added fix from Linux 2.4 in serial.c (flush_to_flip_buffer) - * - * Revision 1.4 2002/11/20 11:56:10 starvik - * Merge of Linux 2.5.48 - * - * Revision 1.3 2002/11/13 14:54:13 starvik - * Copied from linux 2.4 - * - * Revision 1.28 2002/10/01 08:08:43 jonashg - * The first partition ends at the start of the partition table. - * - * Revision 1.27 2002/08/21 09:23:13 jonashg - * Speling. - * - * Revision 1.26 2002/08/21 08:35:20 jonashg - * Cosmetic change to printouts. - * - * Revision 1.25 2002/08/21 08:15:42 jonashg - * Made it compile even without CONFIG_MTD_CONCAT defined. - * - * Revision 1.24 2002/08/20 13:12:35 jonashg - * * New approach to probing. Probe cse0 and cse1 separately and (mtd)concat - * the results. - * * Removed compile time tests concerning how the mtdram driver has been - * configured. The user will know about the misconfiguration at runtime - * instead. (The old approach made it impossible to use mtdram for anything - * else than RAM boot). - * - * Revision 1.23 2002/05/13 12:12:28 johana - * Allow compile without CONFIG_MTD_MTDRAM but warn at compiletime and - * be informative at runtime. - * - * Revision 1.22 2002/05/13 10:24:44 johana - * Added #if checks on MTDRAM CONFIG - * - * Revision 1.21 2002/05/06 16:05:20 johana - * Removed debug printout. - * - * Revision 1.20 2002/05/06 16:03:00 johana - * No more cramfs as root hack in generic code. - * It's handled by axisflashmap using mtdram. - * - * Revision 1.19 2002/03/15 17:10:28 bjornw - * Changed comment about cached access since we changed this before - * - * Revision 1.18 2002/03/05 17:06:15 jonashg - * Try amd_flash probe before cfi_probe since amd_flash driver can handle two - * (or more) flash chips of different model and the cfi driver cannot. - * - * Revision 1.17 2001/11/12 19:42:38 pkj - * Fixed compiler warnings. - * - * Revision 1.16 2001/11/08 11:18:58 jonashg - * Always read from uncached address to avoid problems with flushing - * cachelines after write and MTD-erase. No performance loss have been - * seen yet. - * - * Revision 1.15 2001/10/19 12:41:04 jonashg - * Name of probe has changed in MTD. - * - * Revision 1.14 2001/09/21 07:14:10 jonashg - * Made root filesystem (cramfs) use mtdblock driver when booting from flash. - * - * Revision 1.13 2001/08/15 13:57:35 jonashg - * Entire MTD updated to the linux 2.4.7 version. - * - * Revision 1.12 2001/06/11 09:50:30 jonashg - * Oops, 2MB is 0x200000 bytes. - * - * Revision 1.11 2001/06/08 11:39:44 jonashg - * Changed sizes and offsets in axis_default_partitions to use - * CONFIG_ETRAX_PTABLE_SECTOR. - * - * Revision 1.10 2001/05/29 09:42:03 jonashg - * Use macro for end marker length instead of sizeof. - * - * Revision 1.9 2001/05/29 08:52:52 jonashg - * Gave names to the magic fours (size of the ptable end marker). - * - * Revision 1.8 2001/05/28 15:36:20 jonashg - * * Removed old comment about ptable location in flash (it's a CONFIG_ option). - * * Variable ptable was initialized twice to the same value. - * - * Revision 1.7 2001/04/05 13:41:46 markusl - * Updated according to review remarks - * - * Revision 1.6 2001/03/07 09:21:21 bjornw - * No need to waste .data - * - * Revision 1.5 2001/03/06 16:27:01 jonashg - * Probe the entire flash area for flash devices. - * - * Revision 1.4 2001/02/23 12:47:15 bjornw - * Uncached flash in LOW_MAP moved from 0xe to 0x8 - * - * Revision 1.3 2001/02/16 12:11:45 jonashg - * MTD driver amd_flash is now included in MTD CVS repository. - * (It's now in drivers/mtd). - * - * Revision 1.2 2001/02/09 11:12:22 jonashg - * Support for AMD compatible non-CFI flash chips. - * Only tested with Toshiba TC58FVT160 so far. - * - * Revision 1.1 2001/01/12 17:01:18 bjornw - * * Added axisflashmap.c, a physical mapping for MTD that reads and understands - * Axis partition-table format. - * - * */ #include <linux/module.h> @@ -235,7 +112,7 @@ static struct map_info map_cse1 = { }; /* If no partition-table was found, we use this default-set. */ -#define MAX_PARTITIONS 7 +#define MAX_PARTITIONS 7 #define NUM_DEFAULT_PARTITIONS 3 /* @@ -300,6 +177,15 @@ static struct mtd_partition axis_partitions[MAX_PARTITIONS] = { }, }; +#ifdef CONFIG_ETRAX_AXISFLASHMAP_MTD0WHOLE +/* Main flash device */ +static struct mtd_partition main_partition = { + .name = "main", + .size = 0, + .offset = 0 +}; +#endif + /* * Probe a chip select for AMD-compatible (JEDEC) or CFI-compatible flash * chips in that order (because the amd_flash-driver is faster). @@ -316,15 +202,14 @@ static struct mtd_info *probe_cs(struct map_info *map_cs) mtd_cs = do_map_probe("cfi_probe", map_cs); #endif #ifdef CONFIG_MTD_JEDECPROBE - if (!mtd_cs) { + if (!mtd_cs) mtd_cs = do_map_probe("jedec_probe", map_cs); - } #endif return mtd_cs; } -/* +/* * Probe each chip select individually for flash chips. If there are chips on * both cse0 and cse1, the mtd_info structs will be concatenated to one struct * so that MTD partitions can cross chip boundries. @@ -351,7 +236,7 @@ static struct mtd_info *flash_probe(void) if (mtd_cse0 && mtd_cse1) { #ifdef CONFIG_MTD_CONCAT struct mtd_info *mtds[] = { mtd_cse0, mtd_cse1 }; - + /* Since the concatenation layer adds a small overhead we * could try to figure out if the chips in cse0 and cse1 are * identical and reprobe the whole cse0+cse1 window. But since @@ -372,7 +257,7 @@ static struct mtd_info *flash_probe(void) /* The best we can do now is to only use what we found * at cse0. - */ + */ mtd_cse = mtd_cse0; map_destroy(mtd_cse1); } @@ -395,7 +280,7 @@ static int __init init_axis_flash(void) struct partitiontable_head *ptable_head = NULL; struct partitiontable_entry *ptable; int use_default_ptable = 1; /* Until proven otherwise. */ - const char *pmsg = " /dev/flash%d at 0x%08x, size 0x%08x\n"; + const char pmsg[] = " /dev/flash%d at 0x%08x, size 0x%08x\n"; if (!(mymtd = flash_probe())) { /* There's no reason to use this module if no flash chip can @@ -435,7 +320,7 @@ static int __init init_axis_flash(void) unsigned long offset = CONFIG_ETRAX_PTABLE_SECTOR; unsigned char *p; unsigned long csum = 0; - + ptable = (struct partitiontable_entry *) ((unsigned long)ptable_head + sizeof(*ptable_head)); @@ -490,6 +375,16 @@ static int __init init_axis_flash(void) pidx++; } +#ifdef CONFIG_ETRAX_AXISFLASHMAP_MTD0WHOLE + if (mymtd) { + main_partition.size = mymtd->size; + err = add_mtd_partitions(mymtd, &main_partition, 1); + if (err) + panic("axisflashmap: Could not initialize " + "partition for whole main mtd device!\n"); + } +#endif + if (mymtd) { if (use_default_ptable) { printk(KERN_INFO " Using default partition table.\n"); @@ -499,9 +394,8 @@ static int __init init_axis_flash(void) err = add_mtd_partitions(mymtd, axis_partitions, pidx); } - if (err) { + if (err) panic("axisflashmap could not add MTD partitions!\n"); - } } if (!romfs_in_flash) { @@ -515,25 +409,24 @@ static int __init init_axis_flash(void) #else struct mtd_info *mtd_ram; - mtd_ram = kmalloc(sizeof(struct mtd_info), - GFP_KERNEL); - if (!mtd_ram) { + mtd_ram = kmalloc(sizeof(struct mtd_info), GFP_KERNEL); + if (!mtd_ram) panic("axisflashmap couldn't allocate memory for " "mtd_info!\n"); - } printk(KERN_INFO " Adding RAM partition for romfs image:\n"); - printk(pmsg, pidx, romfs_start, romfs_length); - - err = mtdram_init_device(mtd_ram, (void*)romfs_start, - romfs_length, "romfs"); - if (err) { + printk(pmsg, pidx, (unsigned)romfs_start, + (unsigned)romfs_length); + + err = mtdram_init_device(mtd_ram, + (void *)romfs_start, + romfs_length, + "romfs"); + if (err) panic("axisflashmap could not initialize MTD RAM " "device!\n"); - } #endif } - return err; } diff --git a/arch/cris/arch-v10/drivers/ds1302.c b/arch/cris/arch-v10/drivers/ds1302.c index 1d1936a1813..c9aa3904be0 100644 --- a/arch/cris/arch-v10/drivers/ds1302.c +++ b/arch/cris/arch-v10/drivers/ds1302.c @@ -333,7 +333,7 @@ rtc_ioctl(struct inode *inode, struct file *file, unsigned int cmd, ds1302_writereg(RTC_TRICKLECHARGER, tcs_val); return 0; } - case RTC_VLOW_RD: + case RTC_VL_READ: { /* TODO: * Implement voltage low detection support @@ -342,7 +342,7 @@ rtc_ioctl(struct inode *inode, struct file *file, unsigned int cmd, " is not supported\n"); return 0; } - case RTC_VLOW_SET: + case RTC_VL_CLR: { /* TODO: * Nothing to do since Voltage Low detection is not supported diff --git a/arch/cris/arch-v10/drivers/eeprom.c b/arch/cris/arch-v10/drivers/eeprom.c index be35a70798a..f1cac9dc75b 100644 --- a/arch/cris/arch-v10/drivers/eeprom.c +++ b/arch/cris/arch-v10/drivers/eeprom.c @@ -19,77 +19,6 @@ *! Sep 03 1999 Edgar Iglesias Added bail-out stuff if we get interrupted *! in the spin-lock. *! -*! $Log: eeprom.c,v $ -*! Revision 1.12 2005/06/19 17:06:46 starvik -*! Merge of Linux 2.6.12. -*! -*! Revision 1.11 2005/01/26 07:14:46 starvik -*! Applied diff from kernel janitors (Nish Aravamudan). -*! -*! Revision 1.10 2003/09/11 07:29:48 starvik -*! Merge of Linux 2.6.0-test5 -*! -*! Revision 1.9 2003/07/04 08:27:37 starvik -*! Merge of Linux 2.5.74 -*! -*! Revision 1.8 2003/04/09 05:20:47 starvik -*! Merge of Linux 2.5.67 -*! -*! Revision 1.6 2003/02/10 07:19:28 starvik -*! Removed misplaced ; -*! -*! Revision 1.5 2002/12/11 13:13:57 starvik -*! Added arch/ to v10 specific includes -*! Added fix from Linux 2.4 in serial.c (flush_to_flip_buffer) -*! -*! Revision 1.4 2002/11/20 11:56:10 starvik -*! Merge of Linux 2.5.48 -*! -*! Revision 1.3 2002/11/18 13:16:06 starvik -*! Linux 2.5 port of latest 2.4 drivers -*! -*! Revision 1.8 2001/06/15 13:24:29 jonashg -*! * Added verification of pointers from userspace in read and write. -*! * Made busy counter volatile. -*! * Added define for initial write delay. -*! * Removed warnings by using loff_t instead of unsigned long. -*! -*! Revision 1.7 2001/06/14 15:26:54 jonashg -*! Removed test because condition is always true. -*! -*! Revision 1.6 2001/06/14 15:18:20 jonashg -*! Kb -> kB (makes quite a difference if you don't know if you have 2k or 16k). -*! -*! Revision 1.5 2001/06/14 14:39:51 jonashg -*! Forgot to use name when registering the driver. -*! -*! Revision 1.4 2001/06/14 14:35:47 jonashg -*! * Gave driver a name and used it in printk's. -*! * Cleanup. -*! -*! Revision 1.3 2001/03/19 16:04:46 markusl -*! Fixed init of fops struct -*! -*! Revision 1.2 2001/03/19 10:35:07 markusl -*! 2.4 port of eeprom driver -*! -*! Revision 1.8 2000/05/18 10:42:25 edgar -*! Make sure to end write cycle on _every_ write -*! -*! Revision 1.7 2000/01/17 17:41:01 johana -*! Adjusted probing and return -ENOSPC when writing outside EEPROM -*! -*! Revision 1.6 2000/01/17 15:50:36 johana -*! Added adaptive timing adjustments and fixed autoprobing for 2k and 16k(?) -*! EEPROMs -*! -*! Revision 1.5 1999/09/03 15:07:37 edgar -*! Added bail-out check to the spinlock -*! -*! Revision 1.4 1999/09/03 12:11:17 bjornw -*! Proper atomicity (need to use spinlocks, not if's). users -> busy. -*! -*! *! (c) 1999 Axis Communications AB, Lund, Sweden *!*****************************************************************************/ @@ -103,10 +32,10 @@ #include <asm/uaccess.h> #include "i2c.h" -#define D(x) +#define D(x) /* If we should use adaptive timing or not: */ -//#define EEPROM_ADAPTIVE_TIMING +/* #define EEPROM_ADAPTIVE_TIMING */ #define EEPROM_MAJOR_NR 122 /* use a LOCAL/EXPERIMENTAL major for now */ #define EEPROM_MINOR_NR 0 diff --git a/arch/cris/arch-v10/drivers/gpio.c b/arch/cris/arch-v10/drivers/gpio.c index 0d347a70583..68a998bd106 100644 --- a/arch/cris/arch-v10/drivers/gpio.c +++ b/arch/cris/arch-v10/drivers/gpio.c @@ -1,138 +1,11 @@ -/* $Id: gpio.c,v 1.17 2005/06/19 17:06:46 starvik Exp $ - * +/* * Etrax general port I/O device * - * Copyright (c) 1999, 2000, 2001, 2002 Axis Communications AB + * Copyright (c) 1999-2007 Axis Communications AB * * Authors: Bjorn Wesen (initial version) * Ola Knutsson (LED handling) * Johan Adolfsson (read/set directions, write, port G) - * - * $Log: gpio.c,v $ - * Revision 1.17 2005/06/19 17:06:46 starvik - * Merge of Linux 2.6.12. - * - * Revision 1.16 2005/03/07 13:02:29 starvik - * Protect driver global states with spinlock - * - * Revision 1.15 2005/01/05 06:08:55 starvik - * No need to do local_irq_disable after local_irq_save. - * - * Revision 1.14 2004/12/13 12:21:52 starvik - * Added I/O and DMA allocators from Linux 2.4 - * - * Revision 1.12 2004/08/24 07:19:59 starvik - * Whitespace cleanup - * - * Revision 1.11 2004/05/14 07:58:03 starvik - * Merge of changes from 2.4 - * - * Revision 1.9 2003/09/11 07:29:48 starvik - * Merge of Linux 2.6.0-test5 - * - * Revision 1.8 2003/07/04 08:27:37 starvik - * Merge of Linux 2.5.74 - * - * Revision 1.7 2003/01/10 07:44:07 starvik - * init_ioremap is now called by kernel before drivers are initialized - * - * Revision 1.6 2002/12/11 13:13:57 starvik - * Added arch/ to v10 specific includes - * Added fix from Linux 2.4 in serial.c (flush_to_flip_buffer) - * - * Revision 1.5 2002/11/20 11:56:11 starvik - * Merge of Linux 2.5.48 - * - * Revision 1.4 2002/11/18 10:10:05 starvik - * Linux 2.5 port of latest gpio.c from Linux 2.4 - * - * Revision 1.20 2002/10/16 21:16:24 johana - * Added support for PA high level interrupt. - * That gives 2ms response time with iodtest for high levels and 2-12 ms - * response time on low levels if the check is not made in - * process.c:cpu_idle() as well. - * - * Revision 1.19 2002/10/14 18:27:33 johana - * Implemented alarm handling so select() now works. - * Latency is around 6-9 ms with a etrax_gpio_wake_up_check() in - * cpu_idle(). - * Otherwise I get 15-18 ms (same as doing the poll in userspace - - * but less overhead). - * TODO? Perhaps we should add the check in IMMEDIATE_BH (or whatever it - * is in 2.4) as well? - * TODO? Perhaps call request_irq()/free_irq() only when needed? - * Increased version to 2.5 - * - * Revision 1.18 2002/10/11 15:02:00 johana - * Mask inverted 8 bit value in setget_input(). - * - * Revision 1.17 2002/06/17 15:53:01 johana - * Added IO_READ_INBITS, IO_READ_OUTBITS, IO_SETGET_INPUT and IO_SETGET_OUTPUT - * that take a pointer as argument and thus can handle 32 bit ports (G) - * correctly. - * These should be used instead of IO_READBITS, IO_SETINPUT and IO_SETOUTPUT. - * (especially if Port G bit 31 is used) - * - * Revision 1.16 2002/06/17 09:59:51 johana - * Returning 32 bit values in the ioctl return value doesn't work if bit - * 31 is set (could happen for port G), so mask it of with 0x7FFFFFFF. - * A new set of ioctl's will be added. - * - * Revision 1.15 2002/05/06 13:19:13 johana - * IO_SETINPUT returns mask with bit set = inputs for PA and PB as well. - * - * Revision 1.14 2002/04/12 12:01:53 johana - * Use global r_port_g_data_shadow. - * Moved gpio_init_port_g() closer to gpio_init() and marked it __init. - * - * Revision 1.13 2002/04/10 12:03:55 johana - * Added support for port G /dev/gpiog (minor 3). - * Changed indentation on switch cases. - * Fixed other spaces to tabs. - * - * Revision 1.12 2001/11/12 19:42:15 pkj - * * Corrected return values from gpio_leds_ioctl(). - * * Fixed compiler warnings. - * - * Revision 1.11 2001/10/30 14:39:12 johana - * Added D() around gpio_write printk. - * - * Revision 1.10 2001/10/25 10:24:42 johana - * Added IO_CFG_WRITE_MODE ioctl and write method that can do fast - * bittoggling in the kernel. (This speeds up programming an FPGA with 450kB - * from ~60 seconds to 4 seconds). - * Added save_flags/cli/restore_flags in ioctl. - * - * Revision 1.9 2001/05/04 14:16:07 matsfg - * Corrected spelling error - * - * Revision 1.8 2001/04/27 13:55:26 matsfg - * Moved initioremap. - * Turns off all LEDS on init. - * Added support for shutdown and powerbutton. - * - * Revision 1.7 2001/04/04 13:30:08 matsfg - * Added bitset and bitclear for leds. Calls init_ioremap to set up memmapping - * - * Revision 1.6 2001/03/26 16:03:06 bjornw - * Needs linux/config.h - * - * Revision 1.5 2001/03/26 14:22:03 bjornw - * Namechange of some config options - * - * Revision 1.4 2001/02/27 13:52:48 bjornw - * malloc.h -> slab.h - * - * Revision 1.3 2001/01/24 15:06:48 bjornw - * gpio_wq correct type - * - * Revision 1.2 2001/01/18 16:07:30 bjornw - * 2.4 port - * - * Revision 1.1 2001/01/18 15:55:16 bjornw - * Verbatim copy of etraxgpio.c from elinux 2.0 added - * - * */ @@ -165,7 +38,7 @@ static int dp_cnt; #else #define DP(x) #endif - + static char gpio_name[] = "etrax gpio"; #if 0 @@ -173,9 +46,9 @@ static wait_queue_head_t *gpio_wq; #endif static int gpio_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg); -static ssize_t gpio_write(struct file * file, const char * buf, size_t count, - loff_t *off); + unsigned int cmd, unsigned long arg); +static ssize_t gpio_write(struct file *file, const char __user *buf, + size_t count, loff_t *off); static int gpio_open(struct inode *inode, struct file *filp); static int gpio_release(struct inode *inode, struct file *filp); static unsigned int gpio_poll(struct file *filp, struct poll_table_struct *wait); @@ -201,22 +74,22 @@ struct gpio_private { /* linked list of alarms to check for */ -static struct gpio_private *alarmlist = 0; +static struct gpio_private *alarmlist; -static int gpio_some_alarms = 0; /* Set if someone uses alarm */ -static unsigned long gpio_pa_irq_enabled_mask = 0; +static int gpio_some_alarms; /* Set if someone uses alarm */ +static unsigned long gpio_pa_irq_enabled_mask; static DEFINE_SPINLOCK(gpio_lock); /* Protect directions etc */ /* Port A and B use 8 bit access, but Port G is 32 bit */ #define NUM_PORTS (GPIO_MINOR_B+1) -static volatile unsigned char *ports[NUM_PORTS] = { - R_PORT_PA_DATA, +static volatile unsigned char *ports[NUM_PORTS] = { + R_PORT_PA_DATA, R_PORT_PB_DATA, }; static volatile unsigned char *shads[NUM_PORTS] = { - &port_pa_data_shadow, + &port_pa_data_shadow, &port_pb_data_shadow }; @@ -236,29 +109,29 @@ static volatile unsigned char *shads[NUM_PORTS] = { #endif -static unsigned char changeable_dir[NUM_PORTS] = { +static unsigned char changeable_dir[NUM_PORTS] = { CONFIG_ETRAX_PA_CHANGEABLE_DIR, - CONFIG_ETRAX_PB_CHANGEABLE_DIR + CONFIG_ETRAX_PB_CHANGEABLE_DIR }; -static unsigned char changeable_bits[NUM_PORTS] = { +static unsigned char changeable_bits[NUM_PORTS] = { CONFIG_ETRAX_PA_CHANGEABLE_BITS, - CONFIG_ETRAX_PB_CHANGEABLE_BITS + CONFIG_ETRAX_PB_CHANGEABLE_BITS }; -static volatile unsigned char *dir[NUM_PORTS] = { - R_PORT_PA_DIR, - R_PORT_PB_DIR +static volatile unsigned char *dir[NUM_PORTS] = { + R_PORT_PA_DIR, + R_PORT_PB_DIR }; static volatile unsigned char *dir_shadow[NUM_PORTS] = { - &port_pa_dir_shadow, - &port_pb_dir_shadow + &port_pa_dir_shadow, + &port_pb_dir_shadow }; /* All bits in port g that can change dir. */ static const unsigned long int changeable_dir_g_mask = 0x01FFFF01; -/* Port G is 32 bit, handle it special, some bits are both inputs +/* Port G is 32 bit, handle it special, some bits are both inputs and outputs at the same time, only some of the bits can change direction and some of them in groups of 8 bit. */ static unsigned long changeable_dir_g; @@ -269,18 +142,17 @@ static unsigned long dir_g_shadow; /* 1=output */ #define USE_PORTS(priv) ((priv)->minor <= GPIO_MINOR_B) - -static unsigned int -gpio_poll(struct file *file, - poll_table *wait) +static unsigned int gpio_poll(struct file *file, poll_table *wait) { unsigned int mask = 0; - struct gpio_private *priv = (struct gpio_private *)file->private_data; + struct gpio_private *priv = file->private_data; unsigned long data; - spin_lock(&gpio_lock); + unsigned long flags; + + spin_lock_irqsave(&gpio_lock, flags); + poll_wait(file, &priv->alarm_wq, wait); if (priv->minor == GPIO_MINOR_A) { - unsigned long flags; unsigned long tmp; data = *R_PORT_PA_DATA; /* PA has support for high level interrupt - @@ -288,27 +160,25 @@ gpio_poll(struct file *file, */ tmp = ~data & priv->highalarm & 0xFF; tmp = (tmp << R_IRQ_MASK1_SET__pa0__BITNR); - local_irq_save(flags); + gpio_pa_irq_enabled_mask |= tmp; *R_IRQ_MASK1_SET = tmp; - local_irq_restore(flags); - } else if (priv->minor == GPIO_MINOR_B) data = *R_PORT_PB_DATA; else if (priv->minor == GPIO_MINOR_G) data = *R_PORT_G_DATA; else { - spin_unlock(&gpio_lock); - return 0; + mask = 0; + goto out; } - + if ((data & priv->highalarm) || (~data & priv->lowalarm)) { mask = POLLIN|POLLRDNORM; } - spin_unlock(&gpio_lock); - +out: + spin_unlock_irqrestore(&gpio_lock, flags); DP(printk("gpio_poll ready: mask 0x%08X\n", mask)); return mask; @@ -316,16 +186,19 @@ gpio_poll(struct file *file, int etrax_gpio_wake_up_check(void) { - struct gpio_private *priv = alarmlist; + struct gpio_private *priv; unsigned long data = 0; int ret = 0; - spin_lock(&gpio_lock); + unsigned long flags; + + spin_lock_irqsave(&gpio_lock, flags); + priv = alarmlist; while (priv) { - if (USE_PORTS(priv)) { + if (USE_PORTS(priv)) data = *priv->port; - } else if (priv->minor == GPIO_MINOR_G) { + else if (priv->minor == GPIO_MINOR_G) data = *R_PORT_G_DATA; - } + if ((data & priv->highalarm) || (~data & priv->lowalarm)) { DP(printk("etrax_gpio_wake_up_check %i\n",priv->minor)); @@ -334,12 +207,12 @@ int etrax_gpio_wake_up_check(void) } priv = priv->next; } - spin_unlock(&gpio_lock); + spin_unlock_irqrestore(&gpio_lock, flags); return ret; } static irqreturn_t -gpio_poll_timer_interrupt(int irq, void *dev_id, struct pt_regs *regs) +gpio_poll_timer_interrupt(int irq, void *dev_id) { if (gpio_some_alarms) { etrax_gpio_wake_up_check(); @@ -349,10 +222,13 @@ gpio_poll_timer_interrupt(int irq, void *dev_id, struct pt_regs *regs) } static irqreturn_t -gpio_pa_interrupt(int irq, void *dev_id, struct pt_regs *regs) +gpio_interrupt(int irq, void *dev_id) { unsigned long tmp; - spin_lock(&gpio_lock); + unsigned long flags; + + spin_lock_irqsave(&gpio_lock, flags); + /* Find what PA interrupts are active */ tmp = (*R_IRQ_READ1); @@ -363,75 +239,70 @@ gpio_pa_interrupt(int irq, void *dev_id, struct pt_regs *regs) *R_IRQ_MASK1_CLR = tmp; gpio_pa_irq_enabled_mask &= ~tmp; - spin_unlock(&gpio_lock); + spin_unlock_irqrestore(&gpio_lock, flags); - if (gpio_some_alarms) { + if (gpio_some_alarms) return IRQ_RETVAL(etrax_gpio_wake_up_check()); - } + return IRQ_NONE; } +static void gpio_write_bit(struct gpio_private *priv, + unsigned char data, int bit) +{ + *priv->port = *priv->shadow &= ~(priv->clk_mask); + if (data & 1 << bit) + *priv->port = *priv->shadow |= priv->data_mask; + else + *priv->port = *priv->shadow &= ~(priv->data_mask); + + /* For FPGA: min 5.0ns (DCC) before CCLK high */ + *priv->port = *priv->shadow |= priv->clk_mask; +} -static ssize_t gpio_write(struct file * file, const char * buf, size_t count, - loff_t *off) +static void gpio_write_byte(struct gpio_private *priv, unsigned char data) { - struct gpio_private *priv = (struct gpio_private *)file->private_data; - unsigned char data, clk_mask, data_mask, write_msb; - unsigned long flags; + int i; - spin_lock(&gpio_lock); + if (priv->write_msb) + for (i = 7; i >= 0; i--) + gpio_write_bit(priv, data, i); + else + for (i = 0; i <= 7; i++) + gpio_write_bit(priv, data, i); +} +static ssize_t gpio_write(struct file *file, const char __user *buf, + size_t count, loff_t *off) +{ + struct gpio_private *priv = file->private_data; + unsigned long flags; ssize_t retval = count; - if (priv->minor !=GPIO_MINOR_A && priv->minor != GPIO_MINOR_B) { - retval = -EFAULT; - goto out; - } - - if (!access_ok(VERIFY_READ, buf, count)) { - retval = -EFAULT; - goto out; - } - clk_mask = priv->clk_mask; - data_mask = priv->data_mask; + + if (priv->minor != GPIO_MINOR_A && priv->minor != GPIO_MINOR_B) + return -EFAULT; + + if (!access_ok(VERIFY_READ, buf, count)) + return -EFAULT; + + spin_lock_irqsave(&gpio_lock, flags); + /* It must have been configured using the IO_CFG_WRITE_MODE */ /* Perhaps a better error code? */ - if (clk_mask == 0 || data_mask == 0) { + if (priv->clk_mask == 0 || priv->data_mask == 0) { retval = -EPERM; goto out; } - write_msb = priv->write_msb; - D(printk("gpio_write: %lu to data 0x%02X clk 0x%02X msb: %i\n",count, data_mask, clk_mask, write_msb)); - while (count--) { - int i; - data = *buf++; - if (priv->write_msb) { - for (i = 7; i >= 0;i--) { - local_irq_save(flags); - *priv->port = *priv->shadow &= ~clk_mask; - if (data & 1<<i) - *priv->port = *priv->shadow |= data_mask; - else - *priv->port = *priv->shadow &= ~data_mask; - /* For FPGA: min 5.0ns (DCC) before CCLK high */ - *priv->port = *priv->shadow |= clk_mask; - local_irq_restore(flags); - } - } else { - for (i = 0; i <= 7;i++) { - local_irq_save(flags); - *priv->port = *priv->shadow &= ~clk_mask; - if (data & 1<<i) - *priv->port = *priv->shadow |= data_mask; - else - *priv->port = *priv->shadow &= ~data_mask; - /* For FPGA: min 5.0ns (DCC) before CCLK high */ - *priv->port = *priv->shadow |= clk_mask; - local_irq_restore(flags); - } - } - } + + D(printk(KERN_DEBUG "gpio_write: %02X to data 0x%02X " + "clk 0x%02X msb: %i\n", + count, priv->data_mask, priv->clk_mask, priv->write_msb)); + + while (count--) + gpio_write_byte(priv, *buf++); + out: - spin_unlock(&gpio_lock); + spin_unlock_irqrestore(&gpio_lock, flags); return retval; } @@ -442,22 +313,20 @@ gpio_open(struct inode *inode, struct file *filp) { struct gpio_private *priv; int p = iminor(inode); + unsigned long flags; if (p > GPIO_MINOR_LAST) return -EINVAL; - priv = kmalloc(sizeof(struct gpio_private), - GFP_KERNEL); + priv = kzalloc(sizeof(struct gpio_private), GFP_KERNEL); if (!priv) return -ENOMEM; priv->minor = p; - /* initialize the io/alarm struct and link it into our alarmlist */ + /* initialize the io/alarm struct */ - priv->next = alarmlist; - alarmlist = priv; if (USE_PORTS(priv)) { /* A and B */ priv->port = ports[p]; priv->shadow = shads[p]; @@ -480,7 +349,13 @@ gpio_open(struct inode *inode, struct file *filp) priv->data_mask = 0; init_waitqueue_head(&priv->alarm_wq); - filp->private_data = (void *)priv; + filp->private_data = priv; + + /* link it into our alarmlist */ + spin_lock_irqsave(&gpio_lock, flags); + priv->next = alarmlist; + alarmlist = priv; + spin_unlock_irqrestore(&gpio_lock, flags); return 0; } @@ -490,11 +365,12 @@ gpio_release(struct inode *inode, struct file *filp) { struct gpio_private *p; struct gpio_private *todel; + unsigned long flags; - spin_lock(&gpio_lock); + spin_lock_irqsave(&gpio_lock, flags); - p = alarmlist; - todel = (struct gpio_private *)filp->private_data; + p = alarmlist; + todel = filp->private_data; /* unlink from alarmlist and free the private structure */ @@ -512,123 +388,114 @@ gpio_release(struct inode *inode, struct file *filp) while (p) { if (p->highalarm | p->lowalarm) { gpio_some_alarms = 1; - spin_unlock(&gpio_lock); - return 0; + goto out; } p = p->next; } gpio_some_alarms = 0; - spin_unlock(&gpio_lock); +out: + spin_unlock_irqrestore(&gpio_lock, flags); return 0; } -/* Main device API. ioctl's to read/set/clear bits, as well as to +/* Main device API. ioctl's to read/set/clear bits, as well as to * set alarms to wait for using a subsequent select(). */ - unsigned long inline setget_input(struct gpio_private *priv, unsigned long arg) { - /* Set direction 0=unchanged 1=input, - * return mask with 1=input - */ - unsigned long flags; + /* Set direction 0=unchanged 1=input, + * return mask with 1=input */ if (USE_PORTS(priv)) { - local_irq_save(flags); - *priv->dir = *priv->dir_shadow &= + *priv->dir = *priv->dir_shadow &= ~((unsigned char)arg & priv->changeable_dir); - local_irq_restore(flags); return ~(*priv->dir_shadow) & 0xFF; /* Only 8 bits */ - } else if (priv->minor == GPIO_MINOR_G) { - /* We must fiddle with R_GEN_CONFIG to change dir */ - local_irq_save(flags); - if (((arg & dir_g_in_bits) != arg) && - (arg & changeable_dir_g)) { - arg &= changeable_dir_g; - /* Clear bits in genconfig to set to input */ - if (arg & (1<<0)) { - genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG,g0dir); - dir_g_in_bits |= (1<<0); - dir_g_out_bits &= ~(1<<0); - } - if ((arg & 0x0000FF00) == 0x0000FF00) { - genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG,g8_15dir); - dir_g_in_bits |= 0x0000FF00; - dir_g_out_bits &= ~0x0000FF00; - } - if ((arg & 0x00FF0000) == 0x00FF0000) { - genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG,g16_23dir); - dir_g_in_bits |= 0x00FF0000; - dir_g_out_bits &= ~0x00FF0000; - } - if (arg & (1<<24)) { - genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG,g24dir); - dir_g_in_bits |= (1<<24); - dir_g_out_bits &= ~(1<<24); - } - D(printk(KERN_INFO "gpio: SETINPUT on port G set " - "genconfig to 0x%08lX " - "in_bits: 0x%08lX " - "out_bits: 0x%08lX\n", - (unsigned long)genconfig_shadow, - dir_g_in_bits, dir_g_out_bits)); - *R_GEN_CONFIG = genconfig_shadow; - /* Must be a >120 ns delay before writing this again */ - + } + + if (priv->minor != GPIO_MINOR_G) + return 0; + + /* We must fiddle with R_GEN_CONFIG to change dir */ + if (((arg & dir_g_in_bits) != arg) && + (arg & changeable_dir_g)) { + arg &= changeable_dir_g; + /* Clear bits in genconfig to set to input */ + if (arg & (1<<0)) { + genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG, g0dir); + dir_g_in_bits |= (1<<0); + dir_g_out_bits &= ~(1<<0); } - local_irq_restore(flags); - return dir_g_in_bits; + if ((arg & 0x0000FF00) == 0x0000FF00) { + genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG, g8_15dir); + dir_g_in_bits |= 0x0000FF00; + dir_g_out_bits &= ~0x0000FF00; + } + if ((arg & 0x00FF0000) == 0x00FF0000) { + genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG, g16_23dir); + dir_g_in_bits |= 0x00FF0000; + dir_g_out_bits &= ~0x00FF0000; + } + if (arg & (1<<24)) { + genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG, g24dir); + dir_g_in_bits |= (1<<24); + dir_g_out_bits &= ~(1<<24); + } + D(printk(KERN_DEBUG "gpio: SETINPUT on port G set " + "genconfig to 0x%08lX " + "in_bits: 0x%08lX " + "out_bits: 0x%08lX\n", + (unsigned long)genconfig_shadow, + dir_g_in_bits, dir_g_out_bits)); + *R_GEN_CONFIG = genconfig_shadow; + /* Must be a >120 ns delay before writing this again */ + } - return 0; + return dir_g_in_bits; } /* setget_input */ unsigned long inline setget_output(struct gpio_private *priv, unsigned long arg) { - unsigned long flags; if (USE_PORTS(priv)) { - local_irq_save(flags); - *priv->dir = *priv->dir_shadow |= - ((unsigned char)arg & priv->changeable_dir); - local_irq_restore(flags); + *priv->dir = *priv->dir_shadow |= + ((unsigned char)arg & priv->changeable_dir); return *priv->dir_shadow; - } else if (priv->minor == GPIO_MINOR_G) { - /* We must fiddle with R_GEN_CONFIG to change dir */ - local_irq_save(flags); - if (((arg & dir_g_out_bits) != arg) && - (arg & changeable_dir_g)) { - /* Set bits in genconfig to set to output */ - if (arg & (1<<0)) { - genconfig_shadow |= IO_MASK(R_GEN_CONFIG,g0dir); - dir_g_out_bits |= (1<<0); - dir_g_in_bits &= ~(1<<0); - } - if ((arg & 0x0000FF00) == 0x0000FF00) { - genconfig_shadow |= IO_MASK(R_GEN_CONFIG,g8_15dir); - dir_g_out_bits |= 0x0000FF00; - dir_g_in_bits &= ~0x0000FF00; - } - if ((arg & 0x00FF0000) == 0x00FF0000) { - genconfig_shadow |= IO_MASK(R_GEN_CONFIG,g16_23dir); - dir_g_out_bits |= 0x00FF0000; - dir_g_in_bits &= ~0x00FF0000; - } - if (arg & (1<<24)) { - genconfig_shadow |= IO_MASK(R_GEN_CONFIG,g24dir); - dir_g_out_bits |= (1<<24); - dir_g_in_bits &= ~(1<<24); - } - D(printk(KERN_INFO "gpio: SETOUTPUT on port G set " - "genconfig to 0x%08lX " - "in_bits: 0x%08lX " - "out_bits: 0x%08lX\n", - (unsigned long)genconfig_shadow, - dir_g_in_bits, dir_g_out_bits)); - *R_GEN_CONFIG = genconfig_shadow; - /* Must be a >120 ns delay before writing this again */ + } + if (priv->minor != GPIO_MINOR_G) + return 0; + + /* We must fiddle with R_GEN_CONFIG to change dir */ + if (((arg & dir_g_out_bits) != arg) && + (arg & changeable_dir_g)) { + /* Set bits in genconfig to set to output */ + if (arg & (1<<0)) { + genconfig_shadow |= IO_MASK(R_GEN_CONFIG, g0dir); + dir_g_out_bits |= (1<<0); + dir_g_in_bits &= ~(1<<0); } - local_irq_restore(flags); - return dir_g_out_bits & 0x7FFFFFFF; + if ((arg & 0x0000FF00) == 0x0000FF00) { + genconfig_shadow |= IO_MASK(R_GEN_CONFIG, g8_15dir); + dir_g_out_bits |= 0x0000FF00; + dir_g_in_bits &= ~0x0000FF00; + } + if ((arg & 0x00FF0000) == 0x00FF0000) { + genconfig_shadow |= IO_MASK(R_GEN_CONFIG, g16_23dir); + dir_g_out_bits |= 0x00FF0000; + dir_g_in_bits &= ~0x00FF0000; + } + if (arg & (1<<24)) { + genconfig_shadow |= IO_MASK(R_GEN_CONFIG, g24dir); + dir_g_out_bits |= (1<<24); + dir_g_in_bits &= ~(1<<24); + } + D(printk(KERN_INFO "gpio: SETOUTPUT on port G set " + "genconfig to 0x%08lX " + "in_bits: 0x%08lX " + "out_bits: 0x%08lX\n", + (unsigned long)genconfig_shadow, + dir_g_in_bits, dir_g_out_bits)); + *R_GEN_CONFIG = genconfig_shadow; + /* Must be a >120 ns delay before writing this again */ } - return 0; + return dir_g_out_bits & 0x7FFFFFFF; } /* setget_output */ static int @@ -642,12 +509,11 @@ gpio_ioctl(struct inode *inode, struct file *file, unsigned long val; int ret = 0; - struct gpio_private *priv = (struct gpio_private *)file->private_data; - if (_IOC_TYPE(cmd) != ETRAXGPIO_IOCTYPE) { + struct gpio_private *priv = file->private_data; + if (_IOC_TYPE(cmd) != ETRAXGPIO_IOCTYPE) return -EINVAL; - } - spin_lock(&gpio_lock); + spin_lock_irqsave(&gpio_lock, flags); switch (_IOC_NR(cmd)) { case IO_READBITS: /* Use IO_READ_INBITS and IO_READ_OUTBITS instead */ @@ -659,7 +525,6 @@ gpio_ioctl(struct inode *inode, struct file *file, } break; case IO_SETBITS: - local_irq_save(flags); // set changeable bits with a 1 in arg if (USE_PORTS(priv)) { *priv->port = *priv->shadow |= @@ -667,10 +532,8 @@ gpio_ioctl(struct inode *inode, struct file *file, } else if (priv->minor == GPIO_MINOR_G) { *R_PORT_G_DATA = port_g_data_shadow |= (arg & dir_g_out_bits); } - local_irq_restore(flags); break; case IO_CLRBITS: - local_irq_save(flags); // clear changeable bits with a 1 in arg if (USE_PORTS(priv)) { *priv->port = *priv->shadow &= @@ -678,7 +541,6 @@ gpio_ioctl(struct inode *inode, struct file *file, } else if (priv->minor == GPIO_MINOR_G) { *R_PORT_G_DATA = port_g_data_shadow &= ~((unsigned long)arg & dir_g_out_bits); } - local_irq_restore(flags); break; case IO_HIGHALARM: // set alarm when bits with 1 in arg go high @@ -698,6 +560,8 @@ gpio_ioctl(struct inode *inode, struct file *file, /* Must update gpio_some_alarms */ struct gpio_private *p = alarmlist; int some_alarms; + spin_lock_irq(&gpio_lock); + p = alarmlist; some_alarms = 0; while (p) { if (p->highalarm | p->lowalarm) { @@ -707,6 +571,7 @@ gpio_ioctl(struct inode *inode, struct file *file, p = p->next; } gpio_some_alarms = some_alarms; + spin_unlock_irq(&gpio_lock); } break; case IO_READDIR: /* Use IO_SETGET_INPUT/OUTPUT instead! */ @@ -766,7 +631,7 @@ gpio_ioctl(struct inode *inode, struct file *file, } else if (priv->minor == GPIO_MINOR_G) { val = *R_PORT_G_DATA; } - if (copy_to_user((unsigned long*)arg, &val, sizeof(val))) + if (copy_to_user((void __user *)arg, &val, sizeof(val))) ret = -EFAULT; break; case IO_READ_OUTBITS: @@ -776,33 +641,32 @@ gpio_ioctl(struct inode *inode, struct file *file, } else if (priv->minor == GPIO_MINOR_G) { val = port_g_data_shadow; } - if (copy_to_user((unsigned long*)arg, &val, sizeof(val))) + if (copy_to_user((void __user *)arg, &val, sizeof(val))) ret = -EFAULT; break; case IO_SETGET_INPUT: /* bits set in *arg is set to input, * *arg updated with current input pins. */ - if (copy_from_user(&val, (unsigned long*)arg, sizeof(val))) + if (copy_from_user(&val, (void __user *)arg, sizeof(val))) { ret = -EFAULT; break; } val = setget_input(priv, val); - if (copy_to_user((unsigned long*)arg, &val, sizeof(val))) + if (copy_to_user((void __user *)arg, &val, sizeof(val))) ret = -EFAULT; break; case IO_SETGET_OUTPUT: /* bits set in *arg is set to output, * *arg updated with current output pins. */ - if (copy_from_user(&val, (unsigned long*)arg, sizeof(val))) - { + if (copy_from_user(&val, (void __user *)arg, sizeof(val))) { ret = -EFAULT; break; } val = setget_output(priv, val); - if (copy_to_user((unsigned long*)arg, &val, sizeof(val))) + if (copy_to_user((void __user *)arg, &val, sizeof(val))) ret = -EFAULT; break; default: @@ -812,7 +676,7 @@ gpio_ioctl(struct inode *inode, struct file *file, ret = -EINVAL; } /* switch */ - spin_unlock(&gpio_lock); + spin_unlock_irqrestore(&gpio_lock, flags); return ret; } @@ -824,18 +688,18 @@ gpio_leds_ioctl(unsigned int cmd, unsigned long arg) switch (_IOC_NR(cmd)) { case IO_LEDACTIVE_SET: - green = ((unsigned char) arg) & 1; - red = (((unsigned char) arg) >> 1) & 1; - LED_ACTIVE_SET_G(green); - LED_ACTIVE_SET_R(red); + green = ((unsigned char)arg) & 1; + red = (((unsigned char)arg) >> 1) & 1; + CRIS_LED_ACTIVE_SET_G(green); + CRIS_LED_ACTIVE_SET_R(red); break; case IO_LED_SETBIT: - LED_BIT_SET(arg); + CRIS_LED_BIT_SET(arg); break; case IO_LED_CLRBIT: - LED_BIT_CLR(arg); + CRIS_LED_BIT_CLR(arg); break; default: @@ -845,7 +709,7 @@ gpio_leds_ioctl(unsigned int cmd, unsigned long arg) return 0; } -const struct file_operations gpio_fops = { +static const struct file_operations gpio_fops = { .owner = THIS_MODULE, .poll = gpio_poll, .ioctl = gpio_ioctl, @@ -854,16 +718,18 @@ const struct file_operations gpio_fops = { .release = gpio_release, }; - -void ioif_watcher(const unsigned int gpio_in_available, - const unsigned int gpio_out_available, - const unsigned char pa_available, - const unsigned char pb_available) +static void ioif_watcher(const unsigned int gpio_in_available, + const unsigned int gpio_out_available, + const unsigned char pa_available, + const unsigned char pb_available) { unsigned long int flags; - D(printk("gpio.c: ioif_watcher called\n")); - D(printk("gpio.c: G in: 0x%08x G out: 0x%08x PA: 0x%02x PB: 0x%02x\n", - gpio_in_available, gpio_out_available, pa_available, pb_available)); + + D(printk(KERN_DEBUG "gpio.c: ioif_watcher called\n")); + D(printk(KERN_DEBUG "gpio.c: G in: 0x%08x G out: 0x%08x " + "PA: 0x%02x PB: 0x%02x\n", + gpio_in_available, gpio_out_available, + pa_available, pb_available)); spin_lock_irqsave(&gpio_lock, flags); @@ -872,7 +738,7 @@ void ioif_watcher(const unsigned int gpio_in_available, /* Initialise the dir_g_shadow etc. depending on genconfig */ /* 0=input 1=output */ - if (genconfig_shadow & IO_STATE(R_GEN_CONFIG, g0dir, out)) + if (genconfig_shadow & IO_STATE(R_GEN_CONFIG, g0dir, out)) dir_g_shadow |= (1 << 0); if (genconfig_shadow & IO_STATE(R_GEN_CONFIG, g8_15dir, out)) dir_g_shadow |= 0x0000FF00; @@ -884,7 +750,8 @@ void ioif_watcher(const unsigned int gpio_in_available, changeable_dir_g = changeable_dir_g_mask; changeable_dir_g &= dir_g_out_bits; changeable_dir_g &= dir_g_in_bits; - /* Correct the bits that can change direction */ + + /* Correct the bits that can change direction */ dir_g_out_bits &= ~changeable_dir_g; dir_g_out_bits |= dir_g_shadow; dir_g_in_bits &= ~changeable_dir_g; @@ -892,7 +759,8 @@ void ioif_watcher(const unsigned int gpio_in_available, spin_unlock_irqrestore(&gpio_lock, flags); - printk(KERN_INFO "GPIO port G: in_bits: 0x%08lX out_bits: 0x%08lX val: %08lX\n", + printk(KERN_INFO "GPIO port G: in_bits: 0x%08lX out_bits: 0x%08lX " + "val: %08lX\n", dir_g_in_bits, dir_g_out_bits, (unsigned long)*R_PORT_G_DATA); printk(KERN_INFO "GPIO port G: dir: %08lX changeable: %08lX\n", dir_g_shadow, changeable_dir_g); @@ -900,16 +768,12 @@ void ioif_watcher(const unsigned int gpio_in_available, /* main driver initialization routine, called from mem.c */ -static __init int -gpio_init(void) +static int __init gpio_init(void) { int res; #if defined (CONFIG_ETRAX_CSP0_LEDS) int i; #endif - printk("gpio init\n"); - - /* do the formalities */ res = register_chrdev(GPIO_MAJOR, gpio_name, &gpio_fops); if (res < 0) { @@ -919,43 +783,45 @@ gpio_init(void) /* Clear all leds */ #if defined (CONFIG_ETRAX_CSP0_LEDS) || defined (CONFIG_ETRAX_PA_LEDS) || defined (CONFIG_ETRAX_PB_LEDS) - LED_NETWORK_SET(0); - LED_ACTIVE_SET(0); - LED_DISK_READ(0); - LED_DISK_WRITE(0); + CRIS_LED_NETWORK_SET(0); + CRIS_LED_ACTIVE_SET(0); + CRIS_LED_DISK_READ(0); + CRIS_LED_DISK_WRITE(0); #if defined (CONFIG_ETRAX_CSP0_LEDS) - for (i = 0; i < 32; i++) { - LED_BIT_SET(i); - } + for (i = 0; i < 32; i++) + CRIS_LED_BIT_SET(i); #endif #endif /* The I/O interface allocation watcher will be called when * registering it. */ if (cris_io_interface_register_watcher(ioif_watcher)){ - printk(KERN_WARNING "gpio_init: Failed to install IO if allocator watcher\n"); + printk(KERN_WARNING "gpio_init: Failed to install IO " + "if allocator watcher\n"); } - printk(KERN_INFO "ETRAX 100LX GPIO driver v2.5, (c) 2001, 2002, 2003, 2004 Axis Communications AB\n"); + printk(KERN_INFO "ETRAX 100LX GPIO driver v2.5, (c) 2001-2008 " + "Axis Communications AB\n"); /* We call etrax_gpio_wake_up_check() from timer interrupt and * from cpu_idle() in kernel/process.c * The check in cpu_idle() reduces latency from ~15 ms to ~6 ms * in some tests. - */ - if (request_irq(TIMER0_IRQ_NBR, gpio_poll_timer_interrupt, - IRQF_SHARED | IRQF_DISABLED,"gpio poll", NULL)) { + */ + res = request_irq(TIMER0_IRQ_NBR, gpio_poll_timer_interrupt, + IRQF_SHARED | IRQF_DISABLED, "gpio poll", gpio_name); + if (res) { printk(KERN_CRIT "err: timer0 irq for gpio\n"); + return res; } - if (request_irq(PA_IRQ_NBR, gpio_pa_interrupt, - IRQF_SHARED | IRQF_DISABLED,"gpio PA", NULL)) { + res = request_irq(PA_IRQ_NBR, gpio_interrupt, + IRQF_SHARED | IRQF_DISABLED, "gpio PA", gpio_name); + if (res) printk(KERN_CRIT "err: PA irq for gpio\n"); - } - return res; } /* this makes sure that gpio_init is called during kernel boot */ - module_init(gpio_init); + diff --git a/arch/cris/arch-v10/drivers/i2c.c b/arch/cris/arch-v10/drivers/i2c.c index aca81ddaf60..d6d22067d0c 100644 --- a/arch/cris/arch-v10/drivers/i2c.c +++ b/arch/cris/arch-v10/drivers/i2c.c @@ -6,85 +6,9 @@ *! kernel modules (i2c_writereg/readreg) and from userspace using *! ioctl()'s *! -*! Nov 30 1998 Torbjorn Eliasson Initial version. -*! Bjorn Wesen Elinux kernel version. -*! Jan 14 2000 Johan Adolfsson Fixed PB shadow register stuff - -*! don't use PB_I2C if DS1302 uses same bits, -*! use PB. -*! $Log: i2c.c,v $ -*! Revision 1.13 2005/03/07 13:13:07 starvik -*! Added spinlocks to protect states etc -*! -*! Revision 1.12 2005/01/05 06:11:22 starvik -*! No need to do local_irq_disable after local_irq_save. -*! -*! Revision 1.11 2004/12/13 12:21:52 starvik -*! Added I/O and DMA allocators from Linux 2.4 -*! -*! Revision 1.9 2004/08/24 06:49:14 starvik -*! Whitespace cleanup -*! -*! Revision 1.8 2004/06/08 08:48:26 starvik -*! Removed unused code -*! -*! Revision 1.7 2004/05/28 09:26:59 starvik -*! Modified I2C initialization to work in 2.6. -*! -*! Revision 1.6 2004/05/14 07:58:03 starvik -*! Merge of changes from 2.4 -*! -*! Revision 1.4 2002/12/11 13:13:57 starvik -*! Added arch/ to v10 specific includes -*! Added fix from Linux 2.4 in serial.c (flush_to_flip_buffer) -*! -*! Revision 1.3 2002/11/20 11:56:11 starvik -*! Merge of Linux 2.5.48 -*! -*! Revision 1.2 2002/11/18 13:16:06 starvik -*! Linux 2.5 port of latest 2.4 drivers -*! -*! Revision 1.9 2002/10/31 15:32:26 starvik -*! Update Port B register and shadow even when running with hardware support -*! to avoid glitches when reading bits -*! Never set direction to out in i2c_inbyte -*! Removed incorrect clock toggling at end of i2c_inbyte -*! -*! Revision 1.8 2002/08/13 06:31:53 starvik -*! Made SDA and SCL line configurable -*! Modified i2c_inbyte to work with PCF8563 -*! -*! Revision 1.7 2001/04/04 13:11:36 markusl -*! Updated according to review remarks -*! -*! Revision 1.6 2001/03/19 12:43:00 markusl -*! Made some symbols unstatic (used by the eeprom driver) -*! -*! Revision 1.5 2001/02/27 13:52:48 bjornw -*! malloc.h -> slab.h -*! -*! Revision 1.4 2001/02/15 07:17:40 starvik -*! Corrected usage if port_pb_i2c_shadow -*! -*! Revision 1.3 2001/01/26 17:55:13 bjornw -*! * Made I2C_USES_PB_NOT_PB_I2C a CONFIG option instead of assigning it -*! magically. Config.in needs to set it for the options that need it, like -*! Dallas 1302 support. Actually, it should be default since it screws up -*! the PB bits even if you don't use I2C.. -*! * Include linux/config.h to get the above -*! -*! Revision 1.2 2001/01/18 15:49:30 bjornw -*! 2.4 port of I2C including some cleanups (untested of course) -*! -*! Revision 1.1 2001/01/18 15:35:25 bjornw -*! Verbatim copy of the Etrax i2c driver, 2.0 elinux version -*! -*! -*! --------------------------------------------------------------------------- -*! -*! (C) Copyright 1999-2002 Axis Communications AB, LUND, SWEDEN +*! (C) Copyright 1999-2007 Axis Communications AB, LUND, SWEDEN *! *!***************************************************************************/ -/* $Id: i2c.c,v 1.13 2005/03/07 13:13:07 starvik Exp $ */ /****************** INCLUDE FILES SECTION ***********************************/ @@ -622,7 +546,7 @@ i2c_readreg(unsigned char theSlave, unsigned char theReg) * last received byte needs to be nacked * instead of acked */ - i2c_sendack(); + i2c_sendnack(); /* * end sequence */ @@ -708,6 +632,7 @@ i2c_init(void) if (!first) { return res; } + first = 0; /* Setup and enable the Port B I2C interface */ diff --git a/arch/cris/arch-v10/drivers/pcf8563.c b/arch/cris/arch-v10/drivers/pcf8563.c index c263b8232db..52103d16dc6 100644 --- a/arch/cris/arch-v10/drivers/pcf8563.c +++ b/arch/cris/arch-v10/drivers/pcf8563.c @@ -8,14 +8,13 @@ * low detector are also provided. All address and data are transferred * serially via two-line bidirectional I2C-bus. Maximum bus speed is * 400 kbits/s. The built-in word address register is incremented - * automatically after each written or read bute. + * automatically after each written or read byte. * - * Copyright (c) 2002, Axis Communications AB + * Copyright (c) 2002-2007, Axis Communications AB * All rights reserved. * * Author: Tobias Anderberg <tobiasa@axis.com>. * - * $Id: pcf8563.c,v 1.11 2005/03/07 13:13:07 starvik Exp $ */ #include <linux/module.h> @@ -27,19 +26,19 @@ #include <linux/ioctl.h> #include <linux/delay.h> #include <linux/bcd.h> -#include <linux/capability.h> +#include <linux/mutex.h> #include <asm/uaccess.h> #include <asm/system.h> #include <asm/io.h> -#include <asm/arch/svinto.h> #include <asm/rtc.h> + #include "i2c.h" -#define PCF8563_MAJOR 121 /* Local major number. */ -#define DEVICE_NAME "rtc" /* Name which is registered in /proc/devices. */ +#define PCF8563_MAJOR 121 /* Local major number. */ +#define DEVICE_NAME "rtc" /* Name which is registered in /proc/devices. */ #define PCF8563_NAME "PCF8563" -#define DRIVER_VERSION "$Revision: 1.11 $" +#define DRIVER_VERSION "$Revision: 1.24 $" /* I2C bus slave registers. */ #define RTC_I2C_READ 0xa3 @@ -49,71 +48,88 @@ #define rtc_read(x) i2c_readreg(RTC_I2C_READ, x) #define rtc_write(x,y) i2c_writereg(RTC_I2C_WRITE, x, y) -static DEFINE_SPINLOCK(rtc_lock); /* Protect state etc */ - +static DEFINE_MUTEX(rtc_lock); /* Protect state etc */ + static const unsigned char days_in_month[] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; int pcf8563_ioctl(struct inode *, struct file *, unsigned int, unsigned long); +/* Cache VL bit value read at driver init since writing the RTC_SECOND + * register clears the VL status. + */ +static int voltage_low; + static const struct file_operations pcf8563_fops = { .owner = THIS_MODULE, .ioctl = pcf8563_ioctl, }; unsigned char -pcf8563_readreg(int reg) +pcf8563_readreg(int reg) { - unsigned char res = i2c_readreg(RTC_I2C_READ, reg); - - /* The PCF8563 does not return 0 for unimplemented bits */ - switch(reg) - { - case RTC_SECONDS: - case RTC_MINUTES: - res &= 0x7f; - break; - case RTC_HOURS: - case RTC_DAY_OF_MONTH: - res &= 0x3f; - break; - case RTC_MONTH: - res = (res & 0x1f) - 1; /* PCF8563 returns month in range 1-12 */ - break; + unsigned char res = rtc_read(reg); + + /* The PCF8563 does not return 0 for unimplemented bits. */ + switch (reg) { + case RTC_SECONDS: + case RTC_MINUTES: + res &= 0x7F; + break; + case RTC_HOURS: + case RTC_DAY_OF_MONTH: + res &= 0x3F; + break; + case RTC_WEEKDAY: + res &= 0x07; + break; + case RTC_MONTH: + res &= 0x1F; + break; + case RTC_CONTROL1: + res &= 0xA8; + break; + case RTC_CONTROL2: + res &= 0x1F; + break; + case RTC_CLOCKOUT_FREQ: + case RTC_TIMER_CONTROL: + res &= 0x83; + break; } return res; } void -pcf8563_writereg(int reg, unsigned char val) +pcf8563_writereg(int reg, unsigned char val) { -#ifdef CONFIG_ETRAX_RTC_READONLY - if (reg == RTC_CONTROL1 || (reg >= RTC_SECONDS && reg <= RTC_YEAR)) - return; -#endif - rtc_write(reg, val); } void get_rtc_time(struct rtc_time *tm) { - tm->tm_sec = rtc_read(RTC_SECONDS); - tm->tm_min = rtc_read(RTC_MINUTES); + tm->tm_sec = rtc_read(RTC_SECONDS); + tm->tm_min = rtc_read(RTC_MINUTES); tm->tm_hour = rtc_read(RTC_HOURS); tm->tm_mday = rtc_read(RTC_DAY_OF_MONTH); - tm->tm_mon = rtc_read(RTC_MONTH); + tm->tm_wday = rtc_read(RTC_WEEKDAY); + tm->tm_mon = rtc_read(RTC_MONTH); tm->tm_year = rtc_read(RTC_YEAR); - if (tm->tm_sec & 0x80) - printk(KERN_WARNING "%s: RTC Low Voltage - date/time is not reliable!\n", PCF8563_NAME); + if (tm->tm_sec & 0x80) { + printk(KERN_ERR "%s: RTC Voltage Low - reliable date/time " + "information is no longer guaranteed!\n", PCF8563_NAME); + } - tm->tm_year = BCD_TO_BIN(tm->tm_year) + ((tm->tm_mon & 0x80) ? 100 : 0); - tm->tm_sec &= 0x7f; - tm->tm_min &= 0x7f; - tm->tm_hour &= 0x3f; - tm->tm_mday &= 0x3f; - tm->tm_mon &= 0x1f; + tm->tm_year = BCD_TO_BIN(tm->tm_year) + + ((tm->tm_mon & 0x80) ? 100 : 0); + tm->tm_sec &= 0x7F; + tm->tm_min &= 0x7F; + tm->tm_hour &= 0x3F; + tm->tm_mday &= 0x3F; + tm->tm_wday &= 0x07; /* Not coded in BCD. */ + tm->tm_mon &= 0x1F; BCD_TO_BIN(tm->tm_sec); BCD_TO_BIN(tm->tm_min); @@ -126,17 +142,24 @@ get_rtc_time(struct rtc_time *tm) int __init pcf8563_init(void) { - int ret; - - if ((ret = i2c_init())) { - printk(KERN_CRIT "pcf8563_init: failed to init i2c\n"); - return ret; + static int res; + static int first = 1; + + if (!first) + return res; + first = 0; + + /* Initiate the i2c protocol. */ + res = i2c_init(); + if (res < 0) { + printk(KERN_CRIT "pcf8563_init: Failed to init i2c.\n"); + return res; } /* * First of all we need to reset the chip. This is done by - * clearing control1, control2 and clk freq, clear the - * Voltage Low bit, and resetting all alarms. + * clearing control1, control2 and clk freq and resetting + * all alarms. */ if (rtc_write(RTC_CONTROL1, 0x00) < 0) goto err; @@ -147,34 +170,36 @@ pcf8563_init(void) if (rtc_write(RTC_CLOCKOUT_FREQ, 0x00) < 0) goto err; - /* Clear the VL bit in the seconds register. */ - ret = rtc_read(RTC_SECONDS); - - if (rtc_write(RTC_SECONDS, (ret & 0x7f)) < 0) + if (rtc_write(RTC_TIMER_CONTROL, 0x03) < 0) goto err; - + /* Reset the alarms. */ - if (rtc_write(RTC_MINUTE_ALARM, 0x00) < 0) + if (rtc_write(RTC_MINUTE_ALARM, 0x80) < 0) goto err; - - if (rtc_write(RTC_HOUR_ALARM, 0x00) < 0) + + if (rtc_write(RTC_HOUR_ALARM, 0x80) < 0) goto err; - - if (rtc_write(RTC_DAY_ALARM, 0x00) < 0) + + if (rtc_write(RTC_DAY_ALARM, 0x80) < 0) goto err; - - if (rtc_write(RTC_WEEKDAY_ALARM, 0x00) < 0) + + if (rtc_write(RTC_WEEKDAY_ALARM, 0x80) < 0) goto err; - - /* Check for low voltage, and warn about it.. */ - if (rtc_read(RTC_SECONDS) & 0x80) - printk(KERN_WARNING "%s: RTC Low Voltage - date/time is not reliable!\n", PCF8563_NAME); - - return 0; + + /* Check for low voltage, and warn about it. */ + if (rtc_read(RTC_SECONDS) & 0x80) { + voltage_low = 1; + printk(KERN_WARNING "%s: RTC Voltage Low - reliable " + "date/time information is no longer guaranteed!\n", + PCF8563_NAME); + } + + return res; err: printk(KERN_INFO "%s: Error initializing chip.\n", PCF8563_NAME); - return -1; + res = -1; + return res; } void __exit @@ -187,8 +212,8 @@ pcf8563_exit(void) * ioctl calls for this driver. Why return -ENOTTY upon error? Because * POSIX says so! */ -int -pcf8563_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) +int pcf8563_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) { /* Some sanity checks. */ if (_IOC_TYPE(cmd) != RTC_MAGIC) @@ -198,124 +223,146 @@ pcf8563_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned return -ENOTTY; switch (cmd) { - case RTC_RD_TIME: - { - struct rtc_time tm; - - spin_lock(&rtc_lock); - get_rtc_time(&tm); - - if (copy_to_user((struct rtc_time *) arg, &tm, sizeof(struct rtc_time))) { - spin_unlock(&rtc_lock); - return -EFAULT; - } - - spin_unlock(&rtc_lock); - return 0; - } - break; - case RTC_SET_TIME: - { -#ifdef CONFIG_ETRAX_RTC_READONLY - return -EPERM; -#else - int leap; - int century; - struct rtc_time tm; - - memset(&tm, 0, sizeof (struct rtc_time)); - if (!capable(CAP_SYS_TIME)) - return -EPERM; - - if (copy_from_user(&tm, (struct rtc_time *) arg, sizeof(struct rtc_time))) - return -EFAULT; - - /* Convert from struct tm to struct rtc_time. */ - tm.tm_year += 1900; - tm.tm_mon += 1; - - leap = ((tm.tm_mon == 2) && ((tm.tm_year % 4) == 0)) ? 1 : 0; - - /* Perform some sanity checks. */ - if ((tm.tm_year < 1970) || - (tm.tm_mon > 12) || - (tm.tm_mday == 0) || - (tm.tm_mday > days_in_month[tm.tm_mon] + leap) || - (tm.tm_hour >= 24) || - (tm.tm_min >= 60) || - (tm.tm_sec >= 60)) - return -EINVAL; - - century = (tm.tm_year >= 2000) ? 0x80 : 0; - tm.tm_year = tm.tm_year % 100; - - BIN_TO_BCD(tm.tm_year); - BIN_TO_BCD(tm.tm_mday); - BIN_TO_BCD(tm.tm_hour); - BIN_TO_BCD(tm.tm_min); - BIN_TO_BCD(tm.tm_sec); - tm.tm_mon |= century; - - spin_lock(&rtc_lock); - - rtc_write(RTC_YEAR, tm.tm_year); - rtc_write(RTC_MONTH, tm.tm_mon); - rtc_write(RTC_DAY_OF_MONTH, tm.tm_mday); - rtc_write(RTC_HOURS, tm.tm_hour); - rtc_write(RTC_MINUTES, tm.tm_min); - rtc_write(RTC_SECONDS, tm.tm_sec); - - spin_unlock(&rtc_lock); - - return 0; -#endif /* !CONFIG_ETRAX_RTC_READONLY */ - } - - case RTC_VLOW_RD: - { - int vl_bit = 0; - - if (rtc_read(RTC_SECONDS) & 0x80) { - vl_bit = 1; - printk(KERN_WARNING "%s: RTC Voltage Low - reliable " - "date/time information is no longer guaranteed!\n", - PCF8563_NAME); - } - if (copy_to_user((int *) arg, &vl_bit, sizeof(int))) - return -EFAULT; - - return 0; - } + case RTC_RD_TIME: + { + struct rtc_time tm; - case RTC_VLOW_SET: - { - /* Clear the VL bit in the seconds register */ - int ret = rtc_read(RTC_SECONDS); + mutex_lock(&rtc_lock); + memset(&tm, 0, sizeof tm); + get_rtc_time(&tm); - rtc_write(RTC_SECONDS, (ret & 0x7F)); + if (copy_to_user((struct rtc_time *) arg, &tm, + sizeof tm)) { + spin_unlock(&rtc_lock); + return -EFAULT; + } + + mutex_unlock(&rtc_lock); - return 0; + return 0; + } + case RTC_SET_TIME: + { + int leap; + int year; + int century; + struct rtc_time tm; + + memset(&tm, 0, sizeof tm); + if (!capable(CAP_SYS_TIME)) + return -EPERM; + + if (copy_from_user(&tm, (struct rtc_time *) arg, sizeof tm)) + return -EFAULT; + + /* Convert from struct tm to struct rtc_time. */ + tm.tm_year += 1900; + tm.tm_mon += 1; + + /* + * Check if tm.tm_year is a leap year. A year is a leap + * year if it is divisible by 4 but not 100, except + * that years divisible by 400 _are_ leap years. + */ + year = tm.tm_year; + leap = (tm.tm_mon == 2) && + ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0); + + /* Perform some sanity checks. */ + if ((tm.tm_year < 1970) || + (tm.tm_mon > 12) || + (tm.tm_mday == 0) || + (tm.tm_mday > days_in_month[tm.tm_mon] + leap) || + (tm.tm_wday >= 7) || + (tm.tm_hour >= 24) || + (tm.tm_min >= 60) || + (tm.tm_sec >= 60)) + return -EINVAL; + + century = (tm.tm_year >= 2000) ? 0x80 : 0; + tm.tm_year = tm.tm_year % 100; + + BIN_TO_BCD(tm.tm_year); + BIN_TO_BCD(tm.tm_mon); + BIN_TO_BCD(tm.tm_mday); + BIN_TO_BCD(tm.tm_hour); + BIN_TO_BCD(tm.tm_min); + BIN_TO_BCD(tm.tm_sec); + tm.tm_mon |= century; + + mutex_lock(&rtc_lock); + + rtc_write(RTC_YEAR, tm.tm_year); + rtc_write(RTC_MONTH, tm.tm_mon); + rtc_write(RTC_WEEKDAY, tm.tm_wday); /* Not coded in BCD. */ + rtc_write(RTC_DAY_OF_MONTH, tm.tm_mday); + rtc_write(RTC_HOURS, tm.tm_hour); + rtc_write(RTC_MINUTES, tm.tm_min); + rtc_write(RTC_SECONDS, tm.tm_sec); + + mutex_unlock(&rtc_lock); + + return 0; + } + case RTC_VL_READ: + if (voltage_low) { + printk(KERN_ERR "%s: RTC Voltage Low - " + "reliable date/time information is no " + "longer guaranteed!\n", PCF8563_NAME); } - default: - return -ENOTTY; + if (copy_to_user((int *) arg, &voltage_low, sizeof(int))) + return -EFAULT; + return 0; + + case RTC_VL_CLR: + { + /* Clear the VL bit in the seconds register in case + * the time has not been set already (which would + * have cleared it). This does not really matter + * because of the cached voltage_low value but do it + * anyway for consistency. */ + + int ret = rtc_read(RTC_SECONDS); + + rtc_write(RTC_SECONDS, (ret & 0x7F)); + + /* Clear the cached value. */ + voltage_low = 0; + + return 0; + } + default: + return -ENOTTY; } return 0; } -static int __init -pcf8563_register(void) +static int __init pcf8563_register(void) { - pcf8563_init(); + if (pcf8563_init() < 0) { + printk(KERN_INFO "%s: Unable to initialize Real-Time Clock " + "Driver, %s\n", PCF8563_NAME, DRIVER_VERSION); + return -1; + } + if (register_chrdev(PCF8563_MAJOR, DEVICE_NAME, &pcf8563_fops) < 0) { printk(KERN_INFO "%s: Unable to get major number %d for RTC device.\n", PCF8563_NAME, PCF8563_MAJOR); return -1; } - printk(KERN_INFO "%s Real-Time Clock Driver, %s\n", PCF8563_NAME, DRIVER_VERSION); - return 0; + printk(KERN_INFO "%s Real-Time Clock Driver, %s\n", PCF8563_NAME, + DRIVER_VERSION); + + /* Check for low voltage, and warn about it. */ + if (voltage_low) { + printk(KERN_WARNING "%s: RTC Voltage Low - reliable date/time " + "information is no longer guaranteed!\n", PCF8563_NAME); + } + + return 0; } module_init(pcf8563_register); diff --git a/arch/cris/arch-v10/drivers/sync_serial.c b/arch/cris/arch-v10/drivers/sync_serial.c new file mode 100644 index 00000000000..069546e342c --- /dev/null +++ b/arch/cris/arch-v10/drivers/sync_serial.c @@ -0,0 +1,1441 @@ +/* + * Simple synchronous serial port driver for ETRAX 100LX. + * + * Synchronous serial ports are used for continuous streamed data like audio. + * The default setting for this driver is compatible with the STA 013 MP3 + * decoder. The driver can easily be tuned to fit other audio encoder/decoders + * and SPI + * + * Copyright (c) 2001-2008 Axis Communications AB + * + * Author: Mikael Starvik, Johan Adolfsson + * + */ +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/errno.h> +#include <linux/major.h> +#include <linux/sched.h> +#include <linux/slab.h> +#include <linux/interrupt.h> +#include <linux/poll.h> +#include <linux/init.h> +#include <linux/timer.h> +#include <asm/irq.h> +#include <asm/dma.h> +#include <asm/io.h> +#include <asm/arch/svinto.h> +#include <asm/uaccess.h> +#include <asm/system.h> +#include <asm/sync_serial.h> +#include <asm/arch/io_interface_mux.h> + +/* The receiver is a bit tricky beacuse of the continuous stream of data.*/ +/* */ +/* Three DMA descriptors are linked together. Each DMA descriptor is */ +/* responsible for port->bufchunk of a common buffer. */ +/* */ +/* +---------------------------------------------+ */ +/* | +----------+ +----------+ +----------+ | */ +/* +-> | Descr[0] |-->| Descr[1] |-->| Descr[2] |-+ */ +/* +----------+ +----------+ +----------+ */ +/* | | | */ +/* v v v */ +/* +-------------------------------------+ */ +/* | BUFFER | */ +/* +-------------------------------------+ */ +/* |<- data_avail ->| */ +/* readp writep */ +/* */ +/* If the application keeps up the pace readp will be right after writep.*/ +/* If the application can't keep the pace we have to throw away data. */ +/* The idea is that readp should be ready with the data pointed out by */ +/* Descr[i] when the DMA has filled in Descr[i+1]. */ +/* Otherwise we will discard */ +/* the rest of the data pointed out by Descr1 and set readp to the start */ +/* of Descr2 */ + +#define SYNC_SERIAL_MAJOR 125 + +/* IN_BUFFER_SIZE should be a multiple of 6 to make sure that 24 bit */ +/* words can be handled */ +#define IN_BUFFER_SIZE 12288 +#define IN_DESCR_SIZE 256 +#define NUM_IN_DESCR (IN_BUFFER_SIZE/IN_DESCR_SIZE) +#define OUT_BUFFER_SIZE 4096 + +#define DEFAULT_FRAME_RATE 0 +#define DEFAULT_WORD_RATE 7 + +/* NOTE: Enabling some debug will likely cause overrun or underrun, + * especially if manual mode is use. + */ +#define DEBUG(x) +#define DEBUGREAD(x) +#define DEBUGWRITE(x) +#define DEBUGPOLL(x) +#define DEBUGRXINT(x) +#define DEBUGTXINT(x) + +/* Define some macros to access ETRAX 100 registers */ +#define SETF(var, reg, field, val) \ + do { \ + var = (var & ~IO_MASK_(reg##_, field##_)) | \ + IO_FIELD_(reg##_, field##_, val); \ + } while (0) + +#define SETS(var, reg, field, val) \ + do { \ + var = (var & ~IO_MASK_(reg##_, field##_)) | \ + IO_STATE_(reg##_, field##_, _##val); \ + } while (0) + +struct sync_port { + /* Etrax registers and bits*/ + const volatile unsigned *const status; + volatile unsigned *const ctrl_data; + volatile unsigned *const output_dma_first; + volatile unsigned char *const output_dma_cmd; + volatile unsigned char *const output_dma_clr_irq; + volatile unsigned *const input_dma_first; + volatile unsigned char *const input_dma_cmd; + volatile unsigned *const input_dma_descr; + /* 8*4 */ + volatile unsigned char *const input_dma_clr_irq; + volatile unsigned *const data_out; + const volatile unsigned *const data_in; + char data_avail_bit; /* In R_IRQ_MASK1_RD/SET/CLR */ + char transmitter_ready_bit; /* In R_IRQ_MASK1_RD/SET/CLR */ + char input_dma_descr_bit; /* In R_IRQ_MASK2_RD */ + + char output_dma_bit; /* In R_IRQ_MASK2_RD */ + /* End of fields initialised in array */ + char started; /* 1 if port has been started */ + char port_nbr; /* Port 0 or 1 */ + char busy; /* 1 if port is busy */ + + char enabled; /* 1 if port is enabled */ + char use_dma; /* 1 if port uses dma */ + char tr_running; + + char init_irqs; + + /* Register shadow */ + unsigned int ctrl_data_shadow; + /* Remaining bytes for current transfer */ + volatile unsigned int out_count; + /* Current position in out_buffer */ + unsigned char *outp; + /* 16*4 */ + /* Next byte to be read by application */ + volatile unsigned char *volatile readp; + /* Next byte to be written by etrax */ + volatile unsigned char *volatile writep; + + unsigned int in_buffer_size; + unsigned int inbufchunk; + struct etrax_dma_descr out_descr __attribute__ ((aligned(32))); + struct etrax_dma_descr in_descr[NUM_IN_DESCR] __attribute__ ((aligned(32))); + unsigned char out_buffer[OUT_BUFFER_SIZE] __attribute__ ((aligned(32))); + unsigned char in_buffer[IN_BUFFER_SIZE]__attribute__ ((aligned(32))); + unsigned char flip[IN_BUFFER_SIZE] __attribute__ ((aligned(32))); + struct etrax_dma_descr *next_rx_desc; + struct etrax_dma_descr *prev_rx_desc; + int full; + + wait_queue_head_t out_wait_q; + wait_queue_head_t in_wait_q; +}; + + +static int etrax_sync_serial_init(void); +static void initialize_port(int portnbr); +static inline int sync_data_avail(struct sync_port *port); + +static int sync_serial_open(struct inode *inode, struct file *file); +static int sync_serial_release(struct inode *inode, struct file *file); +static unsigned int sync_serial_poll(struct file *filp, poll_table *wait); + +static int sync_serial_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg); +static ssize_t sync_serial_write(struct file *file, const char *buf, + size_t count, loff_t *ppos); +static ssize_t sync_serial_read(struct file *file, char *buf, + size_t count, loff_t *ppos); + +#if ((defined(CONFIG_ETRAX_SYNCHRONOUS_SERIAL_PORT0) && \ + defined(CONFIG_ETRAX_SYNCHRONOUS_SERIAL0_DMA)) || \ + (defined(CONFIG_ETRAX_SYNCHRONOUS_SERIAL_PORT1) && \ + defined(CONFIG_ETRAX_SYNCHRONOUS_SERIAL1_DMA))) +#define SYNC_SER_DMA +#endif + +static void send_word(struct sync_port *port); +static void start_dma(struct sync_port *port, const char *data, int count); +static void start_dma_in(struct sync_port *port); +#ifdef SYNC_SER_DMA +static irqreturn_t tr_interrupt(int irq, void *dev_id); +static irqreturn_t rx_interrupt(int irq, void *dev_id); +#endif +#if ((defined(CONFIG_ETRAX_SYNCHRONOUS_SERIAL_PORT0) && \ + !defined(CONFIG_ETRAX_SYNCHRONOUS_SERIAL0_DMA)) || \ + (defined(CONFIG_ETRAX_SYNCHRONOUS_SERIAL_PORT1) && \ + !defined(CONFIG_ETRAX_SYNCHRONOUS_SERIAL1_DMA))) +#define SYNC_SER_MANUAL +#endif +#ifdef SYNC_SER_MANUAL +static irqreturn_t manual_interrupt(int irq, void *dev_id); +#endif + +/* The ports */ +static struct sync_port ports[] = { + { + .status = R_SYNC_SERIAL1_STATUS, + .ctrl_data = R_SYNC_SERIAL1_CTRL, + .output_dma_first = R_DMA_CH8_FIRST, + .output_dma_cmd = R_DMA_CH8_CMD, + .output_dma_clr_irq = R_DMA_CH8_CLR_INTR, + .input_dma_first = R_DMA_CH9_FIRST, + .input_dma_cmd = R_DMA_CH9_CMD, + .input_dma_descr = R_DMA_CH9_DESCR, + .input_dma_clr_irq = R_DMA_CH9_CLR_INTR, + .data_out = R_SYNC_SERIAL1_TR_DATA, + .data_in = R_SYNC_SERIAL1_REC_DATA, + .data_avail_bit = IO_BITNR(R_IRQ_MASK1_RD, ser1_data), + .transmitter_ready_bit = IO_BITNR(R_IRQ_MASK1_RD, ser1_ready), + .input_dma_descr_bit = IO_BITNR(R_IRQ_MASK2_RD, dma9_descr), + .output_dma_bit = IO_BITNR(R_IRQ_MASK2_RD, dma8_eop), + .init_irqs = 1, +#if defined(CONFIG_ETRAX_SYNCHRONOUS_SERIAL0_DMA) + .use_dma = 1, +#else + .use_dma = 0, +#endif + }, + { + .status = R_SYNC_SERIAL3_STATUS, + .ctrl_data = R_SYNC_SERIAL3_CTRL, + .output_dma_first = R_DMA_CH4_FIRST, + .output_dma_cmd = R_DMA_CH4_CMD, + .output_dma_clr_irq = R_DMA_CH4_CLR_INTR, + .input_dma_first = R_DMA_CH5_FIRST, + .input_dma_cmd = R_DMA_CH5_CMD, + .input_dma_descr = R_DMA_CH5_DESCR, + .input_dma_clr_irq = R_DMA_CH5_CLR_INTR, + .data_out = R_SYNC_SERIAL3_TR_DATA, + .data_in = R_SYNC_SERIAL3_REC_DATA, + .data_avail_bit = IO_BITNR(R_IRQ_MASK1_RD, ser3_data), + .transmitter_ready_bit = IO_BITNR(R_IRQ_MASK1_RD, ser3_ready), + .input_dma_descr_bit = IO_BITNR(R_IRQ_MASK2_RD, dma5_descr), + .output_dma_bit = IO_BITNR(R_IRQ_MASK2_RD, dma4_eop), + .init_irqs = 1, +#if defined(CONFIG_ETRAX_SYNCHRONOUS_SERIAL1_DMA) + .use_dma = 1, +#else + .use_dma = 0, +#endif + } +}; + +/* Register shadows */ +static unsigned sync_serial_prescale_shadow; + +#define NUMBER_OF_PORTS 2 + +static struct file_operations sync_serial_fops = { + .owner = THIS_MODULE, + .write = sync_serial_write, + .read = sync_serial_read, + .poll = sync_serial_poll, + .ioctl = sync_serial_ioctl, + .open = sync_serial_open, + .release = sync_serial_release +}; + +static int __init etrax_sync_serial_init(void) +{ + ports[0].enabled = 0; + ports[1].enabled = 0; + +#if defined(CONFIG_ETRAX_SYNCHRONOUS_SERIAL_PORT0) + if (cris_request_io_interface(if_sync_serial_1, "sync_ser1")) { + printk(KERN_CRIT "ETRAX100LX sync_serial: " + "Could not allocate IO group for port %d\n", 0); + return -EBUSY; + } +#endif +#if defined(CONFIG_ETRAX_SYNCHRONOUS_SERIAL_PORT1) + if (cris_request_io_interface(if_sync_serial_3, "sync_ser3")) { +#if defined(CONFIG_ETRAX_SYNCHRONOUS_SERIAL_PORT0) + cris_free_io_interface(if_sync_serial_1); +#endif + printk(KERN_CRIT "ETRAX100LX sync_serial: " + "Could not allocate IO group for port %d\n", 1); + return -EBUSY; + } +#endif + + if (register_chrdev(SYNC_SERIAL_MAJOR, "sync serial", + &sync_serial_fops) < 0) { +#if defined(CONFIG_ETRAX_SYNCHRONOUS_SERIAL_PORT1) + cris_free_io_interface(if_sync_serial_3); +#endif +#if defined(CONFIG_ETRAX_SYNCHRONOUS_SERIAL_PORT0) + cris_free_io_interface(if_sync_serial_1); +#endif + printk("unable to get major for synchronous serial port\n"); + return -EBUSY; + } + + /* Deselect synchronous serial ports while configuring. */ + SETS(gen_config_ii_shadow, R_GEN_CONFIG_II, sermode1, async); + SETS(gen_config_ii_shadow, R_GEN_CONFIG_II, sermode3, async); + *R_GEN_CONFIG_II = gen_config_ii_shadow; + + /* Initialize Ports */ +#if defined(CONFIG_ETRAX_SYNCHRONOUS_SERIAL_PORT0) + ports[0].enabled = 1; + SETS(port_pb_i2c_shadow, R_PORT_PB_I2C, syncser1, ss1extra); + SETS(gen_config_ii_shadow, R_GEN_CONFIG_II, sermode1, sync); +#if defined(CONFIG_ETRAX_SYNCHRONOUS_SERIAL0_DMA) + ports[0].use_dma = 1; +#else + ports[0].use_dma = 0; +#endif + initialize_port(0); +#endif + +#if defined(CONFIG_ETRAX_SYNCHRONOUS_SERIAL_PORT1) + ports[1].enabled = 1; + SETS(port_pb_i2c_shadow, R_PORT_PB_I2C, syncser3, ss3extra); + SETS(gen_config_ii_shadow, R_GEN_CONFIG_II, sermode3, sync); +#if defined(CONFIG_ETRAX_SYNCHRONOUS_SERIAL1_DMA) + ports[1].use_dma = 1; +#else + ports[1].use_dma = 0; +#endif + initialize_port(1); +#endif + + *R_PORT_PB_I2C = port_pb_i2c_shadow; /* Use PB4/PB7 */ + + /* Set up timing */ + *R_SYNC_SERIAL_PRESCALE = sync_serial_prescale_shadow = ( + IO_STATE(R_SYNC_SERIAL_PRESCALE, clk_sel_u1, codec) | + IO_STATE(R_SYNC_SERIAL_PRESCALE, word_stb_sel_u1, external) | + IO_STATE(R_SYNC_SERIAL_PRESCALE, clk_sel_u3, codec) | + IO_STATE(R_SYNC_SERIAL_PRESCALE, word_stb_sel_u3, external) | + IO_STATE(R_SYNC_SERIAL_PRESCALE, prescaler, div4) | + IO_FIELD(R_SYNC_SERIAL_PRESCALE, frame_rate, + DEFAULT_FRAME_RATE) | + IO_FIELD(R_SYNC_SERIAL_PRESCALE, word_rate, DEFAULT_WORD_RATE) | + IO_STATE(R_SYNC_SERIAL_PRESCALE, warp_mode, normal)); + + /* Select synchronous ports */ + *R_GEN_CONFIG_II = gen_config_ii_shadow; + + printk(KERN_INFO "ETRAX 100LX synchronous serial port driver\n"); + return 0; +} + +static void __init initialize_port(int portnbr) +{ + struct sync_port *port = &ports[portnbr]; + + DEBUG(printk(KERN_DEBUG "Init sync serial port %d\n", portnbr)); + + port->started = 0; + port->port_nbr = portnbr; + port->busy = 0; + port->tr_running = 0; + + port->out_count = 0; + port->outp = port->out_buffer; + + port->readp = port->flip; + port->writep = port->flip; + port->in_buffer_size = IN_BUFFER_SIZE; + port->inbufchunk = IN_DESCR_SIZE; + port->next_rx_desc = &port->in_descr[0]; + port->prev_rx_desc = &port->in_descr[NUM_IN_DESCR-1]; + port->prev_rx_desc->ctrl = d_eol; + + init_waitqueue_head(&port->out_wait_q); + init_waitqueue_head(&port->in_wait_q); + + port->ctrl_data_shadow = + IO_STATE(R_SYNC_SERIAL1_CTRL, tr_baud, c115k2Hz) | + IO_STATE(R_SYNC_SERIAL1_CTRL, mode, master_output) | + IO_STATE(R_SYNC_SERIAL1_CTRL, error, ignore) | + IO_STATE(R_SYNC_SERIAL1_CTRL, rec_enable, disable) | + IO_STATE(R_SYNC_SERIAL1_CTRL, f_synctype, normal) | + IO_STATE(R_SYNC_SERIAL1_CTRL, f_syncsize, word) | + IO_STATE(R_SYNC_SERIAL1_CTRL, f_sync, on) | + IO_STATE(R_SYNC_SERIAL1_CTRL, clk_mode, normal) | + IO_STATE(R_SYNC_SERIAL1_CTRL, clk_halt, stopped) | + IO_STATE(R_SYNC_SERIAL1_CTRL, bitorder, msb) | + IO_STATE(R_SYNC_SERIAL1_CTRL, tr_enable, disable) | + IO_STATE(R_SYNC_SERIAL1_CTRL, wordsize, size8bit) | + IO_STATE(R_SYNC_SERIAL1_CTRL, buf_empty, lmt_8) | + IO_STATE(R_SYNC_SERIAL1_CTRL, buf_full, lmt_8) | + IO_STATE(R_SYNC_SERIAL1_CTRL, flow_ctrl, enabled) | + IO_STATE(R_SYNC_SERIAL1_CTRL, clk_polarity, neg) | + IO_STATE(R_SYNC_SERIAL1_CTRL, frame_polarity, normal)| + IO_STATE(R_SYNC_SERIAL1_CTRL, status_polarity, inverted)| + IO_STATE(R_SYNC_SERIAL1_CTRL, clk_driver, normal) | + IO_STATE(R_SYNC_SERIAL1_CTRL, frame_driver, normal) | + IO_STATE(R_SYNC_SERIAL1_CTRL, status_driver, normal)| + IO_STATE(R_SYNC_SERIAL1_CTRL, def_out0, high); + + if (port->use_dma) + port->ctrl_data_shadow |= IO_STATE(R_SYNC_SERIAL1_CTRL, + dma_enable, on); + else + port->ctrl_data_shadow |= IO_STATE(R_SYNC_SERIAL1_CTRL, + dma_enable, off); + + *port->ctrl_data = port->ctrl_data_shadow; +} + +static inline int sync_data_avail(struct sync_port *port) +{ + int avail; + unsigned char *start; + unsigned char *end; + + start = (unsigned char *)port->readp; /* cast away volatile */ + end = (unsigned char *)port->writep; /* cast away volatile */ + /* 0123456789 0123456789 + * ----- - ----- + * ^rp ^wp ^wp ^rp + */ + if (end >= start) + avail = end - start; + else + avail = port->in_buffer_size - (start - end); + return avail; +} + +static inline int sync_data_avail_to_end(struct sync_port *port) +{ + int avail; + unsigned char *start; + unsigned char *end; + + start = (unsigned char *)port->readp; /* cast away volatile */ + end = (unsigned char *)port->writep; /* cast away volatile */ + /* 0123456789 0123456789 + * ----- ----- + * ^rp ^wp ^wp ^rp + */ + + if (end >= start) + avail = end - start; + else + avail = port->flip + port->in_buffer_size - start; + return avail; +} + + +static int sync_serial_open(struct inode *inode, struct file *file) +{ + int dev = MINOR(inode->i_rdev); + struct sync_port *port; + int mode; + + DEBUG(printk(KERN_DEBUG "Open sync serial port %d\n", dev)); + + if (dev < 0 || dev >= NUMBER_OF_PORTS || !ports[dev].enabled) { + DEBUG(printk(KERN_DEBUG "Invalid minor %d\n", dev)); + return -ENODEV; + } + port = &ports[dev]; + /* Allow open this device twice (assuming one reader and one writer) */ + if (port->busy == 2) { + DEBUG(printk(KERN_DEBUG "Device is busy.. \n")); + return -EBUSY; + } + if (port->init_irqs) { + if (port->use_dma) { + if (port == &ports[0]) { +#ifdef SYNC_SER_DMA + if (request_irq(24, tr_interrupt, 0, + "synchronous serial 1 dma tr", + &ports[0])) { + printk(KERN_CRIT "Can't alloc " + "sync serial port 1 IRQ"); + return -EBUSY; + } else if (request_irq(25, rx_interrupt, 0, + "synchronous serial 1 dma rx", + &ports[0])) { + free_irq(24, &port[0]); + printk(KERN_CRIT "Can't alloc " + "sync serial port 1 IRQ"); + return -EBUSY; + } else if (cris_request_dma(8, + "synchronous serial 1 dma tr", + DMA_VERBOSE_ON_ERROR, + dma_ser1)) { + free_irq(24, &port[0]); + free_irq(25, &port[0]); + printk(KERN_CRIT "Can't alloc " + "sync serial port 1 " + "TX DMA channel"); + return -EBUSY; + } else if (cris_request_dma(9, + "synchronous serial 1 dma rec", + DMA_VERBOSE_ON_ERROR, + dma_ser1)) { + cris_free_dma(8, NULL); + free_irq(24, &port[0]); + free_irq(25, &port[0]); + printk(KERN_CRIT "Can't alloc " + "sync serial port 1 " + "RX DMA channel"); + return -EBUSY; + } +#endif + RESET_DMA(8); WAIT_DMA(8); + RESET_DMA(9); WAIT_DMA(9); + *R_DMA_CH8_CLR_INTR = + IO_STATE(R_DMA_CH8_CLR_INTR, clr_eop, + do) | + IO_STATE(R_DMA_CH8_CLR_INTR, clr_descr, + do); + *R_DMA_CH9_CLR_INTR = + IO_STATE(R_DMA_CH9_CLR_INTR, clr_eop, + do) | + IO_STATE(R_DMA_CH9_CLR_INTR, clr_descr, + do); + *R_IRQ_MASK2_SET = + IO_STATE(R_IRQ_MASK2_SET, dma8_eop, + set) | + IO_STATE(R_IRQ_MASK2_SET, dma9_descr, + set); + } else if (port == &ports[1]) { +#ifdef SYNC_SER_DMA + if (request_irq(20, tr_interrupt, 0, + "synchronous serial 3 dma tr", + &ports[1])) { + printk(KERN_CRIT "Can't alloc " + "sync serial port 3 IRQ"); + return -EBUSY; + } else if (request_irq(21, rx_interrupt, 0, + "synchronous serial 3 dma rx", + &ports[1])) { + free_irq(20, &ports[1]); + printk(KERN_CRIT "Can't alloc " + "sync serial port 3 IRQ"); + return -EBUSY; + } else if (cris_request_dma(4, + "synchronous serial 3 dma tr", + DMA_VERBOSE_ON_ERROR, + dma_ser3)) { + free_irq(21, &ports[1]); + free_irq(20, &ports[1]); + printk(KERN_CRIT "Can't alloc " + "sync serial port 3 " + "TX DMA channel"); + return -EBUSY; + } else if (cris_request_dma(5, + "synchronous serial 3 dma rec", + DMA_VERBOSE_ON_ERROR, + dma_ser3)) { + cris_free_dma(4, NULL); + free_irq(21, &ports[1]); + free_irq(20, &ports[1]); + printk(KERN_CRIT "Can't alloc " + "sync serial port 3 " + "RX DMA channel"); + return -EBUSY; + } +#endif + RESET_DMA(4); WAIT_DMA(4); + RESET_DMA(5); WAIT_DMA(5); + *R_DMA_CH4_CLR_INTR = + IO_STATE(R_DMA_CH4_CLR_INTR, clr_eop, + do) | + IO_STATE(R_DMA_CH4_CLR_INTR, clr_descr, + do); + *R_DMA_CH5_CLR_INTR = + IO_STATE(R_DMA_CH5_CLR_INTR, clr_eop, + do) | + IO_STATE(R_DMA_CH5_CLR_INTR, clr_descr, + do); + *R_IRQ_MASK2_SET = + IO_STATE(R_IRQ_MASK2_SET, dma4_eop, + set) | + IO_STATE(R_IRQ_MASK2_SET, dma5_descr, + set); + } + start_dma_in(port); + port->init_irqs = 0; + } else { /* !port->use_dma */ +#ifdef SYNC_SER_MANUAL + if (port == &ports[0]) { + if (request_irq(8, + manual_interrupt, + IRQF_SHARED | IRQF_DISABLED, + "synchronous serial manual irq", + &ports[0])) { + printk(KERN_CRIT "Can't alloc " + "sync serial manual irq"); + return -EBUSY; + } + } else if (port == &ports[1]) { + if (request_irq(8, + manual_interrupt, + IRQF_SHARED | IRQF_DISABLED, + "synchronous serial manual irq", + &ports[1])) { + printk(KERN_CRIT "Can't alloc " + "sync serial manual irq"); + return -EBUSY; + } + } + port->init_irqs = 0; +#else + panic("sync_serial: Manual mode not supported.\n"); +#endif /* SYNC_SER_MANUAL */ + } + } /* port->init_irqs */ + + port->busy++; + /* Start port if we use it as input */ + mode = IO_EXTRACT(R_SYNC_SERIAL1_CTRL, mode, port->ctrl_data_shadow); + if (mode == IO_STATE_VALUE(R_SYNC_SERIAL1_CTRL, mode, master_input) || + mode == IO_STATE_VALUE(R_SYNC_SERIAL1_CTRL, mode, slave_input) || + mode == IO_STATE_VALUE(R_SYNC_SERIAL1_CTRL, mode, master_bidir) || + mode == IO_STATE_VALUE(R_SYNC_SERIAL1_CTRL, mode, slave_bidir)) { + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, clk_halt, + running); + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, tr_enable, + enable); + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, rec_enable, + enable); + port->started = 1; + *port->ctrl_data = port->ctrl_data_shadow; + if (!port->use_dma) + *R_IRQ_MASK1_SET = 1 << port->data_avail_bit; + DEBUG(printk(KERN_DEBUG "sser%d rec started\n", dev)); + } + return 0; +} + +static int sync_serial_release(struct inode *inode, struct file *file) +{ + int dev = MINOR(inode->i_rdev); + struct sync_port *port; + + if (dev < 0 || dev >= NUMBER_OF_PORTS || !ports[dev].enabled) { + DEBUG(printk(KERN_DEBUG "Invalid minor %d\n", dev)); + return -ENODEV; + } + port = &ports[dev]; + if (port->busy) + port->busy--; + if (!port->busy) + *R_IRQ_MASK1_CLR = ((1 << port->data_avail_bit) | + (1 << port->transmitter_ready_bit)); + + return 0; +} + + + +static unsigned int sync_serial_poll(struct file *file, poll_table *wait) +{ + int dev = MINOR(file->f_dentry->d_inode->i_rdev); + unsigned int mask = 0; + struct sync_port *port; + DEBUGPOLL(static unsigned int prev_mask = 0); + + port = &ports[dev]; + poll_wait(file, &port->out_wait_q, wait); + poll_wait(file, &port->in_wait_q, wait); + /* Some room to write */ + if (port->out_count < OUT_BUFFER_SIZE) + mask |= POLLOUT | POLLWRNORM; + /* At least an inbufchunk of data */ + if (sync_data_avail(port) >= port->inbufchunk) + mask |= POLLIN | POLLRDNORM; + + DEBUGPOLL(if (mask != prev_mask) + printk(KERN_DEBUG "sync_serial_poll: mask 0x%08X %s %s\n", + mask, + mask & POLLOUT ? "POLLOUT" : "", + mask & POLLIN ? "POLLIN" : ""); + prev_mask = mask; + ); + return mask; +} + +static int sync_serial_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + int return_val = 0; + unsigned long flags; + + int dev = MINOR(file->f_dentry->d_inode->i_rdev); + struct sync_port *port; + + if (dev < 0 || dev >= NUMBER_OF_PORTS || !ports[dev].enabled) { + DEBUG(printk(KERN_DEBUG "Invalid minor %d\n", dev)); + return -1; + } + port = &ports[dev]; + + local_irq_save(flags); + /* Disable port while changing config */ + if (dev) { + if (port->use_dma) { + RESET_DMA(4); WAIT_DMA(4); + port->tr_running = 0; + port->out_count = 0; + port->outp = port->out_buffer; + *R_DMA_CH4_CLR_INTR = + IO_STATE(R_DMA_CH4_CLR_INTR, clr_eop, do) | + IO_STATE(R_DMA_CH4_CLR_INTR, clr_descr, do); + } + SETS(gen_config_ii_shadow, R_GEN_CONFIG_II, sermode3, async); + } else { + if (port->use_dma) { + RESET_DMA(8); WAIT_DMA(8); + port->tr_running = 0; + port->out_count = 0; + port->outp = port->out_buffer; + *R_DMA_CH8_CLR_INTR = + IO_STATE(R_DMA_CH8_CLR_INTR, clr_eop, do) | + IO_STATE(R_DMA_CH8_CLR_INTR, clr_descr, do); + } + SETS(gen_config_ii_shadow, R_GEN_CONFIG_II, sermode1, async); + } + *R_GEN_CONFIG_II = gen_config_ii_shadow; + local_irq_restore(flags); + + switch (cmd) { + case SSP_SPEED: + if (GET_SPEED(arg) == CODEC) { + if (dev) + SETS(sync_serial_prescale_shadow, + R_SYNC_SERIAL_PRESCALE, clk_sel_u3, + codec); + else + SETS(sync_serial_prescale_shadow, + R_SYNC_SERIAL_PRESCALE, clk_sel_u1, + codec); + + SETF(sync_serial_prescale_shadow, + R_SYNC_SERIAL_PRESCALE, prescaler, + GET_FREQ(arg)); + SETF(sync_serial_prescale_shadow, + R_SYNC_SERIAL_PRESCALE, frame_rate, + GET_FRAME_RATE(arg)); + SETF(sync_serial_prescale_shadow, + R_SYNC_SERIAL_PRESCALE, word_rate, + GET_WORD_RATE(arg)); + } else { + SETF(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, + tr_baud, GET_SPEED(arg)); + if (dev) + SETS(sync_serial_prescale_shadow, + R_SYNC_SERIAL_PRESCALE, clk_sel_u3, + baudrate); + else + SETS(sync_serial_prescale_shadow, + R_SYNC_SERIAL_PRESCALE, clk_sel_u1, + baudrate); + } + break; + case SSP_MODE: + if (arg > 5) + return -EINVAL; + if (arg == MASTER_OUTPUT || arg == SLAVE_OUTPUT) + *R_IRQ_MASK1_CLR = 1 << port->data_avail_bit; + else if (!port->use_dma) + *R_IRQ_MASK1_SET = 1 << port->data_avail_bit; + SETF(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, mode, arg); + break; + case SSP_FRAME_SYNC: + if (arg & NORMAL_SYNC) + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, + f_synctype, normal); + else if (arg & EARLY_SYNC) + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, + f_synctype, early); + + if (arg & BIT_SYNC) + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, + f_syncsize, bit); + else if (arg & WORD_SYNC) + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, + f_syncsize, word); + else if (arg & EXTENDED_SYNC) + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, + f_syncsize, extended); + + if (arg & SYNC_ON) + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, + f_sync, on); + else if (arg & SYNC_OFF) + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, + f_sync, off); + + if (arg & WORD_SIZE_8) + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, + wordsize, size8bit); + else if (arg & WORD_SIZE_12) + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, + wordsize, size12bit); + else if (arg & WORD_SIZE_16) + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, + wordsize, size16bit); + else if (arg & WORD_SIZE_24) + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, + wordsize, size24bit); + else if (arg & WORD_SIZE_32) + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, + wordsize, size32bit); + + if (arg & BIT_ORDER_MSB) + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, + bitorder, msb); + else if (arg & BIT_ORDER_LSB) + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, + bitorder, lsb); + + if (arg & FLOW_CONTROL_ENABLE) + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, + flow_ctrl, enabled); + else if (arg & FLOW_CONTROL_DISABLE) + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, + flow_ctrl, disabled); + + if (arg & CLOCK_NOT_GATED) + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, + clk_mode, normal); + else if (arg & CLOCK_GATED) + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, + clk_mode, gated); + + break; + case SSP_IPOLARITY: + /* NOTE!! negedge is considered NORMAL */ + if (arg & CLOCK_NORMAL) + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, + clk_polarity, neg); + else if (arg & CLOCK_INVERT) + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, + clk_polarity, pos); + + if (arg & FRAME_NORMAL) + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, + frame_polarity, normal); + else if (arg & FRAME_INVERT) + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, + frame_polarity, inverted); + + if (arg & STATUS_NORMAL) + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, + status_polarity, normal); + else if (arg & STATUS_INVERT) + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, + status_polarity, inverted); + break; + case SSP_OPOLARITY: + if (arg & CLOCK_NORMAL) + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, + clk_driver, normal); + else if (arg & CLOCK_INVERT) + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, + clk_driver, inverted); + + if (arg & FRAME_NORMAL) + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, + frame_driver, normal); + else if (arg & FRAME_INVERT) + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, + frame_driver, inverted); + + if (arg & STATUS_NORMAL) + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, + status_driver, normal); + else if (arg & STATUS_INVERT) + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, + status_driver, inverted); + break; + case SSP_SPI: + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, flow_ctrl, + disabled); + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, bitorder, + msb); + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, wordsize, + size8bit); + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, f_sync, on); + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, f_syncsize, + word); + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, f_synctype, + normal); + if (arg & SPI_SLAVE) { + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, + frame_polarity, inverted); + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, + clk_polarity, neg); + SETF(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, + mode, SLAVE_INPUT); + } else { + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, + frame_driver, inverted); + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, + clk_driver, inverted); + SETF(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, + mode, MASTER_OUTPUT); + } + break; + case SSP_INBUFCHUNK: +#if 0 + if (arg > port->in_buffer_size/NUM_IN_DESCR) + return -EINVAL; + port->inbufchunk = arg; + /* Make sure in_buffer_size is a multiple of inbufchunk */ + port->in_buffer_size = + (port->in_buffer_size/port->inbufchunk) * + port->inbufchunk; + DEBUG(printk(KERN_DEBUG "inbufchunk %i in_buffer_size: %i\n", + port->inbufchunk, port->in_buffer_size)); + if (port->use_dma) { + if (port->port_nbr == 0) { + RESET_DMA(9); + WAIT_DMA(9); + } else { + RESET_DMA(5); + WAIT_DMA(5); + } + start_dma_in(port); + } +#endif + break; + default: + return_val = -1; + } + /* Make sure we write the config without interruption */ + local_irq_save(flags); + /* Set config and enable port */ + *port->ctrl_data = port->ctrl_data_shadow; + nop(); nop(); nop(); nop(); + *R_SYNC_SERIAL_PRESCALE = sync_serial_prescale_shadow; + nop(); nop(); nop(); nop(); + if (dev) + SETS(gen_config_ii_shadow, R_GEN_CONFIG_II, sermode3, sync); + else + SETS(gen_config_ii_shadow, R_GEN_CONFIG_II, sermode1, sync); + + *R_GEN_CONFIG_II = gen_config_ii_shadow; + /* Reset DMA. At readout from serial port the data could be shifted + * one byte if not resetting DMA. + */ + if (port->use_dma) { + if (port->port_nbr == 0) { + RESET_DMA(9); + WAIT_DMA(9); + } else { + RESET_DMA(5); + WAIT_DMA(5); + } + start_dma_in(port); + } + local_irq_restore(flags); + return return_val; +} + + +static ssize_t sync_serial_write(struct file *file, const char *buf, + size_t count, loff_t *ppos) +{ + int dev = MINOR(file->f_dentry->d_inode->i_rdev); + DECLARE_WAITQUEUE(wait, current); + struct sync_port *port; + unsigned long flags; + unsigned long c, c1; + unsigned long free_outp; + unsigned long outp; + unsigned long out_buffer; + + if (dev < 0 || dev >= NUMBER_OF_PORTS || !ports[dev].enabled) { + DEBUG(printk(KERN_DEBUG "Invalid minor %d\n", dev)); + return -ENODEV; + } + port = &ports[dev]; + + DEBUGWRITE(printk(KERN_DEBUG "W d%d c %lu (%d/%d)\n", + port->port_nbr, count, port->out_count, OUT_BUFFER_SIZE)); + /* Space to end of buffer */ + /* + * out_buffer <c1>012345<- c ->OUT_BUFFER_SIZE + * outp^ +out_count + * ^free_outp + * out_buffer 45<- c ->0123OUT_BUFFER_SIZE + * +out_count outp^ + * free_outp + * + */ + + /* Read variables that may be updated by interrupts */ + local_irq_save(flags); + if (count > OUT_BUFFER_SIZE - port->out_count) + count = OUT_BUFFER_SIZE - port->out_count; + + outp = (unsigned long)port->outp; + free_outp = outp + port->out_count; + local_irq_restore(flags); + out_buffer = (unsigned long)port->out_buffer; + + /* Find out where and how much to write */ + if (free_outp >= out_buffer + OUT_BUFFER_SIZE) + free_outp -= OUT_BUFFER_SIZE; + if (free_outp >= outp) + c = out_buffer + OUT_BUFFER_SIZE - free_outp; + else + c = outp - free_outp; + if (c > count) + c = count; + + DEBUGWRITE(printk(KERN_DEBUG "w op %08lX fop %08lX c %lu\n", + outp, free_outp, c)); + if (copy_from_user((void *)free_outp, buf, c)) + return -EFAULT; + + if (c != count) { + buf += c; + c1 = count - c; + DEBUGWRITE(printk(KERN_DEBUG "w2 fi %lu c %lu c1 %lu\n", + free_outp-out_buffer, c, c1)); + if (copy_from_user((void *)out_buffer, buf, c1)) + return -EFAULT; + } + local_irq_save(flags); + port->out_count += count; + local_irq_restore(flags); + + /* Make sure transmitter/receiver is running */ + if (!port->started) { + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, clk_halt, + running); + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, tr_enable, + enable); + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, rec_enable, + enable); + port->started = 1; + } + + *port->ctrl_data = port->ctrl_data_shadow; + + if (file->f_flags & O_NONBLOCK) { + local_irq_save(flags); + if (!port->tr_running) { + if (!port->use_dma) { + /* Start sender by writing data */ + send_word(port); + /* and enable transmitter ready IRQ */ + *R_IRQ_MASK1_SET = 1 << + port->transmitter_ready_bit; + } else + start_dma(port, + (unsigned char *volatile)port->outp, c); + } + local_irq_restore(flags); + DEBUGWRITE(printk(KERN_DEBUG "w d%d c %lu NB\n", + port->port_nbr, count)); + return count; + } + + /* Sleep until all sent */ + add_wait_queue(&port->out_wait_q, &wait); + set_current_state(TASK_INTERRUPTIBLE); + local_irq_save(flags); + if (!port->tr_running) { + if (!port->use_dma) { + /* Start sender by writing data */ + send_word(port); + /* and enable transmitter ready IRQ */ + *R_IRQ_MASK1_SET = 1 << port->transmitter_ready_bit; + } else + start_dma(port, port->outp, c); + } + local_irq_restore(flags); + schedule(); + set_current_state(TASK_RUNNING); + remove_wait_queue(&port->out_wait_q, &wait); + if (signal_pending(current)) + return -EINTR; + + DEBUGWRITE(printk(KERN_DEBUG "w d%d c %lu\n", port->port_nbr, count)); + return count; +} + +static ssize_t sync_serial_read(struct file *file, char *buf, + size_t count, loff_t *ppos) +{ + int dev = MINOR(file->f_dentry->d_inode->i_rdev); + int avail; + struct sync_port *port; + unsigned char *start; + unsigned char *end; + unsigned long flags; + + if (dev < 0 || dev >= NUMBER_OF_PORTS || !ports[dev].enabled) { + DEBUG(printk(KERN_DEBUG "Invalid minor %d\n", dev)); + return -ENODEV; + } + port = &ports[dev]; + + DEBUGREAD(printk(KERN_DEBUG "R%d c %d ri %lu wi %lu /%lu\n", + dev, count, port->readp - port->flip, + port->writep - port->flip, port->in_buffer_size)); + + if (!port->started) { + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, clk_halt, + running); + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, tr_enable, + enable); + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, rec_enable, + enable); + port->started = 1; + } + *port->ctrl_data = port->ctrl_data_shadow; + + /* Calculate number of available bytes */ + /* Save pointers to avoid that they are modified by interrupt */ + local_irq_save(flags); + start = (unsigned char *)port->readp; /* cast away volatile */ + end = (unsigned char *)port->writep; /* cast away volatile */ + local_irq_restore(flags); + while (start == end && !port->full) { + /* No data */ + if (file->f_flags & O_NONBLOCK) + return -EAGAIN; + + interruptible_sleep_on(&port->in_wait_q); + if (signal_pending(current)) + return -EINTR; + + local_irq_save(flags); + start = (unsigned char *)port->readp; /* cast away volatile */ + end = (unsigned char *)port->writep; /* cast away volatile */ + local_irq_restore(flags); + } + + /* Lazy read, never return wrapped data. */ + if (port->full) + avail = port->in_buffer_size; + else if (end > start) + avail = end - start; + else + avail = port->flip + port->in_buffer_size - start; + + count = count > avail ? avail : count; + if (copy_to_user(buf, start, count)) + return -EFAULT; + /* Disable interrupts while updating readp */ + local_irq_save(flags); + port->readp += count; + if (port->readp >= port->flip + port->in_buffer_size) /* Wrap? */ + port->readp = port->flip; + port->full = 0; + local_irq_restore(flags); + DEBUGREAD(printk(KERN_DEBUG "r %d\n", count)); + return count; +} + +static void send_word(struct sync_port *port) +{ + switch (IO_EXTRACT(R_SYNC_SERIAL1_CTRL, wordsize, + port->ctrl_data_shadow)) { + case IO_STATE_VALUE(R_SYNC_SERIAL1_CTRL, wordsize, size8bit): + port->out_count--; + *port->data_out = *port->outp++; + if (port->outp >= port->out_buffer + OUT_BUFFER_SIZE) + port->outp = port->out_buffer; + break; + case IO_STATE_VALUE(R_SYNC_SERIAL1_CTRL, wordsize, size12bit): + { + int data = (*port->outp++) << 8; + data |= *port->outp++; + port->out_count -= 2; + *port->data_out = data; + if (port->outp >= port->out_buffer + OUT_BUFFER_SIZE) + port->outp = port->out_buffer; + break; + } + case IO_STATE_VALUE(R_SYNC_SERIAL1_CTRL, wordsize, size16bit): + port->out_count -= 2; + *port->data_out = *(unsigned short *)port->outp; + port->outp += 2; + if (port->outp >= port->out_buffer + OUT_BUFFER_SIZE) + port->outp = port->out_buffer; + break; + case IO_STATE_VALUE(R_SYNC_SERIAL1_CTRL, wordsize, size24bit): + port->out_count -= 3; + *port->data_out = *(unsigned int *)port->outp; + port->outp += 3; + if (port->outp >= port->out_buffer + OUT_BUFFER_SIZE) + port->outp = port->out_buffer; + break; + case IO_STATE_VALUE(R_SYNC_SERIAL1_CTRL, wordsize, size32bit): + port->out_count -= 4; + *port->data_out = *(unsigned int *)port->outp; + port->outp += 4; + if (port->outp >= port->out_buffer + OUT_BUFFER_SIZE) + port->outp = port->out_buffer; + break; + } +} + + +static void start_dma(struct sync_port *port, const char *data, int count) +{ + port->tr_running = 1; + port->out_descr.hw_len = 0; + port->out_descr.next = 0; + port->out_descr.ctrl = d_eol | d_eop; /* No d_wait to avoid glitches */ + port->out_descr.sw_len = count; + port->out_descr.buf = virt_to_phys(data); + port->out_descr.status = 0; + + *port->output_dma_first = virt_to_phys(&port->out_descr); + *port->output_dma_cmd = IO_STATE(R_DMA_CH0_CMD, cmd, start); + DEBUGTXINT(printk(KERN_DEBUG "dma %08lX c %d\n", + (unsigned long)data, count)); +} + +static void start_dma_in(struct sync_port *port) +{ + int i; + unsigned long buf; + port->writep = port->flip; + + if (port->writep > port->flip + port->in_buffer_size) { + panic("Offset too large in sync serial driver\n"); + return; + } + buf = virt_to_phys(port->in_buffer); + for (i = 0; i < NUM_IN_DESCR; i++) { + port->in_descr[i].sw_len = port->inbufchunk; + port->in_descr[i].ctrl = d_int; + port->in_descr[i].next = virt_to_phys(&port->in_descr[i+1]); + port->in_descr[i].buf = buf; + port->in_descr[i].hw_len = 0; + port->in_descr[i].status = 0; + port->in_descr[i].fifo_len = 0; + buf += port->inbufchunk; + prepare_rx_descriptor(&port->in_descr[i]); + } + /* Link the last descriptor to the first */ + port->in_descr[i-1].next = virt_to_phys(&port->in_descr[0]); + port->in_descr[i-1].ctrl |= d_eol; + port->next_rx_desc = &port->in_descr[0]; + port->prev_rx_desc = &port->in_descr[NUM_IN_DESCR - 1]; + *port->input_dma_first = virt_to_phys(port->next_rx_desc); + *port->input_dma_cmd = IO_STATE(R_DMA_CH0_CMD, cmd, start); +} + +#ifdef SYNC_SER_DMA +static irqreturn_t tr_interrupt(int irq, void *dev_id) +{ + unsigned long ireg = *R_IRQ_MASK2_RD; + struct etrax_dma_descr *descr; + unsigned int sentl; + int handled = 0; + int i; + + for (i = 0; i < NUMBER_OF_PORTS; i++) { + struct sync_port *port = &ports[i]; + if (!port->enabled || !port->use_dma) + continue; + + /* IRQ active for the port? */ + if (!(ireg & (1 << port->output_dma_bit))) + continue; + + handled = 1; + + /* Clear IRQ */ + *port->output_dma_clr_irq = + IO_STATE(R_DMA_CH0_CLR_INTR, clr_eop, do) | + IO_STATE(R_DMA_CH0_CLR_INTR, clr_descr, do); + + descr = &port->out_descr; + if (!(descr->status & d_stop)) + sentl = descr->sw_len; + else + /* Otherwise find amount of data sent here */ + sentl = descr->hw_len; + + port->out_count -= sentl; + port->outp += sentl; + if (port->outp >= port->out_buffer + OUT_BUFFER_SIZE) + port->outp = port->out_buffer; + if (port->out_count) { + int c = port->out_buffer + OUT_BUFFER_SIZE - port->outp; + if (c > port->out_count) + c = port->out_count; + DEBUGTXINT(printk(KERN_DEBUG + "tx_int DMAWRITE %i %i\n", sentl, c)); + start_dma(port, port->outp, c); + } else { + DEBUGTXINT(printk(KERN_DEBUG + "tx_int DMA stop %i\n", sentl)); + port->tr_running = 0; + } + /* wake up the waiting process */ + wake_up_interruptible(&port->out_wait_q); + } + return IRQ_RETVAL(handled); +} /* tr_interrupt */ + +static irqreturn_t rx_interrupt(int irq, void *dev_id) +{ + unsigned long ireg = *R_IRQ_MASK2_RD; + int i; + int handled = 0; + + for (i = 0; i < NUMBER_OF_PORTS; i++) { + struct sync_port *port = &ports[i]; + + if (!port->enabled || !port->use_dma) + continue; + + if (!(ireg & (1 << port->input_dma_descr_bit))) + continue; + + /* Descriptor interrupt */ + handled = 1; + while (*port->input_dma_descr != + virt_to_phys(port->next_rx_desc)) { + if (port->writep + port->inbufchunk > port->flip + + port->in_buffer_size) { + int first_size = port->flip + + port->in_buffer_size - port->writep; + memcpy(port->writep, + phys_to_virt(port->next_rx_desc->buf), + first_size); + memcpy(port->flip, + phys_to_virt(port->next_rx_desc->buf + + first_size), + port->inbufchunk - first_size); + port->writep = port->flip + + port->inbufchunk - first_size; + } else { + memcpy(port->writep, + phys_to_virt(port->next_rx_desc->buf), + port->inbufchunk); + port->writep += port->inbufchunk; + if (port->writep >= port->flip + + port->in_buffer_size) + port->writep = port->flip; + } + if (port->writep == port->readp) + port->full = 1; + prepare_rx_descriptor(port->next_rx_desc); + port->next_rx_desc->ctrl |= d_eol; + port->prev_rx_desc->ctrl &= ~d_eol; + port->prev_rx_desc = phys_to_virt((unsigned) + port->next_rx_desc); + port->next_rx_desc = phys_to_virt((unsigned) + port->next_rx_desc->next); + /* Wake up the waiting process */ + wake_up_interruptible(&port->in_wait_q); + *port->input_dma_cmd = IO_STATE(R_DMA_CH1_CMD, + cmd, restart); + /* DMA has reached end of descriptor */ + *port->input_dma_clr_irq = IO_STATE(R_DMA_CH0_CLR_INTR, + clr_descr, do); + } + } + return IRQ_RETVAL(handled); +} /* rx_interrupt */ +#endif /* SYNC_SER_DMA */ + +#ifdef SYNC_SER_MANUAL +static irqreturn_t manual_interrupt(int irq, void *dev_id) +{ + int i; + int handled = 0; + + for (i = 0; i < NUMBER_OF_PORTS; i++) { + struct sync_port *port = &ports[i]; + + if (!port->enabled || port->use_dma) + continue; + + /* Data received? */ + if (*R_IRQ_MASK1_RD & (1 << port->data_avail_bit)) { + handled = 1; + /* Read data */ + switch (port->ctrl_data_shadow & + IO_MASK(R_SYNC_SERIAL1_CTRL, wordsize)) { + case IO_STATE(R_SYNC_SERIAL1_CTRL, wordsize, size8bit): + *port->writep++ = + *(volatile char *)port->data_in; + break; + case IO_STATE(R_SYNC_SERIAL1_CTRL, wordsize, size12bit): + { + int data = *(unsigned short *)port->data_in; + *port->writep = (data & 0x0ff0) >> 4; + *(port->writep + 1) = data & 0x0f; + port->writep += 2; + break; + } + case IO_STATE(R_SYNC_SERIAL1_CTRL, wordsize, size16bit): + *(unsigned short *)port->writep = + *(volatile unsigned short *)port->data_in; + port->writep += 2; + break; + case IO_STATE(R_SYNC_SERIAL1_CTRL, wordsize, size24bit): + *(unsigned int *)port->writep = *port->data_in; + port->writep += 3; + break; + case IO_STATE(R_SYNC_SERIAL1_CTRL, wordsize, size32bit): + *(unsigned int *)port->writep = *port->data_in; + port->writep += 4; + break; + } + + /* Wrap? */ + if (port->writep >= port->flip + port->in_buffer_size) + port->writep = port->flip; + if (port->writep == port->readp) { + /* Receive buffer overrun, discard oldest */ + port->readp++; + /* Wrap? */ + if (port->readp >= port->flip + + port->in_buffer_size) + port->readp = port->flip; + } + if (sync_data_avail(port) >= port->inbufchunk) { + /* Wake up application */ + wake_up_interruptible(&port->in_wait_q); + } + } + + /* Transmitter ready? */ + if (*R_IRQ_MASK1_RD & (1 << port->transmitter_ready_bit)) { + if (port->out_count > 0) { + /* More data to send */ + send_word(port); + } else { + /* Transmission finished */ + /* Turn off IRQ */ + *R_IRQ_MASK1_CLR = 1 << + port->transmitter_ready_bit; + /* Wake up application */ + wake_up_interruptible(&port->out_wait_q); + } + } + } + return IRQ_RETVAL(handled); +} +#endif + +module_init(etrax_sync_serial_init); |