Merge branch 'release' of ssh://master.kernel.org/pub/scm/linux/kernel/git/aegl/linux-2.6
authorLinus Torvalds <torvalds@woody.linux-foundation.org>
Mon, 15 Oct 2007 22:32:57 +0000 (15:32 -0700)
committerLinus Torvalds <torvalds@woody.linux-foundation.org>
Mon, 15 Oct 2007 22:32:57 +0000 (15:32 -0700)
* 'release' of ssh://master.kernel.org/pub/scm/linux/kernel/git/aegl/linux-2.6:
  [IA64] build fix for scatterlist

347 files changed:
Documentation/feature-removal-schedule.txt
Documentation/kernel-parameters.txt
Documentation/networking/bonding.txt
Documentation/networking/proc_net_tcp.txt
MAINTAINERS
arch/blackfin/mach-bf548/boards/ezkit.c
arch/m68k/atari/atakeyb.c
arch/mips/au1000/common/prom.c
arch/mips/au1000/common/setup.c
arch/mips/au1000/db1x00/init.c
arch/mips/au1000/mtx-1/init.c
arch/mips/au1000/pb1000/init.c
arch/mips/au1000/pb1100/init.c
arch/mips/au1000/pb1200/board_setup.c
arch/mips/au1000/pb1200/init.c
arch/mips/au1000/pb1500/init.c
arch/mips/au1000/pb1550/init.c
arch/mips/au1000/xxs1500/init.c
arch/powerpc/platforms/cell/axon_msi.c
arch/powerpc/sysdev/dcr.c
arch/powerpc/sysdev/mpic.c
arch/s390/kernel/entry.S
arch/s390/kernel/entry64.S
arch/x86/kernel/entry_32.S
arch/x86/kernel/entry_64.S
arch/x86/kernel/kprobes_32.c
arch/x86/kernel/kprobes_64.c
arch/x86/lib/thunk_64.S
drivers/ata/Kconfig
drivers/ata/Makefile
drivers/ata/ata_piix.c
drivers/ata/libata-core.c
drivers/ata/libata-scsi.c
drivers/ata/pata_cs5536.c [new file with mode: 0644]
drivers/ata/pata_pcmcia.c
drivers/ata/pata_sil680.c
drivers/ata/sata_nv.c
drivers/char/ec3104_keyb.c [deleted file]
drivers/hwmon/Kconfig
drivers/hwmon/ams/ams-input.c
drivers/hwmon/ams/ams.h
drivers/hwmon/applesmc.c
drivers/hwmon/hdaps.c
drivers/infiniband/ulp/ipoib/ipoib.h
drivers/infiniband/ulp/ipoib/ipoib_main.c
drivers/infiniband/ulp/ipoib/ipoib_multicast.c
drivers/input/Kconfig
drivers/input/Makefile
drivers/input/evdev.c
drivers/input/input-polldev.c
drivers/input/input.c
drivers/input/joydev.c
drivers/input/joystick/xpad.c
drivers/input/keyboard/Kconfig
drivers/input/keyboard/Makefile
drivers/input/keyboard/atakbd.c
drivers/input/keyboard/bf54x-keys.c [new file with mode: 0644]
drivers/input/keyboard/gpio_keys.c
drivers/input/keyboard/jornada680_kbd.c [new file with mode: 0644]
drivers/input/keyboard/jornada720_kbd.c [new file with mode: 0644]
drivers/input/keyboard/maple_keyb.c [new file with mode: 0644]
drivers/input/keyboard/omap-keypad.c
drivers/input/mouse/alps.c
drivers/input/mouse/appletouch.c
drivers/input/mouse/lifebook.c
drivers/input/mouse/psmouse-base.c
drivers/input/mousedev.c
drivers/input/serio/i8042.c
drivers/input/touchscreen/Kconfig
drivers/input/touchscreen/Makefile
drivers/input/touchscreen/jornada720_ts.c [new file with mode: 0644]
drivers/input/touchscreen/ucb1400_ts.c
drivers/input/touchscreen/usbtouchscreen.c
drivers/input/tsdev.c [deleted file]
drivers/isdn/i4l/isdn_net.c
drivers/isdn/i4l/isdn_ppp.c
drivers/macintosh/Kconfig
drivers/macintosh/adbhid.c
drivers/misc/thinkpad_acpi.c
drivers/mtd/maps/pxa2xx-flash.c
drivers/net/Kconfig
drivers/net/Makefile
drivers/net/au1000_eth.c
drivers/net/bonding/bond_main.c
drivers/net/bonding/bond_sysfs.c
drivers/net/bonding/bonding.h
drivers/net/cassini.c
drivers/net/cpmac.c [new file with mode: 0644]
drivers/net/gianfar.c
drivers/net/ibm_emac/ibm_emac_mal.c
drivers/net/ibm_emac/ibm_emac_mal.h
drivers/net/ibm_newemac/mal.c
drivers/net/ibm_newemac/mal.h
drivers/net/irda/donauboe.c
drivers/net/jazzsonic.c
drivers/net/loopback.c
drivers/net/mipsnet.c
drivers/net/mipsnet.h
drivers/net/myri10ge/myri10ge.c
drivers/net/myri10ge/myri10ge_mcp.h
drivers/net/natsemi.c
drivers/net/ne-h8300.c
drivers/net/niu.c
drivers/net/saa9730.c
drivers/net/tc35815.c
drivers/net/tehuti.c
drivers/net/tg3.c
drivers/net/tulip/de4x5.c
drivers/net/ucc_geth.c
drivers/net/wan/sdla.c
drivers/net/xen-netfront.c
drivers/scsi/gdth.c
fs/Kconfig
fs/inode.c
fs/jbd/transaction.c
fs/lockd/mon.c
fs/lockd/xdr.c
fs/lockd/xdr4.c
fs/nfs/Makefile
fs/nfs/client.c
fs/nfs/delegation.c
fs/nfs/dir.c
fs/nfs/direct.c
fs/nfs/file.c
fs/nfs/inode.c
fs/nfs/internal.h
fs/nfs/nfs2xdr.c
fs/nfs/nfs3acl.c
fs/nfs/nfs3proc.c
fs/nfs/nfs3xdr.c
fs/nfs/nfs4proc.c
fs/nfs/nfs4state.c
fs/nfs/nfs4xdr.c
fs/nfs/nfsroot.c
fs/nfs/proc.c
fs/nfs/read.c
fs/nfs/super.c
fs/nfs/unlink.c
fs/nfs/write.c
fs/nfsd/nfs4xdr.c
include/asm-blackfin/mach-bf548/bf54x_keys.h [new file with mode: 0644]
include/asm-mips/mach-au1x00/prom.h [new file with mode: 0644]
include/asm-powerpc/dcr-mmio.h
include/asm-powerpc/dcr-native.h
include/asm-x86/irqflags_32.h
include/asm-x86/irqflags_64.h
include/linux/fs.h
include/linux/gpio_keys.h
include/linux/if_bridge.h
include/linux/input.h
include/linux/isdn.h
include/linux/jbd.h
include/linux/jiffies.h
include/linux/keyboard.h
include/linux/lockdep.h
include/linux/mutex.h
include/linux/netfilter.h
include/linux/netfilter/nf_conntrack_amanda.h
include/linux/netfilter/nf_conntrack_ftp.h
include/linux/netfilter/nf_conntrack_h323.h
include/linux/netfilter/nf_conntrack_irc.h
include/linux/netfilter/nf_conntrack_pptp.h
include/linux/netfilter/nf_conntrack_sip.h
include/linux/netfilter/nf_conntrack_tftp.h
include/linux/netfilter/x_tables.h
include/linux/netfilter_arp/arp_tables.h
include/linux/netfilter_bridge/ebtables.h
include/linux/netfilter_ipv4.h
include/linux/netfilter_ipv4/ip_tables.h
include/linux/netfilter_ipv6/ip6_tables.h
include/linux/nfs_fs.h
include/linux/nfs_page.h
include/linux/nfs_xdr.h
include/linux/rcupdate.h
include/linux/skbuff.h
include/linux/sunrpc/clnt.h
include/linux/sunrpc/debug.h
include/linux/sunrpc/msg_prot.h
include/linux/sunrpc/rpc_rdma.h [new file with mode: 0644]
include/linux/sunrpc/xdr.h
include/linux/sunrpc/xprt.h
include/linux/sunrpc/xprtrdma.h [new file with mode: 0644]
include/linux/sunrpc/xprtsock.h [new file with mode: 0644]
include/linux/tcp.h
include/linux/writeback.h
include/net/inet_frag.h [new file with mode: 0644]
include/net/ip.h
include/net/ip_vs.h
include/net/ipv6.h
include/net/netfilter/ipv6/nf_conntrack_ipv6.h
include/net/netfilter/nf_conntrack_core.h
include/net/netfilter/nf_conntrack_helper.h
include/net/netfilter/nf_nat_core.h
include/net/netfilter/nf_nat_helper.h
include/net/netfilter/nf_nat_protocol.h
include/net/netfilter/nf_nat_rule.h
include/net/protocol.h
include/net/xfrm.h
kernel/auditsc.c
kernel/lockdep.c
kernel/lockdep_proc.c
kernel/mutex.c
kernel/rcupdate.c
net/bridge/br.c
net/bridge/br_input.c
net/bridge/br_netfilter.c
net/bridge/netfilter/ebt_arpreply.c
net/bridge/netfilter/ebt_dnat.c
net/bridge/netfilter/ebt_mark.c
net/bridge/netfilter/ebt_redirect.c
net/bridge/netfilter/ebt_snat.c
net/bridge/netfilter/ebtable_broute.c
net/bridge/netfilter/ebtable_filter.c
net/bridge/netfilter/ebtable_nat.c
net/bridge/netfilter/ebtables.c
net/core/dev.c
net/core/neighbour.c
net/core/skbuff.c
net/dccp/ipv6.c
net/decnet/netfilter/dn_rtmsg.c
net/ipv4/Makefile
net/ipv4/inet_fragment.c [new file with mode: 0644]
net/ipv4/ip_forward.c
net/ipv4/ip_fragment.c
net/ipv4/ip_input.c
net/ipv4/ip_output.c
net/ipv4/ipvs/ip_vs_app.c
net/ipv4/ipvs/ip_vs_core.c
net/ipv4/ipvs/ip_vs_ftp.c
net/ipv4/ipvs/ip_vs_proto_tcp.c
net/ipv4/ipvs/ip_vs_proto_udp.c
net/ipv4/ipvs/ip_vs_xmit.c
net/ipv4/netfilter.c
net/ipv4/netfilter/arp_tables.c
net/ipv4/netfilter/arpt_mangle.c
net/ipv4/netfilter/arptable_filter.c
net/ipv4/netfilter/ip_queue.c
net/ipv4/netfilter/ip_tables.c
net/ipv4/netfilter/ipt_CLUSTERIP.c
net/ipv4/netfilter/ipt_ECN.c
net/ipv4/netfilter/ipt_LOG.c
net/ipv4/netfilter/ipt_MASQUERADE.c
net/ipv4/netfilter/ipt_NETMAP.c
net/ipv4/netfilter/ipt_REDIRECT.c
net/ipv4/netfilter/ipt_REJECT.c
net/ipv4/netfilter/ipt_SAME.c
net/ipv4/netfilter/ipt_TOS.c
net/ipv4/netfilter/ipt_TTL.c
net/ipv4/netfilter/ipt_ULOG.c
net/ipv4/netfilter/iptable_filter.c
net/ipv4/netfilter/iptable_mangle.c
net/ipv4/netfilter/iptable_raw.c
net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c
net/ipv4/netfilter/nf_nat_amanda.c
net/ipv4/netfilter/nf_nat_core.c
net/ipv4/netfilter/nf_nat_ftp.c
net/ipv4/netfilter/nf_nat_h323.c
net/ipv4/netfilter/nf_nat_helper.c
net/ipv4/netfilter/nf_nat_irc.c
net/ipv4/netfilter/nf_nat_pptp.c
net/ipv4/netfilter/nf_nat_proto_gre.c
net/ipv4/netfilter/nf_nat_proto_icmp.c
net/ipv4/netfilter/nf_nat_proto_tcp.c
net/ipv4/netfilter/nf_nat_proto_udp.c
net/ipv4/netfilter/nf_nat_proto_unknown.c
net/ipv4/netfilter/nf_nat_rule.c
net/ipv4/netfilter/nf_nat_sip.c
net/ipv4/netfilter/nf_nat_snmp_basic.c
net/ipv4/netfilter/nf_nat_standalone.c
net/ipv4/netfilter/nf_nat_tftp.c
net/ipv4/proc.c
net/ipv4/sysctl_net_ipv4.c
net/ipv4/tcp_input.c
net/ipv4/xfrm4_output.c
net/ipv6/exthdrs.c
net/ipv6/icmp.c
net/ipv6/inet6_connection_sock.c
net/ipv6/ip6_input.c
net/ipv6/ip6_output.c
net/ipv6/netfilter.c
net/ipv6/netfilter/ip6_queue.c
net/ipv6/netfilter/ip6_tables.c
net/ipv6/netfilter/ip6t_HL.c
net/ipv6/netfilter/ip6t_LOG.c
net/ipv6/netfilter/ip6t_REJECT.c
net/ipv6/netfilter/ip6table_filter.c
net/ipv6/netfilter/ip6table_mangle.c
net/ipv6/netfilter/ip6table_raw.c
net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c
net/ipv6/netfilter/nf_conntrack_reasm.c
net/ipv6/proc.c
net/ipv6/reassembly.c
net/ipv6/route.c
net/ipv6/sysctl_net_ipv6.c
net/ipv6/tcp_ipv6.c
net/ipv6/tunnel6.c
net/ipv6/udp.c
net/ipv6/udp_impl.h
net/ipv6/udplite.c
net/ipv6/xfrm6_input.c
net/ipv6/xfrm6_output.c
net/netfilter/core.c
net/netfilter/nf_conntrack_amanda.c
net/netfilter/nf_conntrack_core.c
net/netfilter/nf_conntrack_ftp.c
net/netfilter/nf_conntrack_h323_main.c
net/netfilter/nf_conntrack_irc.c
net/netfilter/nf_conntrack_netbios_ns.c
net/netfilter/nf_conntrack_pptp.c
net/netfilter/nf_conntrack_sane.c
net/netfilter/nf_conntrack_sip.c
net/netfilter/nf_conntrack_tftp.c
net/netfilter/nf_internals.h
net/netfilter/nf_queue.c
net/netfilter/nfnetlink_queue.c
net/netfilter/xt_CLASSIFY.c
net/netfilter/xt_CONNMARK.c
net/netfilter/xt_CONNSECMARK.c
net/netfilter/xt_DSCP.c
net/netfilter/xt_MARK.c
net/netfilter/xt_NFLOG.c
net/netfilter/xt_NFQUEUE.c
net/netfilter/xt_NOTRACK.c
net/netfilter/xt_SECMARK.c
net/netfilter/xt_TCPMSS.c
net/netfilter/xt_TRACE.c
net/netlink/af_netlink.c
net/sched/act_ipt.c
net/sched/sch_ingress.c
net/sctp/ipv6.c
net/sunrpc/Makefile
net/sunrpc/auth_gss/gss_krb5_wrap.c
net/sunrpc/clnt.c
net/sunrpc/rpc_pipe.c
net/sunrpc/rpcb_clnt.c
net/sunrpc/sched.c
net/sunrpc/socklib.c
net/sunrpc/sunrpc_syms.c
net/sunrpc/timer.c
net/sunrpc/xprt.c
net/sunrpc/xprtrdma/Makefile [new file with mode: 0644]
net/sunrpc/xprtrdma/rpc_rdma.c [new file with mode: 0644]
net/sunrpc/xprtrdma/transport.c [new file with mode: 0644]
net/sunrpc/xprtrdma/verbs.c [new file with mode: 0644]
net/sunrpc/xprtrdma/xprt_rdma.h [new file with mode: 0644]
net/sunrpc/xprtsock.c
security/selinux/hooks.c

index 63df226..fb8258e 100644 (file)
@@ -205,20 +205,6 @@ Who:       Len Brown <len.brown@intel.com>
 
 ---------------------------
 
-What:  Compaq touchscreen device emulation
-When:  Oct 2007
-Files: drivers/input/tsdev.c
-Why:   The code says it was obsolete when it was written in 2001.
-       tslib is a userspace library which does anything tsdev can do and
-       much more besides in userspace where this code belongs. There is no
-       longer any need for tsdev and applications should have converted to
-       use tslib by now.
-       The name "tsdev" is also extremely confusing and lots of people have
-       it loaded when they don't need/use it.
-Who:   Richard Purdie <rpurdie@rpsys.net>
-
----------------------------
-
 What:  i2c-ixp2000, i2c-ixp4xx and scx200_i2c drivers
 When:  September 2007
 Why:   Obsolete. The new i2c-gpio driver replaces all hardware-specific
index c323778..085e4a0 100644 (file)
@@ -1083,6 +1083,13 @@ and is between 256 and 4096 characters. It is defined in the file
                        [NFS] set the maximum lifetime for idmapper cache
                        entries.
 
+       nfs.enable_ino64=
+                       [NFS] enable 64-bit inode numbers.
+                       If zero, the NFS client will fake up a 32-bit inode
+                       number for the readdir() and stat() syscalls instead
+                       of returning the full 64-bit number.
+                       The default is to return 64-bit inode numbers.
+
        nmi_watchdog=   [KNL,BUGS=X86-32] Debugging features for SMP kernels
 
        no387           [BUGS=X86-32] Tells the kernel to use the 387 maths
@@ -1883,9 +1890,6 @@ and is between 256 and 4096 characters. It is defined in the file
                        Format:
                        <io>,<irq>,<dma>,<dma2>,<sb_io>,<sb_irq>,<sb_dma>,<mpu_io>,<mpu_irq>
 
-       tsdev.xres=     [TS] Horizontal screen resolution.
-       tsdev.yres=     [TS] Vertical screen resolution.
-
        turbografx.map[2|3]=    [HW,JOY]
                        TurboGraFX parallel port interface
                        Format:
index 1da5666..1134062 100644 (file)
@@ -281,6 +281,39 @@ downdelay
        will be rounded down to the nearest multiple.  The default
        value is 0.
 
+fail_over_mac
+
+       Specifies whether active-backup mode should set all slaves to
+       the same MAC address (the traditional behavior), or, when
+       enabled, change the bond's MAC address when changing the
+       active interface (i.e., fail over the MAC address itself).
+
+       Fail over MAC is useful for devices that cannot ever alter
+       their MAC address, or for devices that refuse incoming
+       broadcasts with their own source MAC (which interferes with
+       the ARP monitor).
+
+       The down side of fail over MAC is that every device on the
+       network must be updated via gratuitous ARP, vs. just updating
+       a switch or set of switches (which often takes place for any
+       traffic, not just ARP traffic, if the switch snoops incoming
+       traffic to update its tables) for the traditional method.  If
+       the gratuitous ARP is lost, communication may be disrupted.
+
+       When fail over MAC is used in conjuction with the mii monitor,
+       devices which assert link up prior to being able to actually
+       transmit and receive are particularly susecptible to loss of
+       the gratuitous ARP, and an appropriate updelay setting may be
+       required.
+
+       A value of 0 disables fail over MAC, and is the default.  A
+       value of 1 enables fail over MAC.  This option is enabled
+       automatically if the first slave added cannot change its MAC
+       address.  This option may be modified via sysfs only when no
+       slaves are present in the bond.
+
+       This option was added in bonding version 3.2.0.
+
 lacp_rate
 
        Option specifying the rate in which we'll ask our link partner
index 5e21f7c..4a79209 100644 (file)
@@ -1,8 +1,9 @@
 This document describes the interfaces /proc/net/tcp and /proc/net/tcp6.
+Note that these interfaces are deprecated in favor of tcp_diag.
 
 These /proc interfaces provide information about currently active TCP 
-connections, and are implemented by tcp_get_info() in net/ipv4/tcp_ipv4.c and
-tcp6_get_info() in net/ipv6/tcp_ipv6.c, respectively.
+connections, and are implemented by tcp4_seq_show() in net/ipv4/tcp_ipv4.c
+and tcp6_seq_show() in net/ipv6/tcp_ipv6.c, respectively.
 
 It will first list all listening TCP sockets, and next list all established
 TCP connections. A typical entry of /proc/net/tcp would look like this (split 
index 12cee3d..c7355e7 100644 (file)
@@ -2404,6 +2404,15 @@ M:       khali@linux-fr.org
 L:     lm-sensors@lm-sensors.org
 S:     Maintained
 
+LOCKDEP AND LOCKSTAT
+P:     Peter Zijlstra
+M:     peterz@infradead.org
+P:     Ingo Molnar
+M:     mingo@redhat.com
+L:     linux-kernel@vger.kernel.org
+T:     git://git.kernel.org/pub/scm/linux/kernel/git/peterz/linux-2.6-lockdep.git
+S:     Maintained
+
 LOGICAL DISK MANAGER SUPPORT (LDM, Windows 2000/XP/Vista Dynamic Disks)
 P:     Richard Russon (FlatCap)
 M:     ldm@flatcap.org
index 2c47db4..046e6d8 100644 (file)
@@ -88,7 +88,7 @@ static struct platform_device bf54x_lq043_device = {
 #endif
 
 #if defined(CONFIG_KEYBOARD_BFIN) || defined(CONFIG_KEYBOARD_BFIN_MODULE)
-static int bf548_keymap[] = {
+static const unsigned int bf548_keymap[] = {
        KEYVAL(0, 0, KEY_ENTER),
        KEYVAL(0, 1, KEY_HELP),
        KEYVAL(0, 2, KEY_0),
@@ -110,8 +110,8 @@ static int bf548_keymap[] = {
 static struct bfin_kpad_platform_data bf54x_kpad_data = {
        .rows                   = 4,
        .cols                   = 4,
-       .keymap                 = bf548_keymap,
-       .keymapsize             = ARRAY_SIZE(bf548_keymap),
+       .keymap                 = bf548_keymap,
+       .keymapsize             = ARRAY_SIZE(bf548_keymap),
        .repeat                 = 0,
        .debounce_time          = 5000, /* ns (5ms) */
        .coldrive_time          = 1000, /* ns (1ms) */
index fbbccb5..880add1 100644 (file)
@@ -1,6 +1,4 @@
 /*
- * linux/arch/m68k/atari/atakeyb.c
- *
  * Atari Keyboard driver for 680x0 Linux
  *
  * This file is subject to the terms and conditions of the GNU General Public
index a8637cd..90d7069 100644 (file)
@@ -33,7 +33,6 @@
  *  with this program; if not, write  to the Free Software Foundation, Inc.,
  *  675 Mass Ave, Cambridge, MA 02139, USA.
  */
-
 #include <linux/module.h>
 #include <linux/kernel.h>
 #include <linux/init.h>
 
 #include <asm/bootinfo.h>
 
-/* #define DEBUG_CMDLINE */
-
-extern int prom_argc;
-extern char **prom_argv, **prom_envp;
-
+int prom_argc;
+char **prom_argv;
+char **prom_envp;
 
 char * __init_or_module prom_getcmdline(void)
 {
        return &(arcs_cmdline[0]);
 }
 
-void  prom_init_cmdline(void)
+void prom_init_cmdline(void)
 {
        char *cp;
        int actr;
@@ -61,7 +58,7 @@ void  prom_init_cmdline(void)
 
        cp = &(arcs_cmdline[0]);
        while(actr < prom_argc) {
-               strcpy(cp, prom_argv[actr]);
+               strcpy(cp, prom_argv[actr]);
                cp += strlen(prom_argv[actr]);
                *cp++ = ' ';
                actr++;
@@ -70,10 +67,8 @@ void  prom_init_cmdline(void)
                --cp;
        if (prom_argc > 1)
                *cp = '\0';
-
 }
 
-
 char *prom_getenv(char *envname)
 {
        /*
@@ -95,21 +90,23 @@ char *prom_getenv(char *envname)
                }
                env++;
        }
+
        return NULL;
 }
 
-inline unsigned char str2hexnum(unsigned char c)
+static inline unsigned char str2hexnum(unsigned char c)
 {
-       if(c >= '0' && c <= '9')
+       if (c >= '0' && c <= '9')
                return c - '0';
-       if(c >= 'a' && c <= 'f')
+       if (c >= 'a' && c <= 'f')
                return c - 'a' + 10;
-       if(c >= 'A' && c <= 'F')
+       if (c >= 'A' && c <= 'F')
                return c - 'A' + 10;
+
        return 0; /* foo */
 }
 
-inline void str2eaddr(unsigned char *ea, unsigned char *str)
+static inline void str2eaddr(unsigned char *ea, unsigned char *str)
 {
        int i;
 
@@ -124,35 +121,29 @@ inline void str2eaddr(unsigned char *ea, unsigned char *str)
        }
 }
 
-int get_ethernet_addr(char *ethernet_addr)
+int prom_get_ethernet_addr(char *ethernet_addr)
 {
-        char *ethaddr_str;
+       char *ethaddr_str;
+       char *argptr;
 
-        ethaddr_str = prom_getenv("ethaddr");
+       /* Check the environment variables first */
+       ethaddr_str = prom_getenv("ethaddr");
        if (!ethaddr_str) {
-               printk("ethaddr not set in boot prom\n");
-               return -1;
-       }
-       str2eaddr(ethernet_addr, ethaddr_str);
-
-#if 0
-       {
-               int i;
+               /* Check command line */
+               argptr = prom_getcmdline();
+               ethaddr_str = strstr(argptr, "ethaddr=");
+               if (!ethaddr_str)
+                       return -1;
 
-       printk("get_ethernet_addr: ");
-       for (i=0; i<5; i++)
-               printk("%02x:", (unsigned char)*(ethernet_addr+i));
-       printk("%02x\n", *(ethernet_addr+i));
+               ethaddr_str += strlen("ethaddr=");
        }
-#endif
+
+       str2eaddr(ethernet_addr, ethaddr_str);
 
        return 0;
 }
+EXPORT_SYMBOL(prom_get_ethernet_addr);
 
 void __init prom_free_prom_memory(void)
 {
 }
-
-EXPORT_SYMBOL(prom_getcmdline);
-EXPORT_SYMBOL(get_ethernet_addr);
-EXPORT_SYMBOL(str2eaddr);
index b212c07..a90d425 100644 (file)
 #include <asm/mipsregs.h>
 #include <asm/reboot.h>
 #include <asm/pgtable.h>
-#include <asm/mach-au1x00/au1000.h>
 #include <asm/time.h>
 
-extern char * prom_getcmdline(void);
+#include <au1000.h>
+#include <prom.h>
+
 extern void __init board_setup(void);
 extern void au1000_restart(char *);
 extern void au1000_halt(void);
index 4d7bcfc..43298fd 100644 (file)
 #include <linux/mm.h>
 #include <linux/sched.h>
 #include <linux/bootmem.h>
-#include <asm/addrspace.h>
-#include <asm/bootinfo.h>
 #include <linux/string.h>
 #include <linux/kernel.h>
 
-int prom_argc;
-char **prom_argv, **prom_envp;
-extern void  __init prom_init_cmdline(void);
-extern char *prom_getenv(char *envname);
+#include <asm/addrspace.h>
+#include <asm/bootinfo.h>
+
+#include <prom.h>
 
 const char *get_system_type(void)
 {
index 2aa7b2e..cdeae32 100644 (file)
 #include <linux/init.h>
 #include <linux/mm.h>
 #include <linux/bootmem.h>
+
 #include <asm/addrspace.h>
 #include <asm/bootinfo.h>
 
-int prom_argc;
-char **prom_argv, **prom_envp;
-extern void  __init prom_init_cmdline(void);
-extern char *prom_getenv(char *envname);
+#include <prom.h>
 
 const char *get_system_type(void)
 {
index 4535f72..ddccaf6 100644 (file)
 #include <linux/mm.h>
 #include <linux/sched.h>
 #include <linux/bootmem.h>
-#include <asm/addrspace.h>
-#include <asm/bootinfo.h>
 #include <linux/string.h>
 #include <linux/kernel.h>
 
-int prom_argc;
-char **prom_argv, **prom_envp;
-extern void  __init prom_init_cmdline(void);
-extern char *prom_getenv(char *envname);
+#include <asm/addrspace.h>
+#include <asm/bootinfo.h>
+
+#include <prom.h>
 
 const char *get_system_type(void)
 {
index 7ba6852..c93fd39 100644 (file)
 #include <linux/mm.h>
 #include <linux/sched.h>
 #include <linux/bootmem.h>
-#include <asm/addrspace.h>
-#include <asm/bootinfo.h>
 #include <linux/string.h>
 #include <linux/kernel.h>
 
-int prom_argc;
-char **prom_argv, **prom_envp;
-extern void  __init prom_init_cmdline(void);
-extern char *prom_getenv(char *envname);
+#include <asm/addrspace.h>
+#include <asm/bootinfo.h>
+
+#include <prom.h>
 
 const char *get_system_type(void)
 {
index 2122515..5dbc986 100644 (file)
 #include <asm/mipsregs.h>
 #include <asm/reboot.h>
 #include <asm/pgtable.h>
-#include <asm/mach-au1x00/au1000.h>
-#include <asm/mach-au1x00/au1xxx_dbdma.h>
+
+#include <au1000.h>
+#include <au1xxx_dbdma.h>
+#include <prom.h>
 
 #ifdef CONFIG_MIPS_PB1200
 #include <asm/mach-pb1x00/pb1200.h>
index 5a70029..c251570 100644 (file)
 #include <linux/mm.h>
 #include <linux/sched.h>
 #include <linux/bootmem.h>
-#include <asm/addrspace.h>
-#include <asm/bootinfo.h>
 #include <linux/string.h>
 #include <linux/kernel.h>
 
-int prom_argc;
-char **prom_argv, **prom_envp;
-extern void  __init prom_init_cmdline(void);
-extern char *prom_getenv(char *envname);
+#include <asm/addrspace.h>
+#include <asm/bootinfo.h>
+
+#include <prom.h>
 
 const char *get_system_type(void)
 {
index e58a9d6..507d4b2 100644 (file)
 #include <linux/mm.h>
 #include <linux/sched.h>
 #include <linux/bootmem.h>
-#include <asm/addrspace.h>
-#include <asm/bootinfo.h>
 #include <linux/string.h>
 #include <linux/kernel.h>
 
-int prom_argc;
-char **prom_argv, **prom_envp;
-extern void  __init prom_init_cmdline(void);
-extern char *prom_getenv(char *envname);
+#include <asm/addrspace.h>
+#include <asm/bootinfo.h>
+
+#include <prom.h>
 
 const char *get_system_type(void)
 {
index fad53bf..b03eee6 100644 (file)
 #include <linux/mm.h>
 #include <linux/sched.h>
 #include <linux/bootmem.h>
-#include <asm/addrspace.h>
-#include <asm/bootinfo.h>
 #include <linux/string.h>
 #include <linux/kernel.h>
 
-int prom_argc;
-char **prom_argv, **prom_envp;
-extern void  __init prom_init_cmdline(void);
-extern char *prom_getenv(char *envname);
+#include <asm/addrspace.h>
+#include <asm/bootinfo.h>
+
+#include <prom.h>
 
 const char *get_system_type(void)
 {
index 9f839c3..6532939 100644 (file)
 #include <linux/mm.h>
 #include <linux/sched.h>
 #include <linux/bootmem.h>
-#include <asm/addrspace.h>
-#include <asm/bootinfo.h>
 #include <linux/string.h>
 #include <linux/kernel.h>
 
-int prom_argc;
-char **prom_argv, **prom_envp;
-extern void  __init prom_init_cmdline(void);
-extern char *prom_getenv(char *envname);
+#include <asm/addrspace.h>
+#include <asm/bootinfo.h>
+
+#include <prom.h>
 
 const char *get_system_type(void)
 {
index 1245b2f..095988f 100644 (file)
@@ -77,12 +77,7 @@ static void msic_dcr_write(struct axon_msic *msic, unsigned int dcr_n, u32 val)
 {
        pr_debug("axon_msi: dcr_write(0x%x, 0x%x)\n", val, dcr_n);
 
-       dcr_write(msic->dcr_host, msic->dcr_host.base + dcr_n, val);
-}
-
-static u32 msic_dcr_read(struct axon_msic *msic, unsigned int dcr_n)
-{
-       return dcr_read(msic->dcr_host, msic->dcr_host.base + dcr_n);
+       dcr_write(msic->dcr_host, dcr_n, val);
 }
 
 static void axon_msi_cascade(unsigned int irq, struct irq_desc *desc)
@@ -91,7 +86,7 @@ static void axon_msi_cascade(unsigned int irq, struct irq_desc *desc)
        u32 write_offset, msi;
        int idx;
 
-       write_offset = msic_dcr_read(msic, MSIC_WRITE_OFFSET_REG);
+       write_offset = dcr_read(msic->dcr_host, MSIC_WRITE_OFFSET_REG);
        pr_debug("axon_msi: original write_offset 0x%x\n", write_offset);
 
        /* write_offset doesn't wrap properly, so we have to mask it */
@@ -306,7 +301,7 @@ static int axon_msi_notify_reboot(struct notifier_block *nb,
        list_for_each_entry(msic, &axon_msic_list, list) {
                pr_debug("axon_msi: disabling %s\n",
                          msic->irq_host->of_node->full_name);
-               tmp  = msic_dcr_read(msic, MSIC_CTRL_REG);
+               tmp  = dcr_read(msic->dcr_host, MSIC_CTRL_REG);
                tmp &= ~MSIC_CTRL_ENABLE & ~MSIC_CTRL_IRQ_ENABLE;
                msic_dcr_write(msic, MSIC_CTRL_REG, tmp);
        }
index ab11c0b..427027c 100644 (file)
@@ -126,13 +126,13 @@ dcr_host_t dcr_map(struct device_node *dev, unsigned int dcr_n,
 }
 EXPORT_SYMBOL_GPL(dcr_map);
 
-void dcr_unmap(dcr_host_t host, unsigned int dcr_n, unsigned int dcr_c)
+void dcr_unmap(dcr_host_t host, unsigned int dcr_c)
 {
        dcr_host_t h = host;
 
        if (h.token == NULL)
                return;
-       h.token += dcr_n * h.stride;
+       h.token += host.base * h.stride;
        iounmap(h.token);
        h.token = NULL;
 }
index 893e654..e479388 100644 (file)
@@ -156,7 +156,7 @@ static inline u32 _mpic_read(enum mpic_reg_type type,
        switch(type) {
 #ifdef CONFIG_PPC_DCR
        case mpic_access_dcr:
-               return dcr_read(rb->dhost, rb->dhost.base + reg);
+               return dcr_read(rb->dhost, reg);
 #endif
        case mpic_access_mmio_be:
                return in_be32(rb->base + (reg >> 2));
@@ -173,7 +173,7 @@ static inline void _mpic_write(enum mpic_reg_type type,
        switch(type) {
 #ifdef CONFIG_PPC_DCR
        case mpic_access_dcr:
-               return dcr_write(rb->dhost, rb->dhost.base + reg, value);
+               return dcr_write(rb->dhost, reg, value);
 #endif
        case mpic_access_mmio_be:
                return out_be32(rb->base + (reg >> 2), value);
index f3bceb1..139ca15 100644 (file)
@@ -68,9 +68,15 @@ STACK_SIZE  = 1 << STACK_SHIFT
        l       %r1,BASED(.Ltrace_irq_off)
        basr    %r14,%r1
        .endm
+
+       .macro  LOCKDEP_SYS_EXIT
+       l       %r1,BASED(.Llockdep_sys_exit)
+       basr    %r14,%r1
+       .endm
 #else
 #define TRACE_IRQS_ON
 #define TRACE_IRQS_OFF
+#define LOCKDEP_SYS_EXIT
 #endif
 
 /*
@@ -260,6 +266,7 @@ sysc_return:
        bno     BASED(sysc_leave)
        tm      __TI_flags+3(%r9),_TIF_WORK_SVC
        bnz     BASED(sysc_work)  # there is work to do (signals etc.)
+       LOCKDEP_SYS_EXIT
 sysc_leave:
        RESTORE_ALL __LC_RETURN_PSW,1
 
@@ -283,6 +290,7 @@ sysc_work:
        bo      BASED(sysc_restart)
        tm      __TI_flags+3(%r9),_TIF_SINGLE_STEP
        bo      BASED(sysc_singlestep)
+       LOCKDEP_SYS_EXIT
        b       BASED(sysc_leave)
 
 #
@@ -572,6 +580,7 @@ io_return:
 #endif
        tm      __TI_flags+3(%r9),_TIF_WORK_INT
        bnz     BASED(io_work)          # there is work to do (signals etc.)
+       LOCKDEP_SYS_EXIT
 io_leave:
        RESTORE_ALL __LC_RETURN_PSW,0
 io_done:
@@ -618,6 +627,7 @@ io_work_loop:
        bo      BASED(io_reschedule)
        tm      __TI_flags+3(%r9),(_TIF_SIGPENDING | _TIF_RESTORE_SIGMASK)
        bnz     BASED(io_sigpending)
+       LOCKDEP_SYS_EXIT
        b       BASED(io_leave)
 
 #
@@ -1040,6 +1050,8 @@ cleanup_io_leave_insn:
 .Ltrace_irq_on: .long  trace_hardirqs_on
 .Ltrace_irq_off:
                .long   trace_hardirqs_off
+.Llockdep_sys_exit:
+               .long   lockdep_sys_exit
 #endif
 .Lcritical_start:
                .long   __critical_start + 0x80000000
index 9c0d5cc..05e26d1 100644 (file)
@@ -66,9 +66,14 @@ _TIF_WORK_INT = (_TIF_SIGPENDING | _TIF_RESTORE_SIGMASK | _TIF_NEED_RESCHED | \
        .macro  TRACE_IRQS_OFF
         brasl  %r14,trace_hardirqs_off
        .endm
+
+       .macro  LOCKDEP_SYS_EXIT
+        brasl  %r14,lockdep_sys_exit
+       .endm
 #else
 #define TRACE_IRQS_ON
 #define TRACE_IRQS_OFF
+#define LOCKDEP_SYS_EXIT
 #endif
 
        .macro  STORE_TIMER lc_offset
@@ -255,6 +260,7 @@ sysc_return:
        jno     sysc_leave
        tm      __TI_flags+7(%r9),_TIF_WORK_SVC
        jnz     sysc_work       # there is work to do (signals etc.)
+       LOCKDEP_SYS_EXIT
 sysc_leave:
        RESTORE_ALL __LC_RETURN_PSW,1
 
@@ -278,6 +284,7 @@ sysc_work:
        jo      sysc_restart
        tm      __TI_flags+7(%r9),_TIF_SINGLE_STEP
        jo      sysc_singlestep
+       LOCKDEP_SYS_EXIT
        j       sysc_leave
 
 #
@@ -558,6 +565,7 @@ io_return:
 #endif
        tm      __TI_flags+7(%r9),_TIF_WORK_INT
        jnz     io_work                 # there is work to do (signals etc.)
+       LOCKDEP_SYS_EXIT
 io_leave:
        RESTORE_ALL __LC_RETURN_PSW,0
 io_done:
@@ -605,6 +613,7 @@ io_work_loop:
        jo      io_reschedule
        tm      __TI_flags+7(%r9),(_TIF_SIGPENDING | _TIF_RESTORE_SIGMASK)
        jnz     io_sigpending
+       LOCKDEP_SYS_EXIT
        j       io_leave
 
 #
index 290b7bc..8099fea 100644 (file)
@@ -251,6 +251,7 @@ check_userspace:
        jb resume_kernel                # not returning to v8086 or userspace
 
 ENTRY(resume_userspace)
+       LOCKDEP_SYS_EXIT
        DISABLE_INTERRUPTS(CLBR_ANY)    # make sure we don't miss an interrupt
                                        # setting need_resched or sigpending
                                        # between sampling and the iret
@@ -338,6 +339,7 @@ sysenter_past_esp:
        jae syscall_badsys
        call *sys_call_table(,%eax,4)
        movl %eax,PT_EAX(%esp)
+       LOCKDEP_SYS_EXIT
        DISABLE_INTERRUPTS(CLBR_ANY)
        TRACE_IRQS_OFF
        movl TI_flags(%ebp), %ecx
@@ -377,6 +379,7 @@ syscall_call:
        call *sys_call_table(,%eax,4)
        movl %eax,PT_EAX(%esp)          # store the return value
 syscall_exit:
+       LOCKDEP_SYS_EXIT
        DISABLE_INTERRUPTS(CLBR_ANY)    # make sure we don't miss an interrupt
                                        # setting need_resched or sigpending
                                        # between sampling and the iret
@@ -467,6 +470,7 @@ work_pending:
        jz work_notifysig
 work_resched:
        call schedule
+       LOCKDEP_SYS_EXIT
        DISABLE_INTERRUPTS(CLBR_ANY)    # make sure we don't miss an interrupt
                                        # setting need_resched or sigpending
                                        # between sampling and the iret
index 1d232e5..f1cacd4 100644 (file)
@@ -244,6 +244,7 @@ ret_from_sys_call:
        movl $_TIF_ALLWORK_MASK,%edi
        /* edi: flagmask */
 sysret_check:          
+       LOCKDEP_SYS_EXIT
        GET_THREAD_INFO(%rcx)
        cli
        TRACE_IRQS_OFF
@@ -333,6 +334,7 @@ int_ret_from_sys_call:
        movl $_TIF_ALLWORK_MASK,%edi
        /* edi: mask to check */
 int_with_check:
+       LOCKDEP_SYS_EXIT_IRQ
        GET_THREAD_INFO(%rcx)
        movl threadinfo_flags(%rcx),%edx
        andl %edi,%edx
@@ -544,11 +546,13 @@ exit_intr:
 retint_with_reschedule:
        movl $_TIF_WORK_MASK,%edi
 retint_check:
+       LOCKDEP_SYS_EXIT_IRQ
        movl threadinfo_flags(%rcx),%edx
        andl %edi,%edx
        CFI_REMEMBER_STATE
        jnz  retint_careful
-retint_swapgs:         
+
+retint_swapgs:         /* return to user-space */
        /*
         * The iretq could re-enable interrupts:
         */
@@ -557,7 +561,7 @@ retint_swapgs:
        swapgs 
        jmp restore_args
 
-retint_restore_args:                           
+retint_restore_args:   /* return to kernel space */
        cli
        /*
         * The iretq could re-enable interrupts:
@@ -866,26 +870,21 @@ error_sti:
        movq ORIG_RAX(%rsp),%rsi        /* get error code */ 
        movq $-1,ORIG_RAX(%rsp)
        call *%rax
-       /* ebx: no swapgs flag (1: don't need swapgs, 0: need it) */     
-error_exit:            
-       movl %ebx,%eax          
+       /* ebx: no swapgs flag (1: don't need swapgs, 0: need it) */
+error_exit:
+       movl %ebx,%eax
        RESTORE_REST
        cli
        TRACE_IRQS_OFF
        GET_THREAD_INFO(%rcx)   
        testl %eax,%eax
        jne  retint_kernel
+       LOCKDEP_SYS_EXIT_IRQ
        movl  threadinfo_flags(%rcx),%edx
        movl  $_TIF_WORK_MASK,%edi
        andl  %edi,%edx
        jnz  retint_careful
-       /*
-        * The iret might restore flags:
-        */
-       TRACE_IRQS_IRETQ
-       swapgs 
-       RESTORE_ARGS 0,8,0                                              
-       jmp iret_label
+       jmp retint_swapgs
        CFI_ENDPROC
 
 error_kernelspace:
index c2d03e9..e7d0d3c 100644 (file)
@@ -557,6 +557,12 @@ static int __kprobes post_kprobe_handler(struct pt_regs *regs)
 
        resume_execution(cur, regs, kcb);
        regs->eflags |= kcb->kprobe_saved_eflags;
+#ifdef CONFIG_TRACE_IRQFLAGS_SUPPORT
+       if (raw_irqs_disabled_flags(regs->eflags))
+               trace_hardirqs_off();
+       else
+               trace_hardirqs_on();
+#endif
 
        /*Restore back the original saved kprobes variables and continue. */
        if (kcb->kprobe_status == KPROBE_REENTER) {
@@ -694,6 +700,7 @@ int __kprobes setjmp_pre_handler(struct kprobe *p, struct pt_regs *regs)
        memcpy(kcb->jprobes_stack, (kprobe_opcode_t *)addr,
                        MIN_STACK_SIZE(addr));
        regs->eflags &= ~IF_MASK;
+       trace_hardirqs_off();
        regs->eip = (unsigned long)(jp->entry);
        return 1;
 }
index 1df17a0..62e28e5 100644 (file)
@@ -544,6 +544,12 @@ int __kprobes post_kprobe_handler(struct pt_regs *regs)
 
        resume_execution(cur, regs, kcb);
        regs->eflags |= kcb->kprobe_saved_rflags;
+#ifdef CONFIG_TRACE_IRQFLAGS_SUPPORT
+       if (raw_irqs_disabled_flags(regs->eflags))
+               trace_hardirqs_off();
+       else
+               trace_hardirqs_on();
+#endif
 
        /* Restore the original saved kprobes variables and continue. */
        if (kcb->kprobe_status == KPROBE_REENTER) {
@@ -684,6 +690,7 @@ int __kprobes setjmp_pre_handler(struct kprobe *p, struct pt_regs *regs)
        memcpy(kcb->jprobes_stack, (kprobe_opcode_t *)addr,
                        MIN_STACK_SIZE(addr));
        regs->eflags &= ~IF_MASK;
+       trace_hardirqs_off();
        regs->rip = (unsigned long)(jp->entry);
        return 1;
 }
index 55e586d..6ea73f3 100644 (file)
        thunk trace_hardirqs_on_thunk,trace_hardirqs_on
        thunk trace_hardirqs_off_thunk,trace_hardirqs_off
 #endif
+
+#ifdef CONFIG_DEBUG_LOCK_ALLOC
+       thunk lockdep_sys_exit_thunk,lockdep_sys_exit
+#endif
        
        /* SAVE_ARGS below is used only for the .cfi directives it contains. */
        CFI_STARTPROC
index 4672066..33f5eb0 100644 (file)
@@ -272,6 +272,15 @@ config PATA_CS5535
 
          If unsure, say N.
 
+config PATA_CS5536
+       tristate "CS5536 PATA support (Experimental)"
+       depends on PCI && X86 && !X86_64 && EXPERIMENTAL
+       help
+         This option enables support for the AMD CS5536
+         companion chip used with the Geode LX processor family.
+
+         If unsure, say N.
+
 config PATA_CYPRESS
        tristate "Cypress CY82C693 PATA support (Very Experimental)"
        depends on PCI && EXPERIMENTAL
index 2a63645..6bdc307 100644 (file)
@@ -28,6 +28,7 @@ obj-$(CONFIG_PATA_CMD64X)     += pata_cmd64x.o
 obj-$(CONFIG_PATA_CS5520)      += pata_cs5520.o
 obj-$(CONFIG_PATA_CS5530)      += pata_cs5530.o
 obj-$(CONFIG_PATA_CS5535)      += pata_cs5535.o
+obj-$(CONFIG_PATA_CS5536)      += pata_cs5536.o
 obj-$(CONFIG_PATA_CYPRESS)     += pata_cypress.o
 obj-$(CONFIG_PATA_EFAR)                += pata_efar.o
 obj-$(CONFIG_PATA_HPT366)      += pata_hpt366.o
index 9ce4aa9..3c6f43e 100644 (file)
@@ -130,6 +130,7 @@ enum {
        ich8_sata_ahci          = 9,
        piix_pata_mwdma         = 10,   /* PIIX3 MWDMA only */
        tolapai_sata_ahci       = 11,
+       ich9_2port_sata         = 12,
 
        /* constants for mapping table */
        P0                      = 0,  /* port 0 */
@@ -238,19 +239,19 @@ static const struct pci_device_id piix_pci_tbl[] = {
        /* SATA Controller 1 IDE (ICH8) */
        { 0x8086, 0x2820, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich8_sata_ahci },
        /* SATA Controller 2 IDE (ICH8) */
-       { 0x8086, 0x2825, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich8_sata_ahci },
+       { 0x8086, 0x2825, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich9_2port_sata },
        /* Mobile SATA Controller IDE (ICH8M) */
        { 0x8086, 0x2828, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich8_sata_ahci },
        /* SATA Controller IDE (ICH9) */
        { 0x8086, 0x2920, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich8_sata_ahci },
        /* SATA Controller IDE (ICH9) */
-       { 0x8086, 0x2921, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich8_sata_ahci },
+       { 0x8086, 0x2921, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich9_2port_sata },
        /* SATA Controller IDE (ICH9) */
-       { 0x8086, 0x2926, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich8_sata_ahci },
+       { 0x8086, 0x2926, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich9_2port_sata },
        /* SATA Controller IDE (ICH9M) */
-       { 0x8086, 0x2928, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich8_sata_ahci },
+       { 0x8086, 0x2928, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich9_2port_sata },
        /* SATA Controller IDE (ICH9M) */
-       { 0x8086, 0x292d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich8_sata_ahci },
+       { 0x8086, 0x292d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich9_2port_sata },
        /* SATA Controller IDE (ICH9M) */
        { 0x8086, 0x292e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich8_sata_ahci },
        /* SATA Controller IDE (Tolapai) */
@@ -448,6 +449,18 @@ static const struct piix_map_db tolapai_map_db = {
        },
 };
 
+static const struct piix_map_db ich9_2port_map_db = {
+       .mask = 0x3,
+       .port_enable = 0x3,
+       .map = {
+               /* PM   PS   SM   SS       MAP */
+               {  P0,  NA,  P1,  NA }, /* 00b */
+               {  RV,  RV,  RV,  RV }, /* 01b */
+               {  RV,  RV,  RV,  RV }, /* 10b */
+               {  RV,  RV,  RV,  RV },
+       },
+};
+
 static const struct piix_map_db *piix_map_db_table[] = {
        [ich5_sata]             = &ich5_map_db,
        [ich6_sata]             = &ich6_map_db,
@@ -455,6 +468,7 @@ static const struct piix_map_db *piix_map_db_table[] = {
        [ich6m_sata_ahci]       = &ich6m_map_db,
        [ich8_sata_ahci]        = &ich8_map_db,
        [tolapai_sata_ahci]     = &tolapai_map_db,
+       [ich9_2port_sata]       = &ich9_2port_map_db,
 };
 
 static struct ata_port_info piix_port_info[] = {
@@ -570,6 +584,17 @@ static struct ata_port_info piix_port_info[] = {
                .udma_mask      = ATA_UDMA6,
                .port_ops       = &piix_sata_ops,
        },
+
+       [ich9_2port_sata] =
+       {
+               .sht            = &piix_sht,
+               .flags          = PIIX_SATA_FLAGS | PIIX_FLAG_SCR |
+                                 PIIX_FLAG_AHCI,
+               .pio_mask       = 0x1f, /* pio0-4 */
+               .mwdma_mask     = 0x07, /* mwdma0-2 */
+               .udma_mask      = ATA_UDMA6,
+               .port_ops       = &piix_sata_ops,
+       },
 };
 
 static struct pci_bits piix_enable_bits[] = {
index b05384a..68699b3 100644 (file)
@@ -3984,6 +3984,7 @@ static const struct ata_blacklist_entry ata_device_blacklist [] = {
        { "ST9120822AS",        "3.CLF",        ATA_HORKAGE_NONCQ, },
        { "ST9160821AS",        "3.CLF",        ATA_HORKAGE_NONCQ, },
        { "ST9160821AS",        "3.ALD",        ATA_HORKAGE_NONCQ, },
+       { "ST9160821AS",        "3.CCD",        ATA_HORKAGE_NONCQ, },
        { "ST3160812AS",        "3.ADJ",        ATA_HORKAGE_NONCQ, },
        { "ST980813AS",         "3.ADB",        ATA_HORKAGE_NONCQ, },
        { "SAMSUNG HD401LJ",    "ZZ100-15",     ATA_HORKAGE_NONCQ, },
@@ -4013,8 +4014,14 @@ int strn_pattern_cmp(const char *patt, const char *name, int wildchar)
        p = strchr(patt, wildchar);
        if (p && ((*(p + 1)) == 0))
                len = p - patt;
-       else
+       else {
                len = strlen(name);
+               if (!len) {
+                       if (!*patt)
+                               return 0;
+                       return -1;
+               }
+       }
 
        return strncmp(patt, name, len);
 }
index ea53e6a..d63c81e 100644 (file)
@@ -1363,6 +1363,7 @@ nothing_to_do:
 static void ata_scsi_qc_complete(struct ata_queued_cmd *qc)
 {
        struct ata_port *ap = qc->ap;
+       struct ata_eh_info *ehi = &qc->dev->link->eh_info;
        struct scsi_cmnd *cmd = qc->scsicmd;
        u8 *cdb = cmd->cmnd;
        int need_sense = (qc->err_mask != 0);
@@ -1376,14 +1377,14 @@ static void ata_scsi_qc_complete(struct ata_queued_cmd *qc)
                case ATA_CMD_SET_FEATURES:
                        if ((qc->tf.feature == SETFEATURES_WC_ON) ||
                            (qc->tf.feature == SETFEATURES_WC_OFF)) {
-                               ap->link.eh_info.action |= ATA_EH_REVALIDATE;
+                               ehi->action |= ATA_EH_REVALIDATE;
                                ata_port_schedule_eh(ap);
                        }
                        break;
 
                case ATA_CMD_INIT_DEV_PARAMS: /* CHS translation changed */
                case ATA_CMD_SET_MULTI: /* multi_count changed */
-                       ap->link.eh_info.action |= ATA_EH_REVALIDATE;
+                       ehi->action |= ATA_EH_REVALIDATE;
                        ata_port_schedule_eh(ap);
                        break;
                }
diff --git a/drivers/ata/pata_cs5536.c b/drivers/ata/pata_cs5536.c
new file mode 100644 (file)
index 0000000..53070f6
--- /dev/null
@@ -0,0 +1,344 @@
+/*
+ * pata_cs5536.c       - CS5536 PATA for new ATA layer
+ *                       (C) 2007 Martin K. Petersen <mkp@mkp.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.         See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307         USA
+ *
+ * Documentation:
+ *     Available from AMD web site.
+ *
+ * The IDE timing registers for the CS5536 live in the Geode Machine
+ * Specific Register file and not PCI config space.  Most BIOSes
+ * virtualize the PCI registers so the chip looks like a standard IDE
+ * controller. Unfortunately not all implementations get this right.
+ * In particular some have problems with unaligned accesses to the
+ * virtualized PCI registers.  This driver always does full dword
+ * writes to work around the issue.  Also, in case of a bad BIOS this
+ * driver can be loaded with the "msr=1" parameter which forces using
+ * the Machine Specific Registers to configure the device.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/blkdev.h>
+#include <linux/delay.h>
+#include <linux/libata.h>
+#include <scsi/scsi_host.h>
+#include <asm/msr.h>
+
+#define DRV_NAME       "pata_cs5536"
+#define DRV_VERSION    "0.0.5"
+
+enum {
+       CFG                     = 0,
+       DTC                     = 1,
+       CAST                    = 2,
+       ETC                     = 3,
+
+       MSR_IDE_BASE            = 0x51300000,
+       MSR_IDE_CFG             = (MSR_IDE_BASE + 0x10),
+       MSR_IDE_DTC             = (MSR_IDE_BASE + 0x12),
+       MSR_IDE_CAST            = (MSR_IDE_BASE + 0x13),
+       MSR_IDE_ETC             = (MSR_IDE_BASE + 0x14),
+
+       PCI_IDE_CFG             = 0x40,
+       PCI_IDE_DTC             = 0x48,
+       PCI_IDE_CAST            = 0x4c,
+       PCI_IDE_ETC             = 0x50,
+
+       IDE_CFG_CHANEN          = 0x2,
+       IDE_CFG_CABLE           = 0x10000,
+
+       IDE_D0_SHIFT            = 24,
+       IDE_D1_SHIFT            = 16,
+       IDE_DRV_MASK            = 0xff,
+
+       IDE_CAST_D0_SHIFT       = 6,
+       IDE_CAST_D1_SHIFT       = 4,
+       IDE_CAST_DRV_MASK       = 0x3,
+       IDE_CAST_CMD_MASK       = 0xff,
+       IDE_CAST_CMD_SHIFT      = 24,
+
+       IDE_ETC_NODMA           = 0x03,
+};
+
+static int use_msr;
+
+static const u32 msr_reg[4] = {
+       MSR_IDE_CFG, MSR_IDE_DTC, MSR_IDE_CAST, MSR_IDE_ETC,
+};
+
+static const u8 pci_reg[4] = {
+       PCI_IDE_CFG, PCI_IDE_DTC, PCI_IDE_CAST, PCI_IDE_ETC,
+};
+
+static inline int cs5536_read(struct pci_dev *pdev, int reg, int *val)
+{
+       if (unlikely(use_msr)) {
+               u32 dummy;
+
+               rdmsr(msr_reg[reg], *val, dummy);
+               return 0;
+       }
+
+       return pci_read_config_dword(pdev, pci_reg[reg], val);
+}
+
+static inline int cs5536_write(struct pci_dev *pdev, int reg, int val)
+{
+       if (unlikely(use_msr)) {
+               wrmsr(msr_reg[reg], val, 0);
+               return 0;
+       }
+
+       return pci_write_config_dword(pdev, pci_reg[reg], val);
+}
+
+/**
+ *     cs5536_cable_detect     -       detect cable type
+ *     @ap: Port to detect on
+ *     @deadline: deadline jiffies for the operation
+ *
+ *     Perform cable detection for ATA66 capable cable. Return a libata
+ *     cable type.
+ */
+
+static int cs5536_cable_detect(struct ata_port *ap)
+{
+       struct pci_dev *pdev = to_pci_dev(ap->host->dev);
+       u32 cfg;
+
+       cs5536_read(pdev, CFG, &cfg);
+
+       if (cfg & (IDE_CFG_CABLE << ap->port_no))
+               return ATA_CBL_PATA80;
+       else
+               return ATA_CBL_PATA40;
+}
+
+/**
+ *     cs5536_set_piomode              -       PIO setup
+ *     @ap: ATA interface
+ *     @adev: device on the interface
+ */
+
+static void cs5536_set_piomode(struct ata_port *ap, struct ata_device *adev)
+{
+       static const u8 drv_timings[5] = {
+               0x98, 0x55, 0x32, 0x21, 0x20,
+       };
+
+       static const u8 addr_timings[5] = {
+               0x2, 0x1, 0x0, 0x0, 0x0,
+       };
+
+       static const u8 cmd_timings[5] = {
+               0x99, 0x92, 0x90, 0x22, 0x20,
+       };
+
+       struct pci_dev *pdev = to_pci_dev(ap->host->dev);
+       struct ata_device *pair = ata_dev_pair(adev);
+       int mode = adev->pio_mode - XFER_PIO_0;
+       int cmdmode = mode;
+       int dshift = ap->port_no ? IDE_D1_SHIFT : IDE_D0_SHIFT;
+       int cshift = ap->port_no ? IDE_CAST_D1_SHIFT : IDE_CAST_D0_SHIFT;
+       u32 dtc, cast, etc;
+
+       if (pair)
+               cmdmode = min(mode, pair->pio_mode - XFER_PIO_0);
+
+       cs5536_read(pdev, DTC, &dtc);
+       cs5536_read(pdev, CAST, &cast);
+       cs5536_read(pdev, ETC, &etc);
+
+       dtc &= ~(IDE_DRV_MASK << dshift);
+       dtc |= drv_timings[mode] << dshift;
+
+       cast &= ~(IDE_CAST_DRV_MASK << cshift);
+       cast |= addr_timings[mode] << cshift;
+
+       cast &= ~(IDE_CAST_CMD_MASK << IDE_CAST_CMD_SHIFT);
+       cast |= cmd_timings[cmdmode] << IDE_CAST_CMD_SHIFT;
+
+       etc &= ~(IDE_DRV_MASK << dshift);
+       etc |= IDE_ETC_NODMA << dshift;
+
+       cs5536_write(pdev, DTC, dtc);
+       cs5536_write(pdev, CAST, cast);
+       cs5536_write(pdev, ETC, etc);
+}
+
+/**
+ *     cs5536_set_dmamode              -       DMA timing setup
+ *     @ap: ATA interface
+ *     @adev: Device being configured
+ *
+ */
+
+static void cs5536_set_dmamode(struct ata_port *ap, struct ata_device *adev)
+{
+       static const u8 udma_timings[6] = {
+               0xc2, 0xc1, 0xc0, 0xc4, 0xc5, 0xc6,
+       };
+
+       static const u8 mwdma_timings[3] = {
+               0x67, 0x21, 0x20,
+       };
+
+       struct pci_dev *pdev = to_pci_dev(ap->host->dev);
+       u32 dtc, etc;
+       int mode = adev->dma_mode;
+       int dshift = ap->port_no ? IDE_D1_SHIFT : IDE_D0_SHIFT;
+
+       if (mode >= XFER_UDMA_0) {
+               cs5536_read(pdev, ETC, &etc);
+
+               etc &= ~(IDE_DRV_MASK << dshift);
+               etc |= udma_timings[mode - XFER_UDMA_0] << dshift;
+
+               cs5536_write(pdev, ETC, etc);
+       } else { /* MWDMA */
+               cs5536_read(pdev, DTC, &dtc);
+
+               dtc &= ~(IDE_DRV_MASK << dshift);
+               dtc |= mwdma_timings[mode] << dshift;
+
+               cs5536_write(pdev, DTC, dtc);
+       }
+}
+
+static struct scsi_host_template cs5536_sht = {
+       .module                 = THIS_MODULE,
+       .name                   = DRV_NAME,
+       .ioctl                  = ata_scsi_ioctl,
+       .queuecommand           = ata_scsi_queuecmd,
+       .can_queue              = ATA_DEF_QUEUE,
+       .this_id                = ATA_SHT_THIS_ID,
+       .sg_tablesize           = LIBATA_MAX_PRD,
+       .cmd_per_lun            = ATA_SHT_CMD_PER_LUN,
+       .emulated               = ATA_SHT_EMULATED,
+       .use_clustering         = ATA_SHT_USE_CLUSTERING,
+       .proc_name              = DRV_NAME,
+       .dma_boundary           = ATA_DMA_BOUNDARY,
+       .slave_configure        = ata_scsi_slave_config,
+       .slave_destroy          = ata_scsi_slave_destroy,
+       .bios_param             = ata_std_bios_param,
+};
+
+static struct ata_port_operations cs5536_port_ops = {
+       .set_piomode            = cs5536_set_piomode,
+       .set_dmamode            = cs5536_set_dmamode,
+       .mode_filter            = ata_pci_default_filter,
+
+       .tf_load                = ata_tf_load,
+       .tf_read                = ata_tf_read,
+       .check_status           = ata_check_status,
+       .exec_command           = ata_exec_command,
+       .dev_select             = ata_std_dev_select,
+
+       .freeze                 = ata_bmdma_freeze,
+       .thaw                   = ata_bmdma_thaw,
+       .error_handler          = ata_bmdma_error_handler,
+       .post_internal_cmd      = ata_bmdma_post_internal_cmd,
+       .cable_detect           = cs5536_cable_detect,
+
+       .bmdma_setup            = ata_bmdma_setup,
+       .bmdma_start            = ata_bmdma_start,
+       .bmdma_stop             = ata_bmdma_stop,
+       .bmdma_status           = ata_bmdma_status,
+
+       .qc_prep                = ata_qc_prep,
+       .qc_issue               = ata_qc_issue_prot,
+
+       .data_xfer              = ata_data_xfer,
+
+       .irq_handler            = ata_interrupt,
+       .irq_clear              = ata_bmdma_irq_clear,
+       .irq_on                 = ata_irq_on,
+
+       .port_start             = ata_port_start,
+};
+
+/**
+ *     cs5536_init_one
+ *     @dev: PCI device
+ *     @id: Entry in match table
+ *
+ */
+
+static int cs5536_init_one(struct pci_dev *dev, const struct pci_device_id *id)
+{
+       static const struct ata_port_info info = {
+               .sht = &cs5536_sht,
+               .flags = ATA_FLAG_SLAVE_POSS,
+               .pio_mask = 0x1f,
+               .mwdma_mask = 0x07,
+               .udma_mask = ATA_UDMA5,
+               .port_ops = &cs5536_port_ops,
+       };
+
+       const struct ata_port_info *ppi[] = { &info, &ata_dummy_port_info };
+       u32 cfg;
+
+       if (use_msr)
+               printk(KERN_ERR DRV_NAME ": Using MSR regs instead of PCI\n");
+
+       cs5536_read(dev, CFG, &cfg);
+
+       if ((cfg & IDE_CFG_CHANEN) == 0) {
+               printk(KERN_ERR DRV_NAME ": disabled by BIOS\n");
+               return -ENODEV;
+       }
+
+       return ata_pci_init_one(dev, ppi);
+}
+
+static const struct pci_device_id cs5536[] = {
+       { PCI_VDEVICE(AMD,      PCI_DEVICE_ID_AMD_CS5536_IDE), },
+       { },
+};
+
+static struct pci_driver cs5536_pci_driver = {
+       .name           = DRV_NAME,
+       .id_table       = cs5536,
+       .probe          = cs5536_init_one,
+       .remove         = ata_pci_remove_one,
+#ifdef CONFIG_PM
+       .suspend        = ata_pci_device_suspend,
+       .resume         = ata_pci_device_resume,
+#endif
+};
+
+static int __init cs5536_init(void)
+{
+       return pci_register_driver(&cs5536_pci_driver);
+}
+
+static void __exit cs5536_exit(void)
+{
+       pci_unregister_driver(&cs5536_pci_driver);
+}
+
+MODULE_AUTHOR("Martin K. Petersen");
+MODULE_DESCRIPTION("low-level driver for the CS5536 IDE controller");
+MODULE_LICENSE("GPL");
+MODULE_DEVICE_TABLE(pci, cs5536);
+MODULE_VERSION(DRV_VERSION);
+module_param_named(msr, use_msr, int, 0644);
+MODULE_PARM_DESC(msr, "Force using MSR to configure IDE function (Default: 0)");
+
+module_init(cs5536_init);
+module_exit(cs5536_exit);
index 782ff4a..5db2013 100644 (file)
@@ -353,6 +353,7 @@ static void pcmcia_remove_one(struct pcmcia_device *pdev)
 
 static struct pcmcia_device_id pcmcia_devices[] = {
        PCMCIA_DEVICE_FUNC_ID(4),
+       PCMCIA_DEVICE_MANF_CARD(0x0000, 0x0000),        /* Corsair */
        PCMCIA_DEVICE_MANF_CARD(0x0007, 0x0000),        /* Hitachi */
        PCMCIA_DEVICE_MANF_CARD(0x000a, 0x0000),        /* I-O Data CFA */
        PCMCIA_DEVICE_MANF_CARD(0x001c, 0x0001),        /* Mitsubishi CFA */
@@ -378,6 +379,7 @@ static struct pcmcia_device_id pcmcia_devices[] = {
        PCMCIA_DEVICE_PROD_ID12("EXP   ", "CD-ROM", 0x0a5c52fd, 0x66536591),
        PCMCIA_DEVICE_PROD_ID12("EXP   ", "PnPIDE", 0x0a5c52fd, 0x0c694728),
        PCMCIA_DEVICE_PROD_ID12("FREECOM", "PCCARD-IDE", 0x5714cbf7, 0x48e0ab8e),
+       PCMCIA_DEVICE_PROD_ID12("Hyperstone", "Model1", 0x3d5b9ef5, 0xca6ab420),
        PCMCIA_DEVICE_PROD_ID12("HITACHI", "FLASH", 0xf4f43949, 0x9eb86aae),
        PCMCIA_DEVICE_PROD_ID12("HITACHI", "microdrive", 0xf4f43949, 0xa6d76178),
        PCMCIA_DEVICE_PROD_ID12("IBM", "microdrive", 0xb569a6e5, 0xa6d76178),
index 2eb75cd..4dc2e73 100644 (file)
@@ -279,7 +279,7 @@ static struct ata_port_operations sil680_port_ops = {
  *     Returns the final clock settings.
  */
 
-static u8 sil680_init_chip(struct pci_dev *pdev)
+static u8 sil680_init_chip(struct pci_dev *pdev, int *try_mmio)
 {
        u32 class_rev   = 0;
        u8 tmpbyte      = 0;
@@ -297,6 +297,8 @@ static u8 sil680_init_chip(struct pci_dev *pdev)
        dev_dbg(&pdev->dev, "sil680: BA5_EN = %d clock = %02X\n",
                tmpbyte & 1, tmpbyte & 0x30);
 
+       *try_mmio = (tmpbyte & 1) || pci_resource_start(pdev, 5);
+
        switch(tmpbyte & 0x30) {
                case 0x00:
                        /* 133 clock attempt to force it on */
@@ -361,25 +363,76 @@ static int __devinit sil680_init_one(struct pci_dev *pdev,
        };
        const struct ata_port_info *ppi[] = { &info, NULL };
        static int printed_version;
+       struct ata_host *host;
+       void __iomem *mmio_base;
+       int rc, try_mmio;
 
        if (!printed_version++)
                dev_printk(KERN_DEBUG, &pdev->dev, "version " DRV_VERSION "\n");
 
-       switch(sil680_init_chip(pdev))
-       {
+       switch (sil680_init_chip(pdev, &try_mmio)) {
                case 0:
                        ppi[0] = &info_slow;
                        break;
                case 0x30:
                        return -ENODEV;
        }
+
+       if (!try_mmio)
+               goto use_ioports;
+
+       /* Try to acquire MMIO resources and fallback to PIO if
+        * that fails
+        */
+       rc = pcim_enable_device(pdev);
+       if (rc)
+               return rc;
+       rc = pcim_iomap_regions(pdev, 1 << SIL680_MMIO_BAR, DRV_NAME);
+       if (rc)
+               goto use_ioports;
+
+       /* Allocate host and set it up */
+       host = ata_host_alloc_pinfo(&pdev->dev, ppi, 2);
+       if (!host)
+               return -ENOMEM;
+       host->iomap = pcim_iomap_table(pdev);
+
+       /* Setup DMA masks */
+       rc = pci_set_dma_mask(pdev, ATA_DMA_MASK);
+       if (rc)
+               return rc;
+       rc = pci_set_consistent_dma_mask(pdev, ATA_DMA_MASK);
+       if (rc)
+               return rc;
+       pci_set_master(pdev);
+
+       /* Get MMIO base and initialize port addresses */
+       mmio_base = host->iomap[SIL680_MMIO_BAR];
+       host->ports[0]->ioaddr.bmdma_addr = mmio_base + 0x00;
+       host->ports[0]->ioaddr.cmd_addr = mmio_base + 0x80;
+       host->ports[0]->ioaddr.ctl_addr = mmio_base + 0x8a;
+       host->ports[0]->ioaddr.altstatus_addr = mmio_base + 0x8a;
+       ata_std_ports(&host->ports[0]->ioaddr);
+       host->ports[1]->ioaddr.bmdma_addr = mmio_base + 0x08;
+       host->ports[1]->ioaddr.cmd_addr = mmio_base + 0xc0;
+       host->ports[1]->ioaddr.ctl_addr = mmio_base + 0xca;
+       host->ports[1]->ioaddr.altstatus_addr = mmio_base + 0xca;
+       ata_std_ports(&host->ports[1]->ioaddr);
+
+       /* Register & activate */
+       return ata_host_activate(host, pdev->irq, ata_interrupt, IRQF_SHARED,
+                                &sil680_sht);
+
+use_ioports:
        return ata_pci_init_one(pdev, ppi);
 }
 
 #ifdef CONFIG_PM
 static int sil680_reinit_one(struct pci_dev *pdev)
 {
-       sil680_init_chip(pdev);
+       int try_mmio;
+
+       sil680_init_chip(pdev, &try_mmio);
        return ata_pci_device_resume(pdev);
 }
 #endif
index 40557fe..240a892 100644 (file)
@@ -169,6 +169,35 @@ enum {
        NV_ADMA_PORT_REGISTER_MODE      = (1 << 0),
        NV_ADMA_ATAPI_SETUP_COMPLETE    = (1 << 1),
 
+       /* MCP55 reg offset */
+       NV_CTL_MCP55                    = 0x400,
+       NV_INT_STATUS_MCP55             = 0x440,
+       NV_INT_ENABLE_MCP55             = 0x444,
+       NV_NCQ_REG_MCP55                = 0x448,
+
+       /* MCP55 */
+       NV_INT_ALL_MCP55                = 0xffff,
+       NV_INT_PORT_SHIFT_MCP55         = 16,   /* each port occupies 16 bits */
+       NV_INT_MASK_MCP55               = NV_INT_ALL_MCP55 & 0xfffd,
+
+       /* SWNCQ ENABLE BITS*/
+       NV_CTL_PRI_SWNCQ                = 0x02,
+       NV_CTL_SEC_SWNCQ                = 0x04,
+
+       /* SW NCQ status bits*/
+       NV_SWNCQ_IRQ_DEV                = (1 << 0),
+       NV_SWNCQ_IRQ_PM                 = (1 << 1),
+       NV_SWNCQ_IRQ_ADDED              = (1 << 2),
+       NV_SWNCQ_IRQ_REMOVED            = (1 << 3),
+
+       NV_SWNCQ_IRQ_BACKOUT            = (1 << 4),
+       NV_SWNCQ_IRQ_SDBFIS             = (1 << 5),
+       NV_SWNCQ_IRQ_DHREGFIS           = (1 << 6),
+       NV_SWNCQ_IRQ_DMASETUP           = (1 << 7),
+
+       NV_SWNCQ_IRQ_HOTPLUG            = NV_SWNCQ_IRQ_ADDED |
+                                         NV_SWNCQ_IRQ_REMOVED,
+
 };
 
 /* ADMA Physical Region Descriptor - one SG segment */
@@ -226,6 +255,42 @@ struct nv_host_priv {
        unsigned long           type;
 };
 
+struct defer_queue {
+       u32             defer_bits;
+       unsigned int    head;
+       unsigned int    tail;
+       unsigned int    tag[ATA_MAX_QUEUE];
+};
+
+enum ncq_saw_flag_list {
+       ncq_saw_d2h     = (1U << 0),
+       ncq_saw_dmas    = (1U << 1),
+       ncq_saw_sdb     = (1U << 2),
+       ncq_saw_backout = (1U << 3),
+};
+
+struct nv_swncq_port_priv {
+       struct ata_prd  *prd;    /* our SG list */
+       dma_addr_t      prd_dma; /* and its DMA mapping */
+       void __iomem    *sactive_block;
+       void __iomem    *irq_block;
+       void __iomem    *tag_block;
+       u32             qc_active;
+
+       unsigned int    last_issue_tag;
+
+       /* fifo circular queue to store deferral command */
+       struct defer_queue defer_queue;
+
+       /* for NCQ interrupt analysis */
+       u32             dhfis_bits;
+       u32             dmafis_bits;
+       u32             sdbfis_bits;
+
+       unsigned int    ncq_flags;
+};
+
+
 #define NV_ADMA_CHECK_INTR(GCTL, PORT) ((GCTL) & ( 1 << (19 + (12 * (PORT)))))
 
 static int nv_init_one (struct pci_dev *pdev, const struct pci_device_id *ent);
@@ -263,13 +328,29 @@ static void nv_adma_host_stop(struct ata_host *host);
 static void nv_adma_post_internal_cmd(struct ata_queued_cmd *qc);
 static void nv_adma_tf_read(struct ata_port *ap, struct ata_taskfile *tf);
 
+static void nv_mcp55_thaw(struct ata_port *ap);
+static void nv_mcp55_freeze(struct ata_port *ap);
+static void nv_swncq_error_handler(struct ata_port *ap);
+static int nv_swncq_slave_config(struct scsi_device *sdev);
+static int nv_swncq_port_start(struct ata_port *ap);
+static void nv_swncq_qc_prep(struct ata_queued_cmd *qc);
+static void nv_swncq_fill_sg(struct ata_queued_cmd *qc);
+static unsigned int nv_swncq_qc_issue(struct ata_queued_cmd *qc);
+static void nv_swncq_irq_clear(struct ata_port *ap, u16 fis);
+static irqreturn_t nv_swncq_interrupt(int irq, void *dev_instance);
+#ifdef CONFIG_PM
+static int nv_swncq_port_suspend(struct ata_port *ap, pm_message_t mesg);
+static int nv_swncq_port_resume(struct ata_port *ap);
+#endif
+
 enum nv_host_type
 {
        GENERIC,
        NFORCE2,
        NFORCE3 = NFORCE2,      /* NF2 == NF3 as far as sata_nv is concerned */
        CK804,
-       ADMA
+       ADMA,
+       SWNCQ,
 };
 
 static const struct pci_device_id nv_pci_tbl[] = {
@@ -280,13 +361,13 @@ static const struct pci_device_id nv_pci_tbl[] = {
        { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_CK804_SATA2), CK804 },
        { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP04_SATA), CK804 },
        { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP04_SATA2), CK804 },
-       { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP51_SATA), GENERIC },
-       { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP51_SATA2), GENERIC },
-       { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP55_SATA), GENERIC },
-       { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP55_SATA2), GENERIC },
-       { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP61_SATA), GENERIC },
-       { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP61_SATA2), GENERIC },
-       { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP61_SATA3), GENERIC },
+       { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP51_SATA), SWNCQ },
+       { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP51_SATA2), SWNCQ },
+       { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP55_SATA), SWNCQ },
+       { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP55_SATA2), SWNCQ },
+       { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP61_SATA), SWNCQ },
+       { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP61_SATA2), SWNCQ },
+       { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP61_SATA3), SWNCQ },
 
        { } /* terminate list */
 };
@@ -339,6 +420,25 @@ static struct scsi_host_template nv_adma_sht = {
        .bios_param             = ata_std_bios_param,
 };
 
+static struct scsi_host_template nv_swncq_sht = {
+       .module                 = THIS_MODULE,
+       .name                   = DRV_NAME,
+       .ioctl                  = ata_scsi_ioctl,
+       .queuecommand           = ata_scsi_queuecmd,
+       .change_queue_depth     = ata_scsi_change_queue_depth,
+       .can_queue              = ATA_MAX_QUEUE,
+       .this_id                = ATA_SHT_THIS_ID,
+       .sg_tablesize           = LIBATA_MAX_PRD,
+       .cmd_per_lun            = ATA_SHT_CMD_PER_LUN,
+       .emulated               = ATA_SHT_EMULATED,
+       .use_clustering         = ATA_SHT_USE_CLUSTERING,
+       .proc_name              = DRV_NAME,
+       .dma_boundary           = ATA_DMA_BOUNDARY,
+       .slave_configure        = nv_swncq_slave_config,
+       .slave_destroy          = ata_scsi_slave_destroy,
+       .bios_param             = ata_std_bios_param,
+};
+
 static const struct ata_port_operations nv_generic_ops = {
        .tf_load                = ata_tf_load,
        .tf_read                = ata_tf_read,
@@ -444,6 +544,35 @@ static const struct ata_port_operations nv_adma_ops = {
        .host_stop              = nv_adma_host_stop,
 };
 
+static const struct ata_port_operations nv_swncq_ops = {
+       .tf_load                = ata_tf_load,
+       .tf_read                = ata_tf_read,
+       .exec_command           = ata_exec_command,
+       .check_status           = ata_check_status,
+       .dev_select             = ata_std_dev_select,
+       .bmdma_setup            = ata_bmdma_setup,
+       .bmdma_start            = ata_bmdma_start,
+       .bmdma_stop             = ata_bmdma_stop,
+       .bmdma_status           = ata_bmdma_status,
+       .qc_defer               = ata_std_qc_defer,
+       .qc_prep                = nv_swncq_qc_prep,
+       .qc_issue               = nv_swncq_qc_issue,
+       .freeze                 = nv_mcp55_freeze,
+       .thaw                   = nv_mcp55_thaw,
+       .error_handler          = nv_swncq_error_handler,
+       .post_internal_cmd      = ata_bmdma_post_internal_cmd,
+       .data_xfer              = ata_data_xfer,
+       .irq_clear              = ata_bmdma_irq_clear,
+       .irq_on                 = ata_irq_on,
+       .scr_read               = nv_scr_read,
+       .scr_write              = nv_scr_write,
+#ifdef CONFIG_PM
+       .port_suspend           = nv_swncq_port_suspend,
+       .port_resume            = nv_swncq_port_resume,
+#endif
+       .port_start             = nv_swncq_port_start,
+};
+
 static const struct ata_port_info nv_port_info[] = {
        /* generic */
        {
@@ -490,6 +619,18 @@ static const struct ata_port_info nv_port_info[] = {
                .port_ops       = &nv_adma_ops,
                .irq_handler    = nv_adma_interrupt,
        },
+       /* SWNCQ */
+       {
+               .sht            = &nv_swncq_sht,
+               .flags          = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY |
+                                 ATA_FLAG_NCQ,
+               .link_flags     = ATA_LFLAG_HRST_TO_RESUME,
+               .pio_mask       = NV_PIO_MASK,
+               .mwdma_mask     = NV_MWDMA_MASK,
+               .udma_mask      = NV_UDMA_MASK,
+               .port_ops       = &nv_swncq_ops,
+               .irq_handler    = nv_swncq_interrupt,
+       },
 };
 
 MODULE_AUTHOR("NVIDIA");
@@ -499,6 +640,7 @@ MODULE_DEVICE_TABLE(pci, nv_pci_tbl);
 MODULE_VERSION(DRV_VERSION);
 
 static int adma_enabled = 1;
+static int swncq_enabled;
 
 static void nv_adma_register_mode(struct ata_port *ap)
 {
@@ -1452,6 +1594,34 @@ static void nv_ck804_thaw(struct ata_port *ap)
        writeb(mask, mmio_base + NV_INT_ENABLE_CK804);
 }
 
+static void nv_mcp55_freeze(struct ata_port *ap)
+{
+       void __iomem *mmio_base = ap->host->iomap[NV_MMIO_BAR];
+       int shift = ap->port_no * NV_INT_PORT_SHIFT_MCP55;
+       u32 mask;
+
+       writel(NV_INT_ALL_MCP55 << shift, mmio_base + NV_INT_STATUS_MCP55);
+
+       mask = readl(mmio_base + NV_INT_ENABLE_MCP55);
+       mask &= ~(NV_INT_ALL_MCP55 << shift);
+       writel(mask, mmio_base + NV_INT_ENABLE_MCP55);
+       ata_bmdma_freeze(ap);
+}
+
+static void nv_mcp55_thaw(struct ata_port *ap)
+{
+       void __iomem *mmio_base = ap->host->iomap[NV_MMIO_BAR];
+       int shift = ap->port_no * NV_INT_PORT_SHIFT_MCP55;
+       u32 mask;
+
+       writel(NV_INT_ALL_MCP55 << shift, mmio_base + NV_INT_STATUS_MCP55);
+
+       mask = readl(mmio_base + NV_INT_ENABLE_MCP55);
+       mask |= (NV_INT_MASK_MCP55 << shift);
+       writel(mask, mmio_base + NV_INT_ENABLE_MCP55);
+       ata_bmdma_thaw(ap);
+}
+
 static int nv_hardreset(struct ata_link *link, unsigned int *class,
                        unsigned long deadline)
 {
@@ -1525,6 +1695,663 @@ static void nv_adma_error_handler(struct ata_port *ap)
                           nv_hardreset, ata_std_postreset);
 }
 
+static void nv_swncq_qc_to_dq(struct ata_port *ap, struct ata_queued_cmd *qc)
+{
+       struct nv_swncq_port_priv *pp = ap->private_data;
+       struct defer_queue *dq = &pp->defer_queue;
+
+       /* queue is full */
+       WARN_ON(dq->tail - dq->head == ATA_MAX_QUEUE);
+       dq->defer_bits |= (1 << qc->tag);
+       dq->tag[dq->tail++ & (ATA_MAX_QUEUE - 1)] = qc->tag;
+}
+
+static struct ata_queued_cmd *nv_swncq_qc_from_dq(struct ata_port *ap)
+{
+       struct nv_swncq_port_priv *pp = ap->private_data;
+       struct defer_queue *dq = &pp->defer_queue;
+       unsigned int tag;
+
+       if (dq->head == dq->tail)       /* null queue */
+               return NULL;
+
+       tag = dq->tag[dq->head & (ATA_MAX_QUEUE - 1)];
+       dq->tag[dq->head++ & (ATA_MAX_QUEUE - 1)] = ATA_TAG_POISON;
+       WARN_ON(!(dq->defer_bits & (1 << tag)));
+       dq->defer_bits &= ~(1 << tag);
+
+       return ata_qc_from_tag(ap, tag);
+}
+
+static void nv_swncq_fis_reinit(struct ata_port *ap)
+{
+       struct nv_swncq_port_priv *pp = ap->private_data;
+
+       pp->dhfis_bits = 0;
+       pp->dmafis_bits = 0;
+       pp->sdbfis_bits = 0;
+       pp->ncq_flags = 0;
+}
+
+static void nv_swncq_pp_reinit(struct ata_port *ap)
+{
+       struct nv_swncq_port_priv *pp = ap->private_data;
+       struct defer_queue *dq = &pp->defer_queue;
+
+       dq->head = 0;
+       dq->tail = 0;
+       dq->defer_bits = 0;
+       pp->qc_active = 0;
+       pp->last_issue_tag = ATA_TAG_POISON;
+       nv_swncq_fis_reinit(ap);
+}
+
+static void nv_swncq_irq_clear(struct ata_port *ap, u16 fis)
+{
+       struct nv_swncq_port_priv *pp = ap->private_data;
+
+       writew(fis, pp->irq_block);
+}
+
+static void __ata_bmdma_stop(struct ata_port *ap)
+{
+       struct ata_queued_cmd qc;
+
+       qc.ap = ap;
+       ata_bmdma_stop(&qc);
+}
+
+static void nv_swncq_ncq_stop(struct ata_port *ap)
+{
+       struct nv_swncq_port_priv *pp = ap->private_data;
+       unsigned int i;
+       u32 sactive;
+       u32 done_mask;
+
+       ata_port_printk(ap, KERN_ERR,
+                       "EH in SWNCQ mode,QC:qc_active 0x%X sactive 0x%X\n",
+                       ap->qc_active, ap->link.sactive);
+       ata_port_printk(ap, KERN_ERR,
+               "SWNCQ:qc_active 0x%X defer_bits 0x%X last_issue_tag 0x%x\n  "
+               "dhfis 0x%X dmafis 0x%X sdbfis 0x%X\n",
+               pp->qc_active, pp->defer_queue.defer_bits, pp->last_issue_tag,
+               pp->dhfis_bits, pp->dmafis_bits, pp->sdbfis_bits);
+
+       ata_port_printk(ap, KERN_ERR, "ATA_REG 0x%X ERR_REG 0x%X\n",
+                       ap->ops->check_status(ap),
+                       ioread8(ap->ioaddr.error_addr));
+
+       sactive = readl(pp->sactive_block);
+       done_mask = pp->qc_active ^ sactive;
+
+       ata_port_printk(ap, KERN_ERR, "tag : dhfis dmafis sdbfis sacitve\n");
+       for (i = 0; i < ATA_MAX_QUEUE; i++) {
+               u8 err = 0;
+               if (pp->qc_active & (1 << i))
+                       err = 0;
+               else if (done_mask & (1 << i))
+                       err = 1;
+               else
+                       continue;
+
+               ata_port_printk(ap, KERN_ERR,
+                               "tag 0x%x: %01x %01x %01x %01x %s\n", i,
+                               (pp->dhfis_bits >> i) & 0x1,
+                               (pp->dmafis_bits >> i) & 0x1,
+                               (pp->sdbfis_bits >> i) & 0x1,
+                               (sactive >> i) & 0x1,
+                               (err ? "error! tag doesn't exit" : " "));
+       }
+
+       nv_swncq_pp_reinit(ap);
+       ap->ops->irq_clear(ap);
+       __ata_bmdma_stop(ap);
+       nv_swncq_irq_clear(ap, 0xffff);
+}
+
+static void nv_swncq_error_handler(struct ata_port *ap)
+{
+       struct ata_eh_context *ehc = &ap->link.eh_context;
+
+       if (ap->link.sactive) {
+               nv_swncq_ncq_stop(ap);
+               ehc->i.action |= ATA_EH_HARDRESET;
+       }
+
+       ata_bmdma_drive_eh(ap, ata_std_prereset, ata_std_softreset,
+                          nv_hardreset, ata_std_postreset);
+}
+
+#ifdef CONFIG_PM
+static int nv_swncq_port_suspend(struct ata_port *ap, pm_message_t mesg)
+{
+       void __iomem *mmio = ap->host->iomap[NV_MMIO_BAR];
+       u32 tmp;
+
+       /* clear irq */
+       writel(~0, mmio + NV_INT_STATUS_MCP55);
+
+       /* disable irq */
+       writel(0, mmio + NV_INT_ENABLE_MCP55);
+
+       /* disable swncq */
+       tmp = readl(mmio + NV_CTL_MCP55);
+       tmp &= ~(NV_CTL_PRI_SWNCQ | NV_CTL_SEC_SWNCQ);
+       writel(tmp, mmio + NV_CTL_MCP55);
+
+       return 0;
+}
+
+static int nv_swncq_port_resume(struct ata_port *ap)
+{
+       void __iomem *mmio = ap->host->iomap[NV_MMIO_BAR];
+       u32 tmp;
+
+       /* clear irq */
+       writel(~0, mmio + NV_INT_STATUS_MCP55);
+
+       /* enable irq */
+       writel(0x00fd00fd, mmio + NV_INT_ENABLE_MCP55);
+
+       /* enable swncq */
+       tmp = readl(mmio + NV_CTL_MCP55);
+       writel(tmp | NV_CTL_PRI_SWNCQ | NV_CTL_SEC_SWNCQ, mmio + NV_CTL_MCP55);
+
+       return 0;
+}
+#endif
+
+static void nv_swncq_host_init(struct ata_host *host)
+{
+       u32 tmp;
+       void __iomem *mmio = host->iomap[NV_MMIO_BAR];
+       struct pci_dev *pdev = to_pci_dev(host->dev);
+       u8 regval;
+
+       /* disable  ECO 398 */
+       pci_read_config_byte(pdev, 0x7f, &regval);
+       regval &= ~(1 << 7);
+       pci_write_config_byte(pdev, 0x7f, regval);
+
+       /* enable swncq */
+       tmp = readl(mmio + NV_CTL_MCP55);
+       VPRINTK("HOST_CTL:0x%X\n", tmp);
+       writel(tmp | NV_CTL_PRI_SWNCQ | NV_CTL_SEC_SWNCQ, mmio + NV_CTL_MCP55);
+
+       /* enable irq intr */
+       tmp = readl(mmio + NV_INT_ENABLE_MCP55);
+       VPRINTK("HOST_ENABLE:0x%X\n", tmp);
+       writel(tmp | 0x00fd00fd, mmio + NV_INT_ENABLE_MCP55);
+
+       /*  clear port irq */
+       writel(~0x0, mmio + NV_INT_STATUS_MCP55);
+}
+
+static int nv_swncq_slave_config(struct scsi_device *sdev)
+{
+       struct ata_port *ap = ata_shost_to_port(sdev->host);
+       struct pci_dev *pdev = to_pci_dev(ap->host->dev);
+       struct ata_device *dev;
+       int rc;
+       u8 rev;
+       u8 check_maxtor = 0;
+       unsigned char model_num[ATA_ID_PROD_LEN + 1];
+
+       rc = ata_scsi_slave_config(sdev);
+       if (sdev->id >= ATA_MAX_DEVICES || sdev->channel || sdev->lun)
+               /* Not a proper libata device, ignore */
+               return rc;
+
+       dev = &ap->link.device[sdev->id];
+       if (!(ap->flags & ATA_FLAG_NCQ) || dev->class == ATA_DEV_ATAPI)
+               return rc;
+
+       /* if MCP51 and Maxtor, then disable ncq */
+       if (pdev->device == PCI_DEVICE_ID_NVIDIA_NFORCE_MCP51_SATA ||
+               pdev->device == PCI_DEVICE_ID_NVIDIA_NFORCE_MCP51_SATA2)
+               check_maxtor = 1;
+
+       /* if MCP55 and rev <= a2 and Maxtor, then disable ncq */
+       if (pdev->device == PCI_DEVICE_ID_NVIDIA_NFORCE_MCP55_SATA ||
+               pdev->device == PCI_DEVICE_ID_NVIDIA_NFORCE_MCP55_SATA2) {
+               pci_read_config_byte(pdev, 0x8, &rev);
+               if (rev <= 0xa2)
+                       check_maxtor = 1;
+       }
+
+       if (!check_maxtor)
+               return rc;
+
+       ata_id_c_string(dev->id, model_num, ATA_ID_PROD, sizeof(model_num));
+
+       if (strncmp(model_num, "Maxtor", 6) == 0) {
+               ata_scsi_change_queue_depth(sdev, 1);
+               ata_dev_printk(dev, KERN_NOTICE,
+                       "Disabling SWNCQ mode (depth %x)\n", sdev->queue_depth);
+       }
+
+       return rc;
+}
+
+static int nv_swncq_port_start(struct ata_port *ap)
+{
+       struct device *dev = ap->host->dev;
+       void __iomem *mmio = ap->host->iomap[NV_MMIO_BAR];
+       struct nv_swncq_port_priv *pp;
+       int rc;
+
+       rc = ata_port_start(ap);
+       if (rc)
+               return rc;
+
+       pp = devm_kzalloc(dev, sizeof(*pp), GFP_KERNEL);
+       if (!pp)
+               return -ENOMEM;
+
+       pp->prd = dmam_alloc_coherent(dev, ATA_PRD_TBL_SZ * ATA_MAX_QUEUE,
+                                     &pp->prd_dma, GFP_KERNEL);
+       if (!pp->prd)
+               return -ENOMEM;
+       memset(pp->prd, 0, ATA_PRD_TBL_SZ * ATA_MAX_QUEUE);
+
+       ap->private_data = pp;
+       pp->sactive_block = ap->ioaddr.scr_addr + 4 * SCR_ACTIVE;
+       pp->irq_block = mmio + NV_INT_STATUS_MCP55 + ap->port_no * 2;
+       pp->tag_block = mmio + NV_NCQ_REG_MCP55 + ap->port_no * 2;
+
+       return 0;
+}
+
+static void nv_swncq_qc_prep(struct ata_queued_cmd *qc)
+{
+       if (qc->tf.protocol != ATA_PROT_NCQ) {
+               ata_qc_prep(qc);
+               return;
+       }
+
+       if (!(qc->flags & ATA_QCFLAG_DMAMAP))
+               return;
+
+       nv_swncq_fill_sg(qc);
+}
+
+static void nv_swncq_fill_sg(struct ata_queued_cmd *qc)
+{
+       struct ata_port *ap = qc->ap;
+       struct scatterlist *sg;
+       unsigned int idx;
+       struct nv_swncq_port_priv *pp = ap->private_data;
+       struct ata_prd *prd;
+
+       WARN_ON(qc->__sg == NULL);
+       WARN_ON(qc->n_elem == 0 && qc->pad_len == 0);
+
+       prd = pp->prd + ATA_MAX_PRD * qc->tag;
+
+       idx = 0;
+       ata_for_each_sg(sg, qc) {
+               u32 addr, offset;
+               u32 sg_len, len;
+
+               addr = (u32)sg_dma_address(sg);
+               sg_len = sg_dma_len(sg);
+
+               while (sg_len) {
+                       offset = addr & 0xffff;
+                       len = sg_len;
+                       if ((offset + sg_len) > 0x10000)
+                               len = 0x10000 - offset;
+
+                       prd[idx].addr = cpu_to_le32(addr);
+                       prd[idx].flags_len = cpu_to_le32(len & 0xffff);
+
+                       idx++;
+                       sg_len -= len;
+                       addr += len;
+               }
+       }
+
+       if (idx)
+               prd[idx - 1].flags_len |= cpu_to_le32(ATA_PRD_EOT);
+}
+
+static unsigned int nv_swncq_issue_atacmd(struct ata_port *ap,
+                                         struct ata_queued_cmd *qc)
+{
+       struct nv_swncq_port_priv *pp = ap->private_data;
+
+       if (qc == NULL)
+               return 0;
+
+       DPRINTK("Enter\n");
+
+       writel((1 << qc->tag), pp->sactive_block);
+       pp->last_issue_tag = qc->tag;
+       pp->dhfis_bits &= ~(1 << qc->tag);
+       pp->dmafis_bits &= ~(1 << qc->tag);
+       pp->qc_active |= (0x1 << qc->tag);
+
+       ap->ops->tf_load(ap, &qc->tf);   /* load tf registers */
+       ap->ops->exec_command(ap, &qc->tf);
+
+       DPRINTK("Issued tag %u\n", qc->tag);
+
+       return 0;
+}
+
+static unsigned int nv_swncq_qc_issue(struct ata_queued_cmd *qc)
+{
+       struct ata_port *ap = qc->ap;
+       struct nv_swncq_port_priv *pp = ap->private_data;
+
+       if (qc->tf.protocol != ATA_PROT_NCQ)
+               return ata_qc_issue_prot(qc);
+
+       DPRINTK("Enter\n");
+
+       if (!pp->qc_active)
+               nv_swncq_issue_atacmd(ap, qc);
+       else
+               nv_swncq_qc_to_dq(ap, qc);      /* add qc to defer queue */
+
+       return 0;
+}
+
+static void nv_swncq_hotplug(struct ata_port *ap, u32 fis)
+{
+       u32 serror;
+       struct ata_eh_info *ehi = &ap->link.eh_info;
+
+       ata_ehi_clear_desc(ehi);
+
+       /* AHCI needs SError cleared; otherwise, it might lock up */
+       sata_scr_read(&ap->link, SCR_ERROR, &serror);
+       sata_scr_write(&ap->link, SCR_ERROR, serror);
+
+       /* analyze @irq_stat */
+       if (fis & NV_SWNCQ_IRQ_ADDED)
+               ata_ehi_push_desc(ehi, "hot plug");
+       else if (fis & NV_SWNCQ_IRQ_REMOVED)
+               ata_ehi_push_desc(ehi, "hot unplug");
+
+       ata_ehi_hotplugged(ehi);
+
+       /* okay, let's hand over to EH */
+       ehi->serror |= serror;
+
+       ata_port_freeze(ap);
+}
+
+static int nv_swncq_sdbfis(struct ata_port *ap)
+{
+       struct ata_queued_cmd *qc;
+       struct nv_swncq_port_priv *pp = ap->private_data;
+       struct ata_eh_info *ehi = &ap->link.eh_info;
+       u32 sactive;
+       int nr_done = 0;
+       u32 done_mask;
+       int i;
+       u8 host_stat;
+       u8 lack_dhfis = 0;
+
+       host_stat = ap->ops->bmdma_status(ap);
+       if (unlikely(host_stat & ATA_DMA_ERR)) {
+               /* error when transfering data to/from memory */
+               ata_ehi_clear_desc(ehi);
+               ata_ehi_push_desc(ehi, "BMDMA stat 0x%x", host_stat);
+               ehi->err_mask |= AC_ERR_HOST_BUS;
+               ehi->action |= ATA_EH_SOFTRESET;
+               return -EINVAL;
+       }
+
+       ap->ops->irq_clear(ap);
+       __ata_bmdma_stop(ap);
+
+       sactive = readl(pp->sactive_block);
+       done_mask = pp->qc_active ^ sactive;
+
+       if (unlikely(done_mask & sactive)) {
+               ata_ehi_clear_desc(ehi);
+               ata_ehi_push_desc(ehi, "illegal SWNCQ:qc_active transition"
+                                 "(%08x->%08x)", pp->qc_active, sactive);
+               ehi->err_mask |= AC_ERR_HSM;
+               ehi->action |= ATA_EH_HARDRESET;
+               return -EINVAL;
+       }
+       for (i = 0; i < ATA_MAX_QUEUE; i++) {
+               if (!(done_mask & (1 << i)))
+                       continue;
+
+               qc = ata_qc_from_tag(ap, i);
+               if (qc) {
+                       ata_qc_complete(qc);
+                       pp->qc_active &= ~(1 << i);
+                       pp->dhfis_bits &= ~(1 << i);
+                       pp->dmafis_bits &= ~(1 << i);
+                       pp->sdbfis_bits |= (1 << i);
+                       nr_done++;
+               }
+       }
+
+       if (!ap->qc_active) {
+               DPRINTK("over\n");
+               nv_swncq_pp_reinit(ap);
+               return nr_done;
+       }
+
+       if (pp->qc_active & pp->dhfis_bits)
+               return nr_done;
+
+       if ((pp->ncq_flags & ncq_saw_backout) ||
+           (pp->qc_active ^ pp->dhfis_bits))
+               /* if the controller cann't get a device to host register FIS,
+                * The driver needs to reissue the new command.
+                */
+               lack_dhfis = 1;
+
+       DPRINTK("id 0x%x QC: qc_active 0x%x,"
+               "SWNCQ:qc_active 0x%X defer_bits %X "
+               "dhfis 0x%X dmafis 0x%X last_issue_tag %x\n",
+               ap->print_id, ap->qc_active, pp->qc_active,
+               pp->defer_queue.defer_bits, pp->dhfis_bits,
+               pp->dmafis_bits, pp->last_issue_tag);
+
+       nv_swncq_fis_reinit(ap);
+
+       if (lack_dhfis) {
+               qc = ata_qc_from_tag(ap, pp->last_issue_tag);
+               nv_swncq_issue_atacmd(ap, qc);
+               return nr_done;
+       }
+
+       if (pp->defer_queue.defer_bits) {
+               /* send deferral queue command */
+               qc = nv_swncq_qc_from_dq(ap);
+               WARN_ON(qc == NULL);
+               nv_swncq_issue_atacmd(ap, qc);
+       }
+
+       return nr_done;
+}
+
+static inline u32 nv_swncq_tag(struct ata_port *ap)
+{
+       struct nv_swncq_port_priv *pp = ap->private_data;
+       u32 tag;
+
+       tag = readb(pp->tag_block) >> 2;
+       return (tag & 0x1f);
+}
+
+static int nv_swncq_dmafis(struct ata_port *ap)
+{
+       struct ata_queued_cmd *qc;
+       unsigned int rw;
+       u8 dmactl;
+       u32 tag;
+       struct nv_swncq_port_priv *pp = ap->private_data;
+
+       __ata_bmdma_stop(ap);
+       tag = nv_swncq_tag(ap);
+
+       DPRINTK("dma setup tag 0x%x\n", tag);
+       qc = ata_qc_from_tag(ap, tag);
+
+       if (unlikely(!qc))
+               return 0;
+
+       rw = qc->tf.flags & ATA_TFLAG_WRITE;
+
+       /* load PRD table addr. */
+       iowrite32(pp->prd_dma + ATA_PRD_TBL_SZ * qc->tag,
+                 ap->ioaddr.bmdma_addr + ATA_DMA_TABLE_OFS);
+
+       /* specify data direction, triple-check start bit is clear */
+       dmactl = ioread8(ap->ioaddr.bmdma_addr + ATA_DMA_CMD);
+       dmactl &= ~ATA_DMA_WR;
+       if (!rw)
+               dmactl |= ATA_DMA_WR;
+
+       iowrite8(dmactl | ATA_DMA_START, ap->ioaddr.bmdma_addr + ATA_DMA_CMD);
+
+       return 1;
+}
+
+static void nv_swncq_host_interrupt(struct ata_port *ap, u16 fis)
+{
+       struct nv_swncq_port_priv *pp = ap->private_data;
+       struct ata_queued_cmd *qc;
+       struct ata_eh_info *ehi = &ap->link.eh_info;
+       u32 serror;
+       u8 ata_stat;
+       int rc = 0;
+
+       ata_stat = ap->ops->check_status(ap);
+       nv_swncq_irq_clear(ap, fis);
+       if (!fis)
+               return;
+
+       if (ap->pflags & ATA_PFLAG_FROZEN)
+               return;
+
+       if (fis & NV_SWNCQ_IRQ_HOTPLUG) {
+               nv_swncq_hotplug(ap, fis);
+               return;
+       }
+
+       if (!pp->qc_active)
+               return;
+
+       if (ap->ops->scr_read(ap, SCR_ERROR, &serror))
+               return;
+       ap->ops->scr_write(ap, SCR_ERROR, serror);
+
+       if (ata_stat & ATA_ERR) {
+               ata_ehi_clear_desc(ehi);
+               ata_ehi_push_desc(ehi, "Ata error. fis:0x%X", fis);
+               ehi->err_mask |= AC_ERR_DEV;
+               ehi->serror |= serror;
+               ehi->action |= ATA_EH_SOFTRESET;
+               ata_port_freeze(ap);
+               return;
+       }
+
+       if (fis & NV_SWNCQ_IRQ_BACKOUT) {
+               /* If the IRQ is backout, driver must issue
+                * the new command again some time later.
+                */
+               pp->ncq_flags |= ncq_saw_backout;
+       }
+
+       if (fis & NV_SWNCQ_IRQ_SDBFIS) {
+               pp->ncq_flags |= ncq_saw_sdb;
+               DPRINTK("id 0x%x SWNCQ: qc_active 0x%X "
+                       "dhfis 0x%X dmafis 0x%X sactive 0x%X\n",
+                       ap->print_id, pp->qc_active, pp->dhfis_bits,
+                       pp->dmafis_bits, readl(pp->sactive_block));
+               rc = nv_swncq_sdbfis(ap);
+               if (rc < 0)
+                       goto irq_error;
+       }
+
+       if (fis & NV_SWNCQ_IRQ_DHREGFIS) {
+               /* The interrupt indicates the new command
+                * was transmitted correctly to the drive.
+                */
+               pp->dhfis_bits |= (0x1 << pp->last_issue_tag);
+               pp->ncq_flags |= ncq_saw_d2h;
+               if (pp->ncq_flags & (ncq_saw_sdb | ncq_saw_backout)) {
+                       ata_ehi_push_desc(ehi, "illegal fis transaction");
+                       ehi->err_mask |= AC_ERR_HSM;
+                       ehi->action |= ATA_EH_HARDRESET;
+                       goto irq_error;
+               }
+
+               if (!(fis & NV_SWNCQ_IRQ_DMASETUP) &&
+                   !(pp->ncq_flags & ncq_saw_dmas)) {
+                       ata_stat = ap->ops->check_status(ap);
+                       if (ata_stat & ATA_BUSY)
+                               goto irq_exit;
+
+                       if (pp->defer_queue.defer_bits) {
+                               DPRINTK("send next command\n");
+                               qc = nv_swncq_qc_from_dq(ap);
+                               nv_swncq_issue_atacmd(ap, qc);
+                       }
+               }
+       }
+
+       if (fis & NV_SWNCQ_IRQ_DMASETUP) {
+               /* program the dma controller with appropriate PRD buffers
+                * and start the DMA transfer for requested command.
+                */
+               pp->dmafis_bits |= (0x1 << nv_swncq_tag(ap));
+               pp->ncq_flags |= ncq_saw_dmas;
+               rc = nv_swncq_dmafis(ap);
+       }
+
+irq_exit:
+       return;
+irq_error:
+       ata_ehi_push_desc(ehi, "fis:0x%x", fis);
+       ata_port_freeze(ap);
+       return;
+}
+
+static irqreturn_t nv_swncq_interrupt(int irq, void *dev_instance)
+{
+       struct ata_host *host = dev_instance;
+       unsigned int i;
+       unsigned int handled = 0;
+       unsigned long flags;
+       u32 irq_stat;
+
+       spin_lock_irqsave(&host->lock, flags);
+
+       irq_stat = readl(host->iomap[NV_MMIO_BAR] + NV_INT_STATUS_MCP55);
+
+       for (i = 0; i < host->n_ports; i++) {
+               struct ata_port *ap = host->ports[i];
+
+               if (ap && !(ap->flags & ATA_FLAG_DISABLED)) {
+                       if (ap->link.sactive) {
+                               nv_swncq_host_interrupt(ap, (u16)irq_stat);
+                               handled = 1;
+                       } else {
+                               if (irq_stat)   /* reserve Hotplug */
+                                       nv_swncq_irq_clear(ap, 0xfff0);
+
+                               handled += nv_host_intr(ap, (u8)irq_stat);
+                       }
+               }
+               irq_stat >>= NV_INT_PORT_SHIFT_MCP55;
+       }
+
+       spin_unlock_irqrestore(&host->lock, flags);
+
+       return IRQ_RETVAL(handled);
+}
+
 static int nv_init_one (struct pci_dev *pdev, const struct pci_device_id *ent)
 {
        static int printed_version = 0;
@@ -1551,7 +2378,7 @@ static int nv_init_one (struct pci_dev *pdev, const struct pci_device_id *ent)
                return rc;
 
        /* determine type and allocate host */
-       if (type >= CK804 && adma_enabled) {
+       if (type == CK804 && adma_enabled) {
                dev_printk(KERN_NOTICE, &pdev->dev, "Using ADMA mode\n");
                type = ADMA;
        }
@@ -1597,6 +2424,9 @@ static int nv_init_one (struct pci_dev *pdev, const struct pci_device_id *ent)
                rc = nv_adma_host_init(host);
                if (rc)
                        return rc;
+       } else if (type == SWNCQ && swncq_enabled) {
+               dev_printk(KERN_NOTICE, &pdev->dev, "Using SWNCQ mode\n");
+               nv_swncq_host_init(host);
        }
 
        pci_set_master(pdev);
@@ -1696,3 +2526,6 @@ module_init(nv_init);
 module_exit(nv_exit);
 module_param_named(adma, adma_enabled, bool, 0444);
 MODULE_PARM_DESC(adma, "Enable use of ADMA (Default: true)");
+module_param_named(swncq, swncq_enabled, bool, 0444);
+MODULE_PARM_DESC(swncq, "Enable use of SWNCQ (Default: false)");
+
diff --git a/drivers/char/ec3104_keyb.c b/drivers/char/ec3104_keyb.c
deleted file mode 100644 (file)
index 0200114..0000000
+++ /dev/null
@@ -1,457 +0,0 @@
-/*
- * linux/drivers/char/ec3104_keyb.c
- * 
- * Copyright (C) 2000 Philipp Rumpf <prumpf@tux.org>
- *
- * based on linux/drivers/char/pc_keyb.c, which had the following comments:
- *
- * Separation of the PC low-level part by Geert Uytterhoeven, May 1997
- * See keyboard.c for the whole history.
- *
- * Major cleanup by Martin Mares, May 1997
- *
- * Combined the keyboard and PS/2 mouse handling into one file,
- * because they share the same hardware.
- * Johan Myreen <jem@iki.fi> 1998-10-08.
- *
- * Code fixes to handle mouse ACKs properly.
- * C. Scott Ananian <cananian@alumni.princeton.edu> 1999-01-29.
- */
-/* EC3104 note:
- * This code was written without any documentation about the EC3104 chip.  While
- * I hope I got most of the basic functionality right, the register names I use
- * are most likely completely different from those in the chip documentation.
- *
- * If you have any further information about the EC3104, please tell me
- * (prumpf@tux.org).
- */
-
-
-#include <linux/spinlock.h>
-#include <linux/sched.h>
-#include <linux/interrupt.h>
-#include <linux/tty.h>
-#include <linux/mm.h>
-#include <linux/signal.h>
-#include <linux/init.h>
-#include <linux/kbd_ll.h>
-#include <linux/delay.h>
-#include <linux/random.h>
-#include <linux/poll.h>
-#include <linux/miscdevice.h>
-#include <linux/slab.h>
-#include <linux/kbd_kern.h>
-#include <linux/bitops.h>
-
-#include <asm/keyboard.h>
-#include <asm/uaccess.h>
-#include <asm/irq.h>
-#include <asm/system.h>
-#include <asm/ec3104.h>
-
-#include <asm/io.h>
-
-/* Some configuration switches are present in the include file... */
-
-#include <linux/pc_keyb.h>
-
-#define MSR_CTS 0x10
-#define MCR_RTS 0x02
-#define LSR_DR 0x01
-#define LSR_BOTH_EMPTY 0x60
-
-static struct e5_struct {
-       u8 packet[8];
-       int pos;
-       int length;
-
-       u8 cached_mcr;
-       u8 last_msr;
-} ec3104_keyb;
-       
-/* Simple translation table for the SysRq keys */
-
-
-#ifdef CONFIG_MAGIC_SYSRQ
-unsigned char ec3104_kbd_sysrq_xlate[128] =
-       "\000\0331234567890-=\177\t"                    /* 0x00 - 0x0f */
-       "qwertyuiop[]\r\000as"                          /* 0x10 - 0x1f */
-       "dfghjkl;'`\000\\zxcv"                          /* 0x20 - 0x2f */
-       "bnm,./\000*\000 \000\201\202\203\204\205"      /* 0x30 - 0x3f */
-       "\206\207\210\211\212\000\000789-456+1"         /* 0x40 - 0x4f */
-       "230\177\000\000\213\214\000\000\000\000\000\000\000\000\000\000" /* 0x50 - 0x5f */
-       "\r\000/";                                      /* 0x60 - 0x6f */
-#endif
-
-static void kbd_write_command_w(int data);
-static void kbd_write_output_w(int data);
-#ifdef CONFIG_PSMOUSE
-static void aux_write_ack(int val);
-static void __aux_write_ack(int val);
-#endif
-
-static DEFINE_SPINLOCK(kbd_controller_lock);
-static unsigned char handle_kbd_event(void);
-
-/* used only by send_data - set by keyboard_interrupt */
-static volatile unsigned char reply_expected;
-static volatile unsigned char acknowledge;
-static volatile unsigned char resend;
-
-
-int ec3104_kbd_setkeycode(unsigned int scancode, unsigned int keycode)
-{
-       return 0;
-}
-
-int ec3104_kbd_getkeycode(unsigned int scancode)
-{
-       return 0;
-}
-
-
-/* yes, it probably would be faster to use an array.  I don't care. */
-
-static inline unsigned char ec3104_scan2key(unsigned char scancode)
-{
-       switch (scancode) {
-       case  1: /* '`' */
-               return 41;
-               
-       case  2 ... 27:
-               return scancode;
-               
-       case 28: /* '\\' */
-               return 43;
-
-       case 29 ... 39:
-               return scancode + 1;
-
-       case 40: /* '\r' */
-               return 28;
-
-       case 41 ... 50:
-               return scancode + 3;
-
-       case 51: /* ' ' */
-               return 57;
-               
-       case 52: /* escape */
-               return 1;
-
-       case 54: /* insert/delete (labelled delete) */
-               /* this should arguably be 110, but I'd like to have ctrl-alt-del
-                * working with a standard keymap */
-               return 111;
-
-       case 55: /* left */
-               return 105;
-       case 56: /* home */
-               return 102;
-       case 57: /* end */
-               return 107;
-       case 58: /* up */
-               return 103;
-       case 59: /* down */
-               return 108;
-       case 60: /* pgup */
-               return 104;
-       case 61: /* pgdown */
-               return 109;
-       case 62: /* right */
-               return 106;
-
-       case 79 ... 88: /* f1 - f10 */
-               return scancode - 20;
-
-       case 89 ... 90: /* f11 - f12 */
-               return scancode - 2;
-
-       case 91: /* left shift */
-               return 42;
-
-       case 92: /* right shift */
-               return 54;
-
-       case 93: /* left alt */
-               return 56;
-       case 94: /* right alt */
-               return 100;
-       case 95: /* left ctrl */
-               return 29;
-       case 96: /* right ctrl */
-               return 97;
-
-       case 97: /* caps lock */
-               return 58;
-       case 102: /* left windows */
-               return 125;
-       case 103: /* right windows */
-               return 126;
-
-       case 106: /* Fn */
-               /* this is wrong. */
-               return 84;
-
-       default:
-               return 0;
-       }
-}
-               
-int ec3104_kbd_translate(unsigned char scancode, unsigned char *keycode,
-                   char raw_mode)
-{
-       scancode &= 0x7f;
-
-       *keycode = ec3104_scan2key(scancode);
-
-       return 1;
-}
-
-char ec3104_kbd_unexpected_up(unsigned char keycode)
-{
-       return 0200;
-}
-
-static inline void handle_keyboard_event(unsigned char scancode)
-{
-#ifdef CONFIG_VT
-       handle_scancode(scancode, !(scancode & 0x80));
-#endif                         
-       tasklet_schedule(&keyboard_tasklet);
-}      
-
-void ec3104_kbd_leds(unsigned char leds)
-{
-}
-
-static u8 e5_checksum(u8 *packet, int count)
-{
-       int i;
-       u8 sum = 0;
-
-       for (i=0; i<count; i++)
-               sum ^= packet[i];
-               
-       if (sum & 0x80)
-               sum ^= 0xc0;
-
-       return sum;
-}
-
-static void e5_wait_for_cts(struct e5_struct *k)
-{
-       u8 msr;
-               
-       do {
-               msr = ctrl_inb(EC3104_SER4_MSR);
-       } while (!(msr & MSR_CTS));
-}
-
-
-static void e5_send_byte(u8 byte, struct e5_struct *k)
-{
-       u8 status;
-               
-       do {
-               status = ctrl_inb(EC3104_SER4_LSR);
-       } while ((status & LSR_BOTH_EMPTY) != LSR_BOTH_EMPTY);
-       
-       printk("<%02x>", byte);
-
-       ctrl_outb(byte, EC3104_SER4_DATA);
-
-       do {
-               status = ctrl_inb(EC3104_SER4_LSR);
-       } while ((status & LSR_BOTH_EMPTY) != LSR_BOTH_EMPTY);
-       
-}
-
-static int e5_send_packet(u8 *packet, int count, struct e5_struct *k)
-{
-       int i;
-
-       disable_irq(EC3104_IRQ_SER4);
-       
-       if (k->cached_mcr & MCR_RTS) {
-               printk("e5_send_packet: too slow\n");
-               enable_irq(EC3104_IRQ_SER4);
-               return -EAGAIN;
-       }
-
-       k->cached_mcr |= MCR_RTS;
-       ctrl_outb(k->cached_mcr, EC3104_SER4_MCR);
-
-       e5_wait_for_cts(k);
-
-       printk("p: ");
-
-       for(i=0; i<count; i++)
-               e5_send_byte(packet[i], k);
-
-       e5_send_byte(e5_checksum(packet, count), k);
-
-       printk("\n");
-
-       udelay(1500);
-
-       k->cached_mcr &= ~MCR_RTS;
-       ctrl_outb(k->cached_mcr, EC3104_SER4_MCR);
-
-       set_current_state(TASK_UNINTERRUPTIBLE);
-       
-       
-
-       enable_irq(EC3104_IRQ_SER4);
-
-       
-
-       return 0;
-}
-
-/*
- * E5 packets we know about:
- * E5->host 0x80 0x05 <checksum> - resend packet
- * host->E5 0x83 0x43 <contrast> - set LCD contrast
- * host->E5 0x85 0x41 0x02 <brightness> 0x02 - set LCD backlight
- * E5->host 0x87 <ps2 packet> 0x00 <checksum> - external PS2 
- * E5->host 0x88 <scancode> <checksum> - key press
- */
-
-static void e5_receive(struct e5_struct *k)
-{
-       k->packet[k->pos++] = ctrl_inb(EC3104_SER4_DATA);
-
-       if (k->pos == 1) {
-               switch(k->packet[0]) {
-               case 0x80:
-                       k->length = 3;
-                       break;
-                       
-               case 0x87: /* PS2 ext */
-                       k->length = 6;
-                       break;
-
-               case 0x88: /* keyboard */
-                       k->length = 3;
-                       break;
-
-               default:
-                       k->length = 1;
-                       printk(KERN_WARNING "unknown E5 packet %02x\n",
-                              k->packet[0]);
-               }
-       }
-
-       if (k->pos == k->length) {
-               int i;
-
-               if (e5_checksum(k->packet, k->length) != 0)
-                       printk(KERN_WARNING "E5: wrong checksum\n");
-
-#if 0
-               printk("E5 packet [");
-               for(i=0; i<k->length; i++) {
-                       printk("%02x ", k->packet[i]);
-               }
-
-               printk("(%02x)]\n", e5_checksum(k->packet, k->length-1));
-#endif
-
-               switch(k->packet[0]) {
-               case 0x80:
-               case 0x88:
-                       handle_keyboard_event(k->packet[1]);
-                       break;
-               }
-
-               k->pos = k->length = 0;
-       }
-}
-
-static void ec3104_keyb_interrupt(int irq, void *data)
-{
-       struct e5_struct *k = &ec3104_keyb;
-       u8 msr, lsr;
-
-       msr = ctrl_inb(EC3104_SER4_MSR);
-       
-       if ((msr & MSR_CTS) && !(k->last_msr & MSR_CTS)) {
-               if (k->cached_mcr & MCR_RTS)
-                       printk("confused: RTS already high\n");
-               /* CTS went high.  Send RTS. */
-               k->cached_mcr |= MCR_RTS;
-               
-               ctrl_outb(k->cached_mcr, EC3104_SER4_MCR);
-       } else if ((!(msr & MSR_CTS)) && (k->last_msr & MSR_CTS)) {
-               /* CTS went low. */
-               if (!(k->cached_mcr & MCR_RTS))
-                       printk("confused: RTS already low\n");
-
-               k->cached_mcr &= ~MCR_RTS;
-
-               ctrl_outb(k->cached_mcr, EC3104_SER4_MCR);
-       }
-
-       k->last_msr = msr;
-
-       lsr = ctrl_inb(EC3104_SER4_LSR);
-
-       if (lsr & LSR_DR)
-               e5_receive(k);
-}
-
-static void ec3104_keyb_clear_state(void)
-{
-       struct e5_struct *k = &ec3104_keyb;
-       u8 msr, lsr;
-       
-       /* we want CTS to be low */
-       k->last_msr = 0;
-
-       for (;;) {
-               msleep(100);
-
-               msr = ctrl_inb(EC3104_SER4_MSR);
-       
-               lsr = ctrl_inb(EC3104_SER4_LSR);
-               
-               if (lsr & LSR_DR) {
-                       e5_receive(k);
-                       continue;
-               }
-
-               if ((msr & MSR_CTS) && !(k->last_msr & MSR_CTS)) {
-                       if (k->cached_mcr & MCR_RTS)
-                               printk("confused: RTS already high\n");
-                       /* CTS went high.  Send RTS. */
-                       k->cached_mcr |= MCR_RTS;
-               
-                       ctrl_outb(k->cached_mcr, EC3104_SER4_MCR);
-               } else if ((!(msr & MSR_CTS)) && (k->last_msr & MSR_CTS)) {
-                       /* CTS went low. */
-                       if (!(k->cached_mcr & MCR_RTS))
-                               printk("confused: RTS already low\n");
-                       
-                       k->cached_mcr &= ~MCR_RTS;
-                       
-                       ctrl_outb(k->cached_mcr, EC3104_SER4_MCR);
-               } else
-                       break;
-
-               k->last_msr = msr;
-
-               continue;
-       }
-}
-
-void __init ec3104_kbd_init_hw(void)
-{
-       ec3104_keyb.last_msr = ctrl_inb(EC3104_SER4_MSR);
-       ec3104_keyb.cached_mcr = ctrl_inb(EC3104_SER4_MCR);
-
-       ec3104_keyb_clear_state();
-
-       /* Ok, finally allocate the IRQ, and off we go.. */
-       request_irq(EC3104_IRQ_SER4, ec3104_keyb_interrupt, 0, "keyboard", NULL);
-}
index e47f881..700a165 100644 (file)
@@ -158,6 +158,7 @@ config SENSORS_K8TEMP
 config SENSORS_AMS
        tristate "Apple Motion Sensor driver"
        depends on PPC_PMAC && !PPC64 && INPUT && ((ADB_PMU && I2C = y) || (ADB_PMU && !I2C) || I2C) && EXPERIMENTAL
+       select INPUT_POLLDEV
        help
          Support for the motion sensor included in PowerBooks. Includes
          implementations for PMU and I2C.
@@ -701,6 +702,7 @@ config SENSORS_W83627EHF
 config SENSORS_HDAPS
        tristate "IBM Hard Drive Active Protection System (hdaps)"
        depends on INPUT && X86
+       select INPUT_POLLDEV
        default n
        help
          This driver provides support for the IBM Hard Drive Active Protection
@@ -722,6 +724,7 @@ config SENSORS_APPLESMC
        depends on INPUT && X86
        select NEW_LEDS
        select LEDS_CLASS
+       select INPUT_POLLDEV
        default n
        help
          This driver provides support for the Apple System Management
index ca7095d..7b81e0c 100644 (file)
@@ -27,47 +27,32 @@ static unsigned int invert;
 module_param(invert, bool, 0644);
 MODULE_PARM_DESC(invert, "Invert input data on X and Y axis");
 
-static int ams_input_kthread(void *data)
+static void ams_idev_poll(struct input_polled_dev *dev)
 {
+       struct input_dev *idev = dev->input;
        s8 x, y, z;
 
-       while (!kthread_should_stop()) {
-               mutex_lock(&ams_info.lock);
-
-               ams_sensors(&x, &y, &z);
-
-               x -= ams_info.xcalib;
-               y -= ams_info.ycalib;
-               z -= ams_info.zcalib;
-
-               input_report_abs(ams_info.idev, ABS_X, invert ? -x : x);
-               input_report_abs(ams_info.idev, ABS_Y, invert ? -y : y);
-               input_report_abs(ams_info.idev, ABS_Z, z);
+       mutex_lock(&ams_info.lock);
 
-               input_sync(ams_info.idev);
+       ams_sensors(&x, &y, &z);
 
-               mutex_unlock(&ams_info.lock);
+       x -= ams_info.xcalib;
+       y -= ams_info.ycalib;
+       z -= ams_info.zcalib;
 
-               msleep(25);
-       }
+       input_report_abs(idev, ABS_X, invert ? -x : x);
+       input_report_abs(idev, ABS_Y, invert ? -y : y);
+       input_report_abs(idev, ABS_Z, z);
 
-       return 0;
-}
+       input_sync(idev);
 
-static int ams_input_open(struct input_dev *dev)
-{
-       ams_info.kthread = kthread_run(ams_input_kthread, NULL, "kams");
-       return IS_ERR(ams_info.kthread) ? PTR_ERR(ams_info.kthread) : 0;
-}
-
-static void ams_input_close(struct input_dev *dev)
-{
-       kthread_stop(ams_info.kthread);
+       mutex_unlock(&ams_info.lock);
 }
 
 /* Call with ams_info.lock held! */
 static void ams_input_enable(void)
 {
+       struct input_dev *input;
        s8 x, y, z;
 
        if (ams_info.idev)
@@ -78,27 +63,29 @@ static void ams_input_enable(void)
        ams_info.ycalib = y;
        ams_info.zcalib = z;
 
-       ams_info.idev = input_allocate_device();
+       ams_info.idev = input_allocate_polled_device();
        if (!ams_info.idev)
                return;
 
-       ams_info.idev->name = "Apple Motion Sensor";
-       ams_info.idev->id.bustype = ams_info.bustype;
-       ams_info.idev->id.vendor = 0;
-       ams_info.idev->open = ams_input_open;
-       ams_info.idev->close = ams_input_close;
-       ams_info.idev->dev.parent = &ams_info.of_dev->dev;
+       ams_info.idev->poll = ams_idev_poll;
+       ams_info.idev->poll_interval = 25;
+
+       input = ams_info.idev->input;
+       input->name = "Apple Motion Sensor";
+       input->id.bustype = ams_info.bustype;
+       input->id.vendor = 0;
+       input->dev.parent = &ams_info.of_dev->dev;
 
-       input_set_abs_params(ams_info.idev, ABS_X, -50, 50, 3, 0);
-       input_set_abs_params(ams_info.idev, ABS_Y, -50, 50, 3, 0);
-       input_set_abs_params(ams_info.idev, ABS_Z, -50, 50, 3, 0);
+       input_set_abs_params(input, ABS_X, -50, 50, 3, 0);
+       input_set_abs_params(input, ABS_Y, -50, 50, 3, 0);
+       input_set_abs_params(input, ABS_Z, -50, 50, 3, 0);
 
-       set_bit(EV_ABS, ams_info.idev->evbit);
-       set_bit(EV_KEY, ams_info.idev->evbit);
-       set_bit(BTN_TOUCH, ams_info.idev->keybit);
+       set_bit(EV_ABS, input->evbit);
+       set_bit(EV_KEY, input->evbit);
+       set_bit(BTN_TOUCH, input->keybit);
 
-       if (input_register_device(ams_info.idev)) {
-               input_free_device(ams_info.idev);
+       if (input_register_polled_device(ams_info.idev)) {
+               input_free_polled_device(ams_info.idev);
                ams_info.idev = NULL;
                return;
        }
@@ -108,7 +95,8 @@ static void ams_input_enable(void)
 static void ams_input_disable(void)
 {
        if (ams_info.idev) {
-               input_unregister_device(ams_info.idev);
+               input_unregister_polled_device(ams_info.idev);
+               input_free_polled_device(ams_info.idev);
                ams_info.idev = NULL;
        }
 }
index 240730e..a6221e5 100644 (file)
@@ -1,5 +1,5 @@
 #include <linux/i2c.h>
-#include <linux/input.h>
+#include <linux/input-polldev.h>
 #include <linux/kthread.h>
 #include <linux/mutex.h>
 #include <linux/spinlock.h>
@@ -52,8 +52,7 @@ struct ams {
 #endif
 
        /* Joystick emulation */
-       struct task_struct *kthread;
-       struct input_dev *idev;
+       struct input_polled_dev *idev;
        __u16 bustype;
 
        /* calibrated null values */
index f37fd7e..4879125 100644 (file)
@@ -28,7 +28,7 @@
 
 #include <linux/delay.h>
 #include <linux/platform_device.h>
-#include <linux/input.h>
+#include <linux/input-polldev.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/timer.h>
@@ -59,9 +59,9 @@
 
 #define LIGHT_SENSOR_LEFT_KEY  "ALV0" /* r-o {alv (6 bytes) */
 #define LIGHT_SENSOR_RIGHT_KEY "ALV1" /* r-o {alv (6 bytes) */
-#define BACKLIGHT_KEY          "LKSB" /* w-o {lkb (2 bytes) */
+#define BACKLIGHT_KEY          "LKSB" /* w-o {lkb (2 bytes) */
 
-#define CLAMSHELL_KEY          "MSLD" /* r-o ui8 (unused) */
+#define CLAMSHELL_KEY          "MSLD" /* r-o ui8 (unused) */
 
 #define MOTION_SENSOR_X_KEY    "MO_X" /* r-o sp78 (2 bytes) */
 #define MOTION_SENSOR_Y_KEY    "MO_Y" /* r-o sp78 (2 bytes) */
@@ -103,7 +103,7 @@ static const char* fan_speed_keys[] = {
 #define INIT_TIMEOUT_MSECS     5000    /* wait up to 5s for device init ... */
 #define INIT_WAIT_MSECS                50      /* ... in 50ms increments */
 
-#define APPLESMC_POLL_PERIOD   (HZ/20) /* poll for input every 1/20s */
+#define APPLESMC_POLL_INTERVAL 50      /* msecs */
 #define APPLESMC_INPUT_FUZZ    4       /* input event threshold */
 #define APPLESMC_INPUT_FLAT    4
 
@@ -125,9 +125,8 @@ static const int debug;
 static struct platform_device *pdev;
 static s16 rest_x;
 static s16 rest_y;
-static struct timer_list applesmc_timer;
-static struct input_dev *applesmc_idev;
 static struct device *hwmon_dev;
+static struct input_polled_dev *applesmc_idev;
 
 /* Indicates whether this computer has an accelerometer. */
 static unsigned int applesmc_accelerometer;
@@ -138,7 +137,7 @@ static unsigned int applesmc_light;
 /* Indicates which temperature sensors set to use. */
 static unsigned int applesmc_temperature_set;
 
-static struct mutex applesmc_lock;
+static DEFINE_MUTEX(applesmc_lock);
 
 /*
  * Last index written to key_at_index sysfs file, and value to use for all other
@@ -455,27 +454,12 @@ static void applesmc_calibrate(void)
        rest_x = -rest_x;
 }
 
-static int applesmc_idev_open(struct input_dev *dev)
-{
-       add_timer(&applesmc_timer);
-
-       return 0;
-}
-
-static void applesmc_idev_close(struct input_dev *dev)
-{
-       del_timer_sync(&applesmc_timer);
-}
-
-static void applesmc_idev_poll(unsigned long unused)
+static void applesmc_idev_poll(struct input_polled_dev *dev)
 {
+       struct input_dev *idev = dev->input;
        s16 x, y;
 
-       /* Cannot sleep.  Try nonblockingly.  If we fail, try again later. */
-       if (!mutex_trylock(&applesmc_lock)) {
-               mod_timer(&applesmc_timer, jiffies + APPLESMC_POLL_PERIOD);
-               return;
-       }
+       mutex_lock(&applesmc_lock);
 
        if (applesmc_read_motion_sensor(SENSOR_X, &x))
                goto out;
@@ -483,13 +467,11 @@ static void applesmc_idev_poll(unsigned long unused)
                goto out;
 
        x = -x;
-       input_report_abs(applesmc_idev, ABS_X, x - rest_x);
-       input_report_abs(applesmc_idev, ABS_Y, y - rest_y);
-       input_sync(applesmc_idev);
+       input_report_abs(idev, ABS_X, x - rest_x);
+       input_report_abs(idev, ABS_Y, y - rest_y);
+       input_sync(idev);
 
 out:
-       mod_timer(&applesmc_timer, jiffies + APPLESMC_POLL_PERIOD);
-
        mutex_unlock(&applesmc_lock);
 }
 
@@ -821,8 +803,7 @@ static ssize_t applesmc_key_at_index_read_show(struct device *dev,
 
        if (!ret) {
                return info[0];
-       }
-       else {
+       } else {
                return ret;
        }
 }
@@ -1093,6 +1074,7 @@ static int applesmc_dmi_match(const struct dmi_system_id *id)
 /* Create accelerometer ressources */
 static int applesmc_create_accelerometer(void)
 {
+       struct input_dev *idev;
        int ret;
 
        ret = sysfs_create_group(&pdev->dev.kobj,
@@ -1100,40 +1082,37 @@ static int applesmc_create_accelerometer(void)
        if (ret)
                goto out;
 
-       applesmc_idev = input_allocate_device();
+       applesmc_idev = input_allocate_polled_device();
        if (!applesmc_idev) {
                ret = -ENOMEM;
                goto out_sysfs;
        }
 
+       applesmc_idev->poll = applesmc_idev_poll;
+       applesmc_idev->poll_interval = APPLESMC_POLL_INTERVAL;
+
        /* initial calibrate for the input device */
        applesmc_calibrate();
 
-       /* initialize the input class */
-       applesmc_idev->name = "applesmc";
-       applesmc_idev->id.bustype = BUS_HOST;
-       applesmc_idev->dev.parent = &pdev->dev;
-       applesmc_idev->evbit[0] = BIT(EV_ABS);
-       applesmc_idev->open = applesmc_idev_open;
-       applesmc_idev->close = applesmc_idev_close;
-       input_set_abs_params(applesmc_idev, ABS_X,
+       /* initialize the input device */
+       idev = applesmc_idev->input;
+       idev->name = "applesmc";
+       idev->id.bustype = BUS_HOST;
+       idev->dev.parent = &pdev->dev;
+       idev->evbit[0] = BIT(EV_ABS);
+       input_set_abs_params(idev, ABS_X,
                        -256, 256, APPLESMC_INPUT_FUZZ, APPLESMC_INPUT_FLAT);
-       input_set_abs_params(applesmc_idev, ABS_Y,
+       input_set_abs_params(idev, ABS_Y,
                        -256, 256, APPLESMC_INPUT_FUZZ, APPLESMC_INPUT_FLAT);
 
-       ret = input_register_device(applesmc_idev);
+       ret = input_register_polled_device(applesmc_idev);
        if (ret)
                goto out_idev;
 
-       /* start up our timer for the input device */
-       init_timer(&applesmc_timer);
-       applesmc_timer.function = applesmc_idev_poll;
-       applesmc_timer.expires = jiffies + APPLESMC_POLL_PERIOD;
-
        return 0;
 
 out_idev:
-       input_free_device(applesmc_idev);
+       input_free_polled_device(applesmc_idev);
 
 out_sysfs:
        sysfs_remove_group(&pdev->dev.kobj, &accelerometer_attributes_group);
@@ -1146,8 +1125,8 @@ out:
 /* Release all ressources used by the accelerometer */
 static void applesmc_release_accelerometer(void)
 {
-       del_timer_sync(&applesmc_timer);
-       input_unregister_device(applesmc_idev);
+       input_unregister_polled_device(applesmc_idev);
+       input_free_polled_device(applesmc_idev);
        sysfs_remove_group(&pdev->dev.kobj, &accelerometer_attributes_group);
 }
 
@@ -1184,8 +1163,6 @@ static int __init applesmc_init(void)
        int count;
        int i;
 
-       mutex_init(&applesmc_lock);
-
        if (!dmi_check_system(applesmc_whitelist)) {
                printk(KERN_WARNING "applesmc: supported laptop not found!\n");
                ret = -ENODEV;
index a7c6d40..8a7ae03 100644 (file)
@@ -28,7 +28,7 @@
 
 #include <linux/delay.h>
 #include <linux/platform_device.h>
-#include <linux/input.h>
+#include <linux/input-polldev.h>
 #include <linux/kernel.h>
 #include <linux/mutex.h>
 #include <linux/module.h>
 #define INIT_TIMEOUT_MSECS     4000    /* wait up to 4s for device init ... */
 #define INIT_WAIT_MSECS                200     /* ... in 200ms increments */
 
-#define HDAPS_POLL_PERIOD      (HZ/20) /* poll for input every 1/20s */
+#define HDAPS_POLL_INTERVAL    50      /* poll for input every 1/20s (50 ms)*/
 #define HDAPS_INPUT_FUZZ       4       /* input event threshold */
 #define HDAPS_INPUT_FLAT       4
 
-static struct timer_list hdaps_timer;
 static struct platform_device *pdev;
-static struct input_dev *hdaps_idev;
+static struct input_polled_dev *hdaps_idev;
 static unsigned int hdaps_invert;
 static u8 km_activity;
 static int rest_x;
@@ -323,24 +322,19 @@ static void hdaps_calibrate(void)
        __hdaps_read_pair(HDAPS_PORT_XPOS, HDAPS_PORT_YPOS, &rest_x, &rest_y);
 }
 
-static void hdaps_mousedev_poll(unsigned long unused)
+static void hdaps_mousedev_poll(struct input_polled_dev *dev)
 {
+       struct input_dev *input_dev = dev->input;
        int x, y;
 
-       /* Cannot sleep.  Try nonblockingly.  If we fail, try again later. */
-       if (mutex_trylock(&hdaps_mtx)) {
-               mod_timer(&hdaps_timer,jiffies + HDAPS_POLL_PERIOD);
-               return;
-       }
+       mutex_lock(&hdaps_mtx);
 
        if (__hdaps_read_pair(HDAPS_PORT_XPOS, HDAPS_PORT_YPOS, &x, &y))
                goto out;
 
-       input_report_abs(hdaps_idev, ABS_X, x - rest_x);
-       input_report_abs(hdaps_idev, ABS_Y, y - rest_y);
-       input_sync(hdaps_idev);
-
-       mod_timer(&hdaps_timer, jiffies + HDAPS_POLL_PERIOD);
+       input_report_abs(input_dev, ABS_X, x - rest_x);
+       input_report_abs(input_dev, ABS_Y, y - rest_y);
+       input_sync(input_dev);
 
 out:
        mutex_unlock(&hdaps_mtx);
@@ -536,6 +530,7 @@ static struct dmi_system_id __initdata hdaps_whitelist[] = {
 
 static int __init hdaps_init(void)
 {
+       struct input_dev *idev;
        int ret;
 
        if (!dmi_check_system(hdaps_whitelist)) {
@@ -563,39 +558,37 @@ static int __init hdaps_init(void)
        if (ret)
                goto out_device;
 
-       hdaps_idev = input_allocate_device();
+       hdaps_idev = input_allocate_polled_device();
        if (!hdaps_idev) {
                ret = -ENOMEM;
                goto out_group;
        }
 
+       hdaps_idev->poll = hdaps_mousedev_poll;
+       hdaps_idev->poll_interval = HDAPS_POLL_INTERVAL;
+
        /* initial calibrate for the input device */
        hdaps_calibrate();
 
        /* initialize the input class */
-       hdaps_idev->name = "hdaps";
-       hdaps_idev->dev.parent = &pdev->dev;
-       hdaps_idev->evbit[0] = BIT(EV_ABS);
-       input_set_abs_params(hdaps_idev, ABS_X,
+       idev = hdaps_idev->input;
+       idev->name = "hdaps";
+       idev->dev.parent = &pdev->dev;
+       idev->evbit[0] = BIT(EV_ABS);
+       input_set_abs_params(idev, ABS_X,
                        -256, 256, HDAPS_INPUT_FUZZ, HDAPS_INPUT_FLAT);
-       input_set_abs_params(hdaps_idev, ABS_Y,
+       input_set_abs_params(idev, ABS_Y,
                        -256, 256, HDAPS_INPUT_FUZZ, HDAPS_INPUT_FLAT);
 
-       ret = input_register_device(hdaps_idev);
+       ret = input_register_polled_device(hdaps_idev);
        if (ret)
                goto out_idev;
 
-       /* start up our timer for the input device */
-       init_timer(&hdaps_timer);
-       hdaps_timer.function = hdaps_mousedev_poll;
-       hdaps_timer.expires = jiffies + HDAPS_POLL_PERIOD;
-       add_timer(&hdaps_timer);
-
        printk(KERN_INFO "hdaps: driver successfully loaded.\n");
        return 0;
 
 out_idev:
-       input_free_device(hdaps_idev);
+       input_free_polled_device(hdaps_idev);
 out_group:
        sysfs_remove_group(&pdev->dev.kobj, &hdaps_attribute_group);
 out_device:
@@ -611,8 +604,8 @@ out:
 
 static void __exit hdaps_exit(void)
 {
-       del_timer_sync(&hdaps_timer);
-       input_unregister_device(hdaps_idev);
+       input_unregister_polled_device(hdaps_idev);
+       input_free_polled_device(hdaps_idev);
        sysfs_remove_group(&pdev->dev.kobj, &hdaps_attribute_group);
        platform_device_unregister(pdev);
        platform_driver_unregister(&hdaps_driver);
index 6545fa7..1b3327a 100644 (file)
@@ -349,6 +349,7 @@ struct ipoib_neigh {
        struct sk_buff_head queue;
 
        struct neighbour   *neighbour;
+       struct net_device *dev;
 
        struct list_head    list;
 };
@@ -365,7 +366,8 @@ static inline struct ipoib_neigh **to_ipoib_neigh(struct neighbour *neigh)
                                     INFINIBAND_ALEN, sizeof(void *));
 }
 
-struct ipoib_neigh *ipoib_neigh_alloc(struct neighbour *neigh);
+struct ipoib_neigh *ipoib_neigh_alloc(struct neighbour *neigh,
+                                     struct net_device *dev);
 void ipoib_neigh_free(struct net_device *dev, struct ipoib_neigh *neigh);
 
 extern struct workqueue_struct *ipoib_workqueue;
index e072f3c..362610d 100644 (file)
@@ -517,7 +517,7 @@ static void neigh_add_path(struct sk_buff *skb, struct net_device *dev)
        struct ipoib_path *path;
        struct ipoib_neigh *neigh;
 
-       neigh = ipoib_neigh_alloc(skb->dst->neighbour);
+       neigh = ipoib_neigh_alloc(skb->dst->neighbour, skb->dev);
        if (!neigh) {
                ++dev->stats.tx_dropped;
                dev_kfree_skb_any(skb);
@@ -692,9 +692,10 @@ static int ipoib_start_xmit(struct sk_buff *skb, struct net_device *dev)
                                goto out;
                        }
                } else if (neigh->ah) {
-                       if (unlikely(memcmp(&neigh->dgid.raw,
+                       if (unlikely((memcmp(&neigh->dgid.raw,
                                            skb->dst->neighbour->ha + 4,
-                                           sizeof(union ib_gid)))) {
+                                           sizeof(union ib_gid))) ||
+                                        (neigh->dev != dev))) {
                                spin_lock(&priv->lock);
                                /*
                                 * It's safe to call ipoib_put_ah() inside
@@ -817,6 +818,13 @@ static void ipoib_neigh_cleanup(struct neighbour *n)
        unsigned long flags;
        struct ipoib_ah *ah = NULL;
 
+       neigh = *to_ipoib_neigh(n);
+       if (neigh) {
+               priv = netdev_priv(neigh->dev);
+               ipoib_dbg(priv, "neigh_destructor for bonding device: %s\n",
+                         n->dev->name);
+       } else
+               return;
        ipoib_dbg(priv,
                  "neigh_cleanup for %06x " IPOIB_GID_FMT "\n",
                  IPOIB_QPN(n->ha),
@@ -824,13 +832,10 @@ static void ipoib_neigh_cleanup(struct neighbour *n)
 
        spin_lock_irqsave(&priv->lock, flags);
 
-       neigh = *to_ipoib_neigh(n);
-       if (neigh) {
-               if (neigh->ah)
-                       ah = neigh->ah;
-               list_del(&neigh->list);
-               ipoib_neigh_free(n->dev, neigh);
-       }
+       if (neigh->ah)
+               ah = neigh->ah;
+       list_del(&neigh->list);
+       ipoib_neigh_free(n->dev, neigh);
 
        spin_unlock_irqrestore(&priv->lock, flags);
 
@@ -838,7 +843,8 @@ static void ipoib_neigh_cleanup(struct neighbour *n)
                ipoib_put_ah(ah);
 }
 
-struct ipoib_neigh *ipoib_neigh_alloc(struct neighbour *neighbour)
+struct ipoib_neigh *ipoib_neigh_alloc(struct neighbour *neighbour,
+                                     struct net_device *dev)
 {
        struct ipoib_neigh *neigh;
 
@@ -847,6 +853,7 @@ struct ipoib_neigh *ipoib_neigh_alloc(struct neighbour *neighbour)
                return NULL;
 
        neigh->neighbour = neighbour;
+       neigh->dev = dev;
        *to_ipoib_neigh(neighbour) = neigh;
        skb_queue_head_init(&neigh->queue);
        ipoib_cm_set(neigh, NULL);
index 827820e..9bcfc7a 100644 (file)
@@ -705,7 +705,8 @@ out:
                if (skb->dst            &&
                    skb->dst->neighbour &&
                    !*to_ipoib_neigh(skb->dst->neighbour)) {
-                       struct ipoib_neigh *neigh = ipoib_neigh_alloc(skb->dst->neighbour);
+                       struct ipoib_neigh *neigh = ipoib_neigh_alloc(skb->dst->neighbour,
+                                                                       skb->dev);
 
                        if (neigh) {
                                kref_get(&mcast->ah->ref);
index 2d87357..63512d9 100644 (file)
@@ -114,28 +114,6 @@ config INPUT_JOYDEV
          To compile this driver as a module, choose M here: the
          module will be called joydev.
 
-config INPUT_TSDEV
-       tristate "Touchscreen interface"
-       ---help---
-         Say Y here if you have an application that only can understand the
-         Compaq touchscreen protocol for absolute pointer data. This is
-         useful namely for embedded configurations.
-
-         If unsure, say N.
-
-         To compile this driver as a module, choose M here: the
-         module will be called tsdev.
-
-config INPUT_TSDEV_SCREEN_X
-       int "Horizontal screen resolution"
-       depends on INPUT_TSDEV
-       default "240"
-
-config INPUT_TSDEV_SCREEN_Y
-       int "Vertical screen resolution"
-       depends on INPUT_TSDEV
-       default "320"
-
 config INPUT_EVDEV
        tristate "Event interface"
        help
index 15eb752..99af903 100644 (file)
@@ -13,7 +13,6 @@ obj-$(CONFIG_INPUT_POLLDEV)   += input-polldev.o
 obj-$(CONFIG_INPUT_MOUSEDEV)   += mousedev.o
 obj-$(CONFIG_INPUT_JOYDEV)     += joydev.o
 obj-$(CONFIG_INPUT_EVDEV)      += evdev.o
-obj-$(CONFIG_INPUT_TSDEV)      += tsdev.o
 obj-$(CONFIG_INPUT_EVBUG)      += evbug.o
 
 obj-$(CONFIG_INPUT_KEYBOARD)   += keyboard/
index f1c3d6c..1d62c8b 100644 (file)
@@ -30,6 +30,8 @@ struct evdev {
        wait_queue_head_t wait;
        struct evdev_client *grab;
        struct list_head client_list;
+       spinlock_t client_lock; /* protects client_list */
+       struct mutex mutex;
        struct device dev;
 };
 
@@ -37,39 +39,54 @@ struct evdev_client {
        struct input_event buffer[EVDEV_BUFFER_SIZE];
        int head;
        int tail;
+       spinlock_t buffer_lock; /* protects access to buffer, head and tail */
        struct fasync_struct *fasync;
        struct evdev *evdev;
        struct list_head node;
 };
 
 static struct evdev *evdev_table[EVDEV_MINORS];
+static DEFINE_MUTEX(evdev_table_mutex);
 
-static void evdev_event(struct input_handle *handle, unsigned int type, unsigned int code, int value)
+static void evdev_pass_event(struct evdev_client *client,
+                            struct input_event *event)
+{
+       /*
+        * Interrupts are disabled, just acquire the lock
+        */
+       spin_lock(&client->buffer_lock);
+       client->buffer[client->head++] = *event;
+       client->head &= EVDEV_BUFFER_SIZE - 1;
+       spin_unlock(&client->buffer_lock);
+
+       kill_fasync(&client->fasync, SIGIO, POLL_IN);
+}
+
+/*
+ * Pass incoming event to all connected clients.
+ */
+static void evdev_event(struct input_handle *handle,
+                       unsigned int type, unsigned int code, int value)
 {
        struct evdev *evdev = handle->private;
        struct evdev_client *client;
+       struct input_event event;
 
-       if (evdev->grab) {
-               client = evdev->grab;
+       do_gettimeofday(&event.time);
+       event.type = type;
+       event.code = code;
+       event.value = value;
 
-               do_gettimeofday(&client->buffer[client->head].time);
-               client->buffer[client->head].type = type;
-               client->buffer[client->head].code = code;
-               client->buffer[client->head].value = value;
-               client->head = (client->head + 1) & (EVDEV_BUFFER_SIZE - 1);
+       rcu_read_lock();
 
-               kill_fasync(&client->fasync, SIGIO, POLL_IN);
-       } else
-               list_for_each_entry(client, &evdev->client_list, node) {
+       client = rcu_dereference(evdev->grab);
+       if (client)
+               evdev_pass_event(client, &event);
+       else
+               list_for_each_entry_rcu(client, &evdev->client_list, node)
+                       evdev_pass_event(client, &event);
 
-                       do_gettimeofday(&client->buffer[client->head].time);
-                       client->buffer[client->head].type = type;
-                       client->buffer[client->head].code = code;
-                       client->buffer[client->head].value = value;
-                       client->head = (client->head + 1) & (EVDEV_BUFFER_SIZE - 1);
-
-                       kill_fasync(&client->fasync, SIGIO, POLL_IN);
-               }
+       rcu_read_unlock();
 
        wake_up_interruptible(&evdev->wait);
 }
@@ -88,38 +105,140 @@ static int evdev_flush(struct file *file, fl_owner_t id)
 {
        struct evdev_client *client = file->private_data;
        struct evdev *evdev = client->evdev;
+       int retval;
+
+       retval = mutex_lock_interruptible(&evdev->mutex);
+       if (retval)
+               return retval;
 
        if (!evdev->exist)
-               return -ENODEV;
+               retval = -ENODEV;
+       else
+               retval = input_flush_device(&evdev->handle, file);
 
-       return input_flush_device(&evdev->handle, file);
+       mutex_unlock(&evdev->mutex);
+       return retval;
 }
 
 static void evdev_free(struct device *dev)
 {
        struct evdev *evdev = container_of(dev, struct evdev, dev);
 
-       evdev_table[evdev->minor] = NULL;
        kfree(evdev);
 }
 
+/*
+ * Grabs an event device (along with underlying input device).
+ * This function is called with evdev->mutex taken.
+ */
+static int evdev_grab(struct evdev *evdev, struct evdev_client *client)
+{
+       int error;
+
+       if (evdev->grab)
+               return -EBUSY;
+
+       error = input_grab_device(&evdev->handle);
+       if (error)
+               return error;
+
+       rcu_assign_pointer(evdev->grab, client);
+       synchronize_rcu();
+
+       return 0;
+}
+
+static int evdev_ungrab(struct evdev *evdev, struct evdev_client *client)
+{
+       if (evdev->grab != client)
+               return  -EINVAL;
+
+       rcu_assign_pointer(evdev->grab, NULL);
+       synchronize_rcu();
+       input_release_device(&evdev->handle);
+
+       return 0;
+}
+
+static void evdev_attach_client(struct evdev *evdev,
+                               struct evdev_client *client)
+{
+       spin_lock(&evdev->client_lock);
+       list_add_tail_rcu(&client->node, &evdev->client_list);
+       spin_unlock(&evdev->client_lock);
+       synchronize_rcu();
+}
+
+static void evdev_detach_client(struct evdev *evdev,
+                               struct evdev_client *client)
+{
+       spin_lock(&evdev->client_lock);
+       list_del_rcu(&client->node);
+       spin_unlock(&evdev->client_lock);
+       synchronize_rcu();
+}
+
+static int evdev_open_device(struct evdev *evdev)
+{
+       int retval;
+
+       retval = mutex_lock_interruptible(&evdev->mutex);
+       if (retval)
+               return retval;
+
+       if (!evdev->exist)
+               retval = -ENODEV;
+       else if (!evdev->open++) {
+               retval = input_open_device(&evdev->handle);
+               if (retval)
+                       evdev->open--;
+       }
+
+       mutex_unlock(&evdev->mutex);
+       return retval;
+}
+
+static void evdev_close_device(struct evdev *evdev)
+{
+       mutex_lock(&evdev->mutex);
+
+       if (evdev->exist && !--evdev->open)
+               input_close_device(&evdev->handle);
+
+       mutex_unlock(&evdev->mutex);
+}
+
+/*
+ * Wake up users waiting for IO so they can disconnect from
+ * dead device.
+ */
+static void evdev_hangup(struct evdev *evdev)
+{
+       struct evdev_client *client;
+
+       spin_lock(&evdev->client_lock);
+       list_for_each_entry(client, &evdev->client_list, node)
+               kill_fasync(&client->fasync, SIGIO, POLL_HUP);
+       spin_unlock(&evdev->client_lock);
+
+       wake_up_interruptible(&evdev->wait);
+}
+
 static int evdev_release(struct inode *inode, struct file *file)
 {
        struct evdev_client *client = file->private_data;
        struct evdev *evdev = client->evdev;
 
-       if (evdev->grab == client) {
-               input_release_device(&evdev->handle);
-               evdev->grab = NULL;
-       }
+       mutex_lock(&evdev->mutex);
+       if (evdev->grab == client)
+               evdev_ungrab(evdev, client);
+       mutex_unlock(&evdev->mutex);
 
        evdev_fasync(-1, file, 0);
-       list_del(&client->node);
+       evdev_detach_client(evdev, client);
        kfree(client);
 
-       if (!--evdev->open && evdev->exist)
-               input_close_device(&evdev->handle);
-
+       evdev_close_device(evdev);
        put_device(&evdev->dev);
 
        return 0;
@@ -127,41 +246,44 @@ static int evdev_release(struct inode *inode, struct file *file)
 
 static int evdev_open(struct inode *inode, struct file *file)
 {
-       struct evdev_client *client;
        struct evdev *evdev;
+       struct evdev_client *client;
        int i = iminor(inode) - EVDEV_MINOR_BASE;
        int error;
 
        if (i >= EVDEV_MINORS)
                return -ENODEV;
 
+       error = mutex_lock_interruptible(&evdev_table_mutex);
+       if (error)
+               return error;
        evdev = evdev_table[i];
+       if (evdev)
+               get_device(&evdev->dev);
+       mutex_unlock(&evdev_table_mutex);
 
-       if (!evdev || !evdev->exist)
+       if (!evdev)
                return -ENODEV;
 
-       get_device(&evdev->dev);
-
        client = kzalloc(sizeof(struct evdev_client), GFP_KERNEL);
        if (!client) {
                error = -ENOMEM;
                goto err_put_evdev;
        }
 
+       spin_lock_init(&client->buffer_lock);
        client->evdev = evdev;
-       list_add_tail(&client->node, &evdev->client_list);
+       evdev_attach_client(evdev, client);
 
-       if (!evdev->open++ && evdev->exist) {
-               error = input_open_device(&evdev->handle);
-               if (error)
-                       goto err_free_client;
-       }
+       error = evdev_open_device(evdev);
+       if (error)
+               goto err_free_client;
 
        file->private_data = client;
        return 0;
 
  err_free_client:
-       list_del(&client->node);
+       evdev_detach_client(evdev, client);
        kfree(client);
  err_put_evdev:
        put_device(&evdev->dev);
@@ -197,12 +319,14 @@ static inline size_t evdev_event_size(void)
                sizeof(struct input_event_compat) : sizeof(struct input_event);
 }
 
-static int evdev_event_from_user(const char __user *buffer, struct input_event *event)
+static int evdev_event_from_user(const char __user *buffer,
+                                struct input_event *event)
 {
        if (COMPAT_TEST) {
                struct input_event_compat compat_event;
 
-               if (copy_from_user(&compat_event, buffer, sizeof(struct input_event_compat)))
+               if (copy_from_user(&compat_event, buffer,
+                                  sizeof(struct input_event_compat)))
                        return -EFAULT;
 
                event->time.tv_sec = compat_event.time.tv_sec;
@@ -219,7 +343,8 @@ static int evdev_event_from_user(const char __user *buffer, struct input_event *
        return 0;
 }
 
-static int evdev_event_to_user(char __user *buffer, const struct input_event *event)
+static int evdev_event_to_user(char __user *buffer,
+                               const struct input_event *event)
 {
        if (COMPAT_TEST) {
                struct input_event_compat compat_event;
@@ -230,7 +355,8 @@ static int evdev_event_to_user(char __user *buffer, const struct input_event *ev
                compat_event.code = event->code;
                compat_event.value = event->value;
 
-               if (copy_to_user(buffer, &compat_event, sizeof(struct input_event_compat)))
+               if (copy_to_user(buffer, &compat_event,
+                                sizeof(struct input_event_compat)))
                        return -EFAULT;
 
        } else {
@@ -248,7 +374,8 @@ static inline size_t evdev_event_size(void)
        return sizeof(struct input_event);
 }
 
-static int evdev_event_from_user(const char __user *buffer, struct input_event *event)
+static int evdev_event_from_user(const char __user *buffer,
+                                struct input_event *event)
 {
        if (copy_from_user(event, buffer, sizeof(struct input_event)))
                return -EFAULT;
@@ -256,7 +383,8 @@ static int evdev_event_from_user(const char __user *buffer, struct input_event *
        return 0;
 }
 
-static int evdev_event_to_user(char __user *buffer, const struct input_event *event)
+static int evdev_event_to_user(char __user *buffer,
+                               const struct input_event *event)
 {
        if (copy_to_user(buffer, event, sizeof(struct input_event)))
                return -EFAULT;
@@ -266,37 +394,71 @@ static int evdev_event_to_user(char __user *buffer, const struct input_event *ev
 
 #endif /* CONFIG_COMPAT */
 
-static ssize_t evdev_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos)
+static ssize_t evdev_write(struct file *file, const char __user *buffer,
+                          size_t count, loff_t *ppos)
 {
        struct evdev_client *client = file->private_data;
        struct evdev *evdev = client->evdev;
        struct input_event event;
-       int retval = 0;
+       int retval;
 
-       if (!evdev->exist)
-               return -ENODEV;
+       retval = mutex_lock_interruptible(&evdev->mutex);
+       if (retval)
+               return retval;
+
+       if (!evdev->exist) {
+               retval = -ENODEV;
+               goto out;
+       }
 
        while (retval < count) {
 
-               if (evdev_event_from_user(buffer + retval, &event))
-                       return -EFAULT;
-               input_inject_event(&evdev->handle, event.type, event.code, event.value);
+               if (evdev_event_from_user(buffer + retval, &event)) {
+                       retval = -EFAULT;
+                       goto out;
+               }
+
+               input_inject_event(&evdev->handle,
+                                  event.type, event.code, event.value);
                retval += evdev_event_size();
        }
 
+ out:
+       mutex_unlock(&evdev->mutex);
        return retval;
 }
 
-static ssize_t evdev_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos)
+static int evdev_fetch_next_event(struct evdev_client *client,
+                                 struct input_event *event)
+{
+       int have_event;
+
+       spin_lock_irq(&client->buffer_lock);
+
+       have_event = client->head != client->tail;
+       if (have_event) {
+               *event = client->buffer[client->tail++];
+               client->tail &= EVDEV_BUFFER_SIZE - 1;
+       }
+
+       spin_unlock_irq(&client->buffer_lock);
+
+       return have_event;
+}
+
+static ssize_t evdev_read(struct file *file, char __user *buffer,
+                         size_t count, loff_t *ppos)
 {
        struct evdev_client *client = file->private_data;
        struct evdev *evdev = client->evdev;
+       struct input_event event;
        int retval;
 
        if (count < evdev_event_size())
                return -EINVAL;
 
-       if (client->head == client->tail && evdev->exist && (file->f_flags & O_NONBLOCK))
+       if (client->head == client->tail && evdev->exist &&
+           (file->f_flags & O_NONBLOCK))
                return -EAGAIN;
 
        retval = wait_event_interruptible(evdev->wait,
@@ -307,14 +469,12 @@ static ssize_t evdev_read(struct file *file, char __user *buffer, size_t count,
        if (!evdev->exist)
                return -ENODEV;
 
-       while (client->head != client->tail && retval + evdev_event_size() <= count) {
-
-               struct input_event *event = (struct input_event *) client->buffer + client->tail;
+       while (retval + evdev_event_size() <= count &&
+              evdev_fetch_next_event(client, &event)) {
 
-               if (evdev_event_to_user(buffer + retval, event))
+               if (evdev_event_to_user(buffer + retval, &event))
                        return -EFAULT;
 
-               client->tail = (client->tail + 1) & (EVDEV_BUFFER_SIZE - 1);
                retval += evdev_event_size();
        }
 
@@ -409,8 +569,8 @@ static int str_to_user(const char *str, unsigned int maxlen, void __user *p)
        return copy_to_user(p, str, len) ? -EFAULT : len;
 }
 
-static long evdev_ioctl_handler(struct file *file, unsigned int cmd,
-                               void __user *p, int compat_mode)
+static long evdev_do_ioctl(struct file *file, unsigned int cmd,
+                          void __user *p, int compat_mode)
 {
        struct evdev_client *client = file->private_data;
        struct evdev *evdev = client->evdev;
@@ -421,215 +581,289 @@ static long evdev_ioctl_handler(struct file *file, unsigned int cmd,
        int i, t, u, v;
        int error;
 
-       if (!evdev->exist)
-               return -ENODEV;
-
        switch (cmd) {
 
-               case EVIOCGVERSION:
-                       return put_user(EV_VERSION, ip);
+       case EVIOCGVERSION:
+               return put_user(EV_VERSION, ip);
 
-               case EVIOCGID:
-                       if (copy_to_user(p, &dev->id, sizeof(struct input_id)))
-                               return -EFAULT;
-                       return 0;
+       case EVIOCGID:
+               if (copy_to_user(p, &dev->id, sizeof(struct input_id)))
+                       return -EFAULT;
+               return 0;
 
-               case EVIOCGREP:
-                       if (!test_bit(EV_REP, dev->evbit))
-                               return -ENOSYS;
-                       if (put_user(dev->rep[REP_DELAY], ip))
-                               return -EFAULT;
-                       if (put_user(dev->rep[REP_PERIOD], ip + 1))
-                               return -EFAULT;
-                       return 0;
+       case EVIOCGREP:
+               if (!test_bit(EV_REP, dev->evbit))
+                       return -ENOSYS;
+               if (put_user(dev->rep[REP_DELAY], ip))
+                       return -EFAULT;
+               if (put_user(dev->rep[REP_PERIOD], ip + 1))
+                       return -EFAULT;
+               return 0;
 
-               case EVIOCSREP:
-                       if (!test_bit(EV_REP, dev->evbit))
-                               return -ENOSYS;
-                       if (get_user(u, ip))
-                               return -EFAULT;
-                       if (get_user(v, ip + 1))
-                               return -EFAULT;
+       case EVIOCSREP:
+               if (!test_bit(EV_REP, dev->evbit))
+                       return -ENOSYS;
+               if (get_user(u, ip))
+                       return -EFAULT;
+               if (get_user(v, ip + 1))
+                       return -EFAULT;
 
-                       input_inject_event(&evdev->handle, EV_REP, REP_DELAY, u);
-                       input_inject_event(&evdev->handle, EV_REP, REP_PERIOD, v);
+               input_inject_event(&evdev->handle, EV_REP, REP_DELAY, u);
+               input_inject_event(&evdev->handle, EV_REP, REP_PERIOD, v);
 
-                       return 0;
+               return 0;
 
-               case EVIOCGKEYCODE:
-                       if (get_user(t, ip))
-                               return -EFAULT;
+       case EVIOCGKEYCODE:
+               if (get_user(t, ip))
+                       return -EFAULT;
 
-                       error = dev->getkeycode(dev, t, &v);
-                       if (error)
-                               return error;
+               error = dev->getkeycode(dev, t, &v);
+               if (error)
+                       return error;
 
-                       if (put_user(v, ip + 1))
-                               return -EFAULT;
+               if (put_user(v, ip + 1))
+                       return -EFAULT;
 
-                       return 0;
+               return 0;
 
-               case EVIOCSKEYCODE:
-                       if (get_user(t, ip) || get_user(v, ip + 1))
-                               return -EFAULT;
+       case EVIOCSKEYCODE:
+               if (get_user(t, ip) || get_user(v, ip + 1))
+                       return -EFAULT;
 
-                       return dev->setkeycode(dev, t, v);
+               return dev->setkeycode(dev, t, v);
 
-               case EVIOCSFF:
-                       if (copy_from_user(&effect, p, sizeof(effect)))
-                               return -EFAULT;
+       case EVIOCSFF:
+               if (copy_from_user(&effect, p, sizeof(effect)))
+                       return -EFAULT;
 
-                       error = input_ff_upload(dev, &effect, file);
+               error = input_ff_upload(dev, &effect, file);
 
-                       if (put_user(effect.id, &(((struct ff_effect __user *)p)->id)))
-                               return -EFAULT;
+               if (put_user(effect.id, &(((struct ff_effect __user *)p)->id)))
+                       return -EFAULT;
 
-                       return error;
+               return error;
 
-               case EVIOCRMFF:
-                       return input_ff_erase(dev, (int)(unsigned long) p, file);
+       case EVIOCRMFF:
+               return input_ff_erase(dev, (int)(unsigned long) p, file);
 
-               case EVIOCGEFFECTS:
-                       i = test_bit(EV_FF, dev->evbit) ? dev->ff->max_effects : 0;
-                       if (put_user(i, ip))
-                               return -EFAULT;
-                       return 0;
-
-               case EVIOCGRAB:
-                       if (p) {
-                               if (evdev->grab)
-                                       return -EBUSY;
-                               if (input_grab_device(&evdev->handle))
-                                       return -EBUSY;
-                               evdev->grab = client;
-                               return 0;
-                       } else {
-                               if (evdev->grab != client)
-                                       return -EINVAL;
-                               input_release_device(&evdev->handle);
-                               evdev->grab = NULL;
-                               return 0;
-                       }
+       case EVIOCGEFFECTS:
+               i = test_bit(EV_FF, dev->evbit) ?
+                               dev->ff->max_effects : 0;
+               if (put_user(i, ip))
+                       return -EFAULT;
+               return 0;
+
+       case EVIOCGRAB:
+               if (p)
+                       return evdev_grab(evdev, client);
+               else
+                       return evdev_ungrab(evdev, client);
 
-               default:
+       default:
 
-                       if (_IOC_TYPE(cmd) != 'E')
-                               return -EINVAL;
+               if (_IOC_TYPE(cmd) != 'E')
+                       return -EINVAL;
 
-                       if (_IOC_DIR(cmd) == _IOC_READ) {
+               if (_IOC_DIR(cmd) == _IOC_READ) {
 
-                               if ((_IOC_NR(cmd) & ~EV_MAX) == _IOC_NR(EVIOCGBIT(0,0))) {
+                       if ((_IOC_NR(cmd) & ~EV_MAX) == _IOC_NR(EVIOCGBIT(0, 0))) {
 
-                                       unsigned long *bits;
-                                       int len;
+                               unsigned long *bits;
+                               int len;
 
-                                       switch (_IOC_NR(cmd) & EV_MAX) {
-                                               case      0: bits = dev->evbit;  len = EV_MAX;  break;
-                                               case EV_KEY: bits = dev->keybit; len = KEY_MAX; break;
-                                               case EV_REL: bits = dev->relbit; len = REL_MAX; break;
-                                               case EV_ABS: bits = dev->absbit; len = ABS_MAX; break;
-                                               case EV_MSC: bits = dev->mscbit; len = MSC_MAX; break;
-                                               case EV_LED: bits = dev->ledbit; len = LED_MAX; break;
-                                               case EV_SND: bits = dev->sndbit; len = SND_MAX; break;
-                                               case EV_FF:  bits = dev->ffbit;  len = FF_MAX;  break;
-                                               case EV_SW:  bits = dev->swbit;  len = SW_MAX;  break;
-                                               default: return -EINVAL;
-                                       }
-                                       return bits_to_user(bits, len, _IOC_SIZE(cmd), p, compat_mode);
-                               }
+                               switch (_IOC_NR(cmd) & EV_MAX) {
 
-                               if (_IOC_NR(cmd) == _IOC_NR(EVIOCGKEY(0)))
-                                       return bits_to_user(dev->key, KEY_MAX, _IOC_SIZE(cmd),
-                                                           p, compat_mode);
+                               case      0: bits = dev->evbit;  len = EV_MAX;  break;
+                               case EV_KEY: bits = dev->keybit; len = KEY_MAX; break;
+                               case EV_REL: bits = dev->relbit; len = REL_MAX; break;
+                               case EV_ABS: bits = dev->absbit; len = ABS_MAX; break;
+                               case EV_MSC: bits = dev->mscbit; len = MSC_MAX; break;
+                               case EV_LED: bits = dev->ledbit; len = LED_MAX; break;
+                               case EV_SND: bits = dev->sndbit; len = SND_MAX; break;
+                               case EV_FF:  bits = dev->ffbit;  len = FF_MAX;  break;
+                               case EV_SW:  bits = dev->swbit;  len = SW_MAX;  break;
+                               default: return -EINVAL;
+                       }
+                               return bits_to_user(bits, len, _IOC_SIZE(cmd), p, compat_mode);
+                       }
 
-                               if (_IOC_NR(cmd) == _IOC_NR(EVIOCGLED(0)))
-                                       return bits_to_user(dev->led, LED_MAX, _IOC_SIZE(cmd),
-                                                           p, compat_mode);
+                       if (_IOC_NR(cmd) == _IOC_NR(EVIOCGKEY(0)))
+                               return bits_to_user(dev->key, KEY_MAX, _IOC_SIZE(cmd),
+                                                   p, compat_mode);
 
-                               if (_IOC_NR(cmd) == _IOC_NR(EVIOCGSND(0)))
-                                       return bits_to_user(dev->snd, SND_MAX, _IOC_SIZE(cmd),
-                                                           p, compat_mode);
+                       if (_IOC_NR(cmd) == _IOC_NR(EVIOCGLED(0)))
+                               return bits_to_user(dev->led, LED_MAX, _IOC_SIZE(cmd),
+                                                   p, compat_mode);
 
-                               if (_IOC_NR(cmd) == _IOC_NR(EVIOCGSW(0)))
-                                       return bits_to_user(dev->sw, SW_MAX, _IOC_SIZE(cmd),
-                                                           p, compat_mode);
+                       if (_IOC_NR(cmd) == _IOC_NR(EVIOCGSND(0)))
+                               return bits_to_user(dev->snd, SND_MAX, _IOC_SIZE(cmd),
+                                                   p, compat_mode);
 
-                               if (_IOC_NR(cmd) == _IOC_NR(EVIOCGNAME(0)))
-                                       return str_to_user(dev->name, _IOC_SIZE(cmd), p);
+                       if (_IOC_NR(cmd) == _IOC_NR(EVIOCGSW(0)))
+                               return bits_to_user(dev->sw, SW_MAX, _IOC_SIZE(cmd),
+                                                   p, compat_mode);
 
-                               if (_IOC_NR(cmd) == _IOC_NR(EVIOCGPHYS(0)))
-                                       return str_to_user(dev->phys, _IOC_SIZE(cmd), p);
+                       if (_IOC_NR(cmd) == _IOC_NR(EVIOCGNAME(0)))
+                               return str_to_user(dev->name, _IOC_SIZE(cmd), p);
 
-                               if (_IOC_NR(cmd) == _IOC_NR(EVIOCGUNIQ(0)))
-                                       return str_to_user(dev->uniq, _IOC_SIZE(cmd), p);
+                       if (_IOC_NR(cmd) == _IOC_NR(EVIOCGPHYS(0)))
+                               return str_to_user(dev->phys, _IOC_SIZE(cmd), p);
 
-                               if ((_IOC_NR(cmd) & ~ABS_MAX) == _IOC_NR(EVIOCGABS(0))) {
+                       if (_IOC_NR(cmd) == _IOC_NR(EVIOCGUNIQ(0)))
+                               return str_to_user(dev->uniq, _IOC_SIZE(cmd), p);
 
-                                       t = _IOC_NR(cmd) & ABS_MAX;
+                       if ((_IOC_NR(cmd) & ~ABS_MAX) == _IOC_NR(EVIOCGABS(0))) {
 
-                                       abs.value = dev->abs[t];
-                                       abs.minimum = dev->absmin[t];
-                                       abs.maximum = dev->absmax[t];
-                                       abs.fuzz = dev->absfuzz[t];
-                                       abs.flat = dev->absflat[t];
+                               t = _IOC_NR(cmd) & ABS_MAX;
 
-                                       if (copy_to_user(p, &abs, sizeof(struct input_absinfo)))
-                                               return -EFAULT;
+                               abs.value = dev->abs[t];
+                               abs.minimum = dev->absmin[t];
+                               abs.maximum = dev->absmax[t];
+                               abs.fuzz = dev->absfuzz[t];
+                               abs.flat = dev->absflat[t];
 
-                                       return 0;
-                               }
+                               if (copy_to_user(p, &abs, sizeof(struct input_absinfo)))
+                                       return -EFAULT;
 
+                               return 0;
                        }
 
-                       if (_IOC_DIR(cmd) == _IOC_WRITE) {
+               }
+
+               if (_IOC_DIR(cmd) == _IOC_WRITE) {
 
-                               if ((_IOC_NR(cmd) & ~ABS_MAX) == _IOC_NR(EVIOCSABS(0))) {
+                       if ((_IOC_NR(cmd) & ~ABS_MAX) == _IOC_NR(EVIOCSABS(0))) {
 
-                                       t = _IOC_NR(cmd) & ABS_MAX;
+                               t = _IOC_NR(cmd) & ABS_MAX;
 
-                                       if (copy_from_user(&abs, p, sizeof(struct input_absinfo)))
-                                               return -EFAULT;
+                               if (copy_from_user(&abs, p,
+                                               sizeof(struct input_absinfo)))
+                                       return -EFAULT;
 
-                                       dev->abs[t] = abs.value;
-                                       dev->absmin[t] = abs.minimum;
-                                       dev->absmax[t] = abs.maximum;
-                                       dev->absfuzz[t] = abs.fuzz;
-                                       dev->absflat[t] = abs.flat;
+                               /*
+                                * Take event lock to ensure that we are not
+                                * changing device parameters in the middle
+                                * of event.
+                                */
+                               spin_lock_irq(&dev->event_lock);
 
-                                       return 0;
-                               }
+                               dev->abs[t] = abs.value;
+                               dev->absmin[t] = abs.minimum;
+                               dev->absmax[t] = abs.maximum;
+                               dev->absfuzz[t] = abs.fuzz;
+                               dev->absflat[t] = abs.flat;
+
+                               spin_unlock_irq(&dev->event_lock);
+
+                               return 0;
                        }
+               }
        }
        return -EINVAL;
 }
 
+static long evdev_ioctl_handler(struct file *file, unsigned int cmd,
+                               void __user *p, int compat_mode)
+{
+       struct evdev_client *client = file->private_data;
+       struct evdev *evdev = client->evdev;
+       int retval;
+
+       retval = mutex_lock_interruptible(&evdev->mutex);
+       if (retval)
+               return retval;
+
+       if (!evdev->exist) {
+               retval = -ENODEV;
+               goto out;
+       }
+
+       retval = evdev_do_ioctl(file, cmd, p, compat_mode);
+
+ out:
+       mutex_unlock(&evdev->mutex);
+       return retval;
+}
+
 static long evdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
 {
        return evdev_ioctl_handler(file, cmd, (void __user *)arg, 0);
 }
 
 #ifdef CONFIG_COMPAT
-static long evdev_ioctl_compat(struct file *file, unsigned int cmd, unsigned long arg)
+static long evdev_ioctl_compat(struct file *file,
+                               unsigned int cmd, unsigned long arg)
 {
        return evdev_ioctl_handler(file, cmd, compat_ptr(arg), 1);
 }
 #endif
 
 static const struct file_operations evdev_fops = {
-       .owner =        THIS_MODULE,
-       .read =         evdev_read,
-       .write =        evdev_write,
-       .poll =         evdev_poll,
-       .open =         evdev_open,
-       .release =      evdev_release,
-       .unlocked_ioctl = evdev_ioctl,
+       .owner          = THIS_MODULE,
+       .read           = evdev_read,
+       .write          = evdev_write,
+       .poll           = evdev_poll,
+       .open           = evdev_open,
+       .release        = evdev_release,
+       .unlocked_ioctl = evdev_ioctl,
 #ifdef CONFIG_COMPAT
-       .compat_ioctl = evdev_ioctl_compat,
+       .compat_ioctl   = evdev_ioctl_compat,
 #endif
-       .fasync =       evdev_fasync,
-       .flush =        evdev_flush
+       .fasync         = evdev_fasync,
+       .flush          = evdev_flush
 };
 
+static int evdev_install_chrdev(struct evdev *evdev)
+{
+       /*
+        * No need to do any locking here as calls to connect and
+        * disconnect are serialized by the input core
+        */
+       evdev_table[evdev->minor] = evdev;
+       return 0;
+}
+
+static void evdev_remove_chrdev(struct evdev *evdev)
+{
+       /*
+        * Lock evdev table to prevent race with evdev_open()
+        */
+       mutex_lock(&evdev_table_mutex);
+       evdev_table[evdev->minor] = NULL;
+       mutex_unlock(&evdev_table_mutex);
+}
+
+/*
+ * Mark device non-existent. This disables writes, ioctls and
+ * prevents new users from opening the device. Already posted
+ * blocking reads will stay, however new ones will fail.
+ */
+static void evdev_mark_dead(struct evdev *evdev)
+{
+       mutex_lock(&evdev->mutex);
+       evdev->exist = 0;
+       mutex_unlock(&evdev->mutex);
+}
+
+static void evdev_cleanup(struct evdev *evdev)
+{
+       struct input_handle *handle = &evdev->handle;
+
+       evdev_mark_dead(evdev);
+       evdev_hangup(evdev);
+       evdev_remove_chrdev(evdev);
+
+       /* evdev is marked dead so no one else accesses evdev->open */
+       if (evdev->open) {
+               input_flush_device(handle, NULL);
+               input_close_device(handle);
+       }
+}
+
+/*
+ * Create new evdev device. Note that input core serializes calls
+ * to connect and disconnect so we don't need to lock evdev_table here.
+ */
 static int evdev_connect(struct input_handler *handler, struct input_dev *dev,
                         const struct input_device_id *id)
 {
@@ -637,7 +871,10 @@ static int evdev_connect(struct input_handler *handler, struct input_dev *dev,
        int minor;
        int error;
 
-       for (minor = 0; minor < EVDEV_MINORS && evdev_table[minor]; minor++);
+       for (minor = 0; minor < EVDEV_MINORS; minor++)
+               if (!evdev_table[minor])
+                       break;
+
        if (minor == EVDEV_MINORS) {
                printk(KERN_ERR "evdev: no more free evdev devices\n");
                return -ENFILE;
@@ -648,38 +885,44 @@ static int evdev_connect(struct input_handler *handler, struct input_dev *dev,
                return -ENOMEM;
 
        INIT_LIST_HEAD(&evdev->client_list);
+       spin_lock_init(&evdev->client_lock);
+       mutex_init(&evdev->mutex);
        init_waitqueue_head(&evdev->wait);
 
+       snprintf(evdev->name, sizeof(evdev->name), "event%d", minor);
        evdev->exist = 1;
        evdev->minor = minor;
+
        evdev->handle.dev = dev;
        evdev->handle.name = evdev->name;
        evdev->handle.handler = handler;
        evdev->handle.private = evdev;
-       snprintf(evdev->name, sizeof(evdev->name), "event%d", minor);
 
-       snprintf(evdev->dev.bus_id, sizeof(evdev->dev.bus_id),
-                "event%d", minor);
+       strlcpy(evdev->dev.bus_id, evdev->name, sizeof(evdev->dev.bus_id));
+       evdev->dev.devt = MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE + minor);
        evdev->dev.class = &input_class;
        evdev->dev.parent = &dev->dev;
-       evdev->dev.devt = MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE + minor);
        evdev->dev.release = evdev_free;
        device_initialize(&evdev->dev);
 
-       evdev_table[minor] = evdev;
-
-       error = device_add(&evdev->dev);
+       error = input_register_handle(&evdev->handle);
        if (error)
                goto err_free_evdev;
 
-       error = input_register_handle(&evdev->handle);
+       error = evdev_install_chrdev(evdev);
+       if (error)
+               goto err_unregister_handle;
+
+       error = device_add(&evdev->dev);
        if (error)
-               goto err_delete_evdev;
+               goto err_cleanup_evdev;
 
        return 0;
 
- err_delete_evdev:
-       device_del(&evdev->dev);
+ err_cleanup_evdev:
+       evdev_cleanup(evdev);
+ err_unregister_handle:
+       input_unregister_handle(&evdev->handle);
  err_free_evdev:
        put_device(&evdev->dev);
        return error;
@@ -688,21 +931,10 @@ static int evdev_connect(struct input_handler *handler, struct input_dev *dev,
 static void evdev_disconnect(struct input_handle *handle)
 {
        struct evdev *evdev = handle->private;
-       struct evdev_client *client;
 
-       input_unregister_handle(handle);
        device_del(&evdev->dev);
-
-       evdev->exist = 0;
-
-       if (evdev->open) {
-               input_flush_device(handle, NULL);
-               input_close_device(handle);
-               list_for_each_entry(client, &evdev->client_list, node)
-                       kill_fasync(&client->fasync, SIGIO, POLL_HUP);
-               wake_up_interruptible(&evdev->wait);
-       }
-
+       evdev_cleanup(evdev);
+       input_unregister_handle(handle);
        put_device(&evdev->dev);
 }
 
@@ -714,13 +946,13 @@ static const struct input_device_id evdev_ids[] = {
 MODULE_DEVICE_TABLE(input, evdev_ids);
 
 static struct input_handler evdev_handler = {
-       .event =        evdev_event,
-       .connect =      evdev_connect,
-       .disconnect =   evdev_disconnect,
-       .fops =         &evdev_fops,
-       .minor =        EVDEV_MINOR_BASE,
-       .name =         "evdev",
-       .id_table =     evdev_ids,
+       .event          = evdev_event,
+       .connect        = evdev_connect,
+       .disconnect     = evdev_disconnect,
+       .fops           = &evdev_fops,
+       .minor          = EVDEV_MINOR_BASE,
+       .name           = "evdev",
+       .id_table       = evdev_ids,
 };
 
 static int __init evdev_init(void)
index b773d4c..92b3598 100644 (file)
@@ -70,6 +70,7 @@ static int input_open_polled_device(struct input_dev *input)
 {
        struct input_polled_dev *dev = input->private;
        int error;
+       unsigned long ticks;
 
        error = input_polldev_start_workqueue();
        if (error)
@@ -78,8 +79,10 @@ static int input_open_polled_device(struct input_dev *input)
        if (dev->flush)
                dev->flush(dev);
 
-       queue_delayed_work(polldev_wq, &dev->work,
-                          msecs_to_jiffies(dev->poll_interval));
+       ticks = msecs_to_jiffies(dev->poll_interval);
+       if (ticks >= HZ)
+               ticks = round_jiffies(ticks);
+       queue_delayed_work(polldev_wq, &dev->work, ticks);
 
        return 0;
 }
index 5dc361c..2f2b020 100644 (file)
 #include <linux/major.h>
 #include <linux/proc_fs.h>
 #include <linux/seq_file.h>
-#include <linux/interrupt.h>
 #include <linux/poll.h>
 #include <linux/device.h>
 #include <linux/mutex.h>
+#include <linux/rcupdate.h>
 
 MODULE_AUTHOR("Vojtech Pavlik <vojtech@suse.cz>");
 MODULE_DESCRIPTION("Input core");
@@ -31,167 +31,245 @@ MODULE_LICENSE("GPL");
 static LIST_HEAD(input_dev_list);
 static LIST_HEAD(input_handler_list);
 
+/*
+ * input_mutex protects access to both input_dev_list and input_handler_list.
+ * This also causes input_[un]register_device and input_[un]register_handler
+ * be mutually exclusive which simplifies locking in drivers implementing
+ * input handlers.
+ */
+static DEFINE_MUTEX(input_mutex);
+
 static struct input_handler *input_table[8];
 
-/**
- * input_event() - report new input event
- * @dev: device that generated the event
- * @type: type of the event
- * @code: event code
- * @value: value of the event
- *
- * This function should be used by drivers implementing various input devices
- * See also input_inject_event()
- */
-void input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
+static inline int is_event_supported(unsigned int code,
+                                    unsigned long *bm, unsigned int max)
 {
-       struct input_handle *handle;
+       return code <= max && test_bit(code, bm);
+}
 
-       if (type > EV_MAX || !test_bit(type, dev->evbit))
-               return;
+static int input_defuzz_abs_event(int value, int old_val, int fuzz)
+{
+       if (fuzz) {
+               if (value > old_val - fuzz / 2 && value < old_val + fuzz / 2)
+                       return old_val;
 
-       add_input_randomness(type, code, value);
+               if (value > old_val - fuzz && value < old_val + fuzz)
+                       return (old_val * 3 + value) / 4;
 
-       switch (type) {
-
-               case EV_SYN:
-                       switch (code) {
-                               case SYN_CONFIG:
-                                       if (dev->event)
-                                               dev->event(dev, type, code, value);
-                                       break;
-
-                               case SYN_REPORT:
-                                       if (dev->sync)
-                                               return;
-                                       dev->sync = 1;
-                                       break;
-                       }
-                       break;
+               if (value > old_val - fuzz * 2 && value < old_val + fuzz * 2)
+                       return (old_val + value) / 2;
+       }
 
-               case EV_KEY:
+       return value;
+}
 
-                       if (code > KEY_MAX || !test_bit(code, dev->keybit) || !!test_bit(code, dev->key) == value)
-                               return;
+/*
+ * Pass event through all open handles. This function is called with
+ * dev->event_lock held and interrupts disabled.
+ */
+static void input_pass_event(struct input_dev *dev,
+                            unsigned int type, unsigned int code, int value)
+{
+       struct input_handle *handle;
 
-                       if (value == 2)
-                               break;
+       rcu_read_lock();
 
-                       change_bit(code, dev->key);
+       handle = rcu_dereference(dev->grab);
+       if (handle)
+               handle->handler->event(handle, type, code, value);
+       else
+               list_for_each_entry_rcu(handle, &dev->h_list, d_node)
+                       if (handle->open)
+                               handle->handler->event(handle,
+                                                       type, code, value);
+       rcu_read_unlock();
+}
 
-                       if (test_bit(EV_REP, dev->evbit) && dev->rep[REP_PERIOD] && dev->rep[REP_DELAY] && dev->timer.data && value) {
-                               dev->repeat_key = code;
-                               mod_timer(&dev->timer, jiffies + msecs_to_jiffies(dev->rep[REP_DELAY]));
-                       }
+/*
+ * Generate software autorepeat event. Note that we take
+ * dev->event_lock here to avoid racing with input_event
+ * which may cause keys get "stuck".
+ */
+static void input_repeat_key(unsigned long data)
+{
+       struct input_dev *dev = (void *) data;
+       unsigned long flags;
 
-                       break;
+       spin_lock_irqsave(&dev->event_lock, flags);
 
-               case EV_SW:
+       if (test_bit(dev->repeat_key, dev->key) &&
+           is_event_supported(dev->repeat_key, dev->keybit, KEY_MAX)) {
 
-                       if (code > SW_MAX || !test_bit(code, dev->swbit) || !!test_bit(code, dev->sw) == value)
-                               return;
+               input_pass_event(dev, EV_KEY, dev->repeat_key, 2);
 
-                       change_bit(code, dev->sw);
+               if (dev->sync) {
+                       /*
+                        * Only send SYN_REPORT if we are not in a middle
+                        * of driver parsing a new hardware packet.
+                        * Otherwise assume that the driver will send
+                        * SYN_REPORT once it's done.
+                        */
+                       input_pass_event(dev, EV_SYN, SYN_REPORT, 1);
+               }
 
-                       break;
+               if (dev->rep[REP_PERIOD])
+                       mod_timer(&dev->timer, jiffies +
+                                       msecs_to_jiffies(dev->rep[REP_PERIOD]));
+       }
 
-               case EV_ABS:
+       spin_unlock_irqrestore(&dev->event_lock, flags);
+}
 
-                       if (code > ABS_MAX || !test_bit(code, dev->absbit))
-                               return;
+static void input_start_autorepeat(struct input_dev *dev, int code)
+{
+       if (test_bit(EV_REP, dev->evbit) &&
+           dev->rep[REP_PERIOD] && dev->rep[REP_DELAY] &&
+           dev->timer.data) {
+               dev->repeat_key = code;
+               mod_timer(&dev->timer,
+                         jiffies + msecs_to_jiffies(dev->rep[REP_DELAY]));
+       }
+}
 
-                       if (dev->absfuzz[code]) {
-                               if ((value > dev->abs[code] - (dev->absfuzz[code] >> 1)) &&
-                                   (value < dev->abs[code] + (dev->absfuzz[code] >> 1)))
-                                       return;
+#define INPUT_IGNORE_EVENT     0
+#define INPUT_PASS_TO_HANDLERS 1
+#define INPUT_PASS_TO_DEVICE   2
+#define INPUT_PASS_TO_ALL      (INPUT_PASS_TO_HANDLERS | INPUT_PASS_TO_DEVICE)
 
-                               if ((value > dev->abs[code] - dev->absfuzz[code]) &&
-                                   (value < dev->abs[code] + dev->absfuzz[code]))
-                                       value = (dev->abs[code] * 3 + value) >> 2;
+static void input_handle_event(struct input_dev *dev,
+                              unsigned int type, unsigned int code, int value)
+{
+       int disposition = INPUT_IGNORE_EVENT;
 
-                               if ((value > dev->abs[code] - (dev->absfuzz[code] << 1)) &&
-                                   (value < dev->abs[code] + (dev->absfuzz[code] << 1)))
-                                       value = (dev->abs[code] + value) >> 1;
-                       }
+       switch (type) {
 
-                       if (dev->abs[code] == value)
-                               return;
+       case EV_SYN:
+               switch (code) {
+               case SYN_CONFIG:
+                       disposition = INPUT_PASS_TO_ALL;
+                       break;
 
-                       dev->abs[code] = value;
+               case SYN_REPORT:
+                       if (!dev->sync) {
+                               dev->sync = 1;
+                               disposition = INPUT_PASS_TO_HANDLERS;
+                       }
                        break;
+               }
+               break;
 
-               case EV_REL:
+       case EV_KEY:
+               if (is_event_supported(code, dev->keybit, KEY_MAX) &&
+                   !!test_bit(code, dev->key) != value) {
 
-                       if (code > REL_MAX || !test_bit(code, dev->relbit) || (value == 0))
-                               return;
+                       if (value != 2) {
+                               __change_bit(code, dev->key);
+                               if (value)
+                                       input_start_autorepeat(dev, code);
+                       }
 
-                       break;
+                       disposition = INPUT_PASS_TO_HANDLERS;
+               }
+               break;
 
-               case EV_MSC:
+       case EV_SW:
+               if (is_event_supported(code, dev->swbit, SW_MAX) &&
+                   !!test_bit(code, dev->sw) != value) {
 
-                       if (code > MSC_MAX || !test_bit(code, dev->mscbit))
-                               return;
+                       __change_bit(code, dev->sw);
+                       disposition = INPUT_PASS_TO_HANDLERS;
+               }
+               break;
 
-                       if (dev->event)
-                               dev->event(dev, type, code, value);
+       case EV_ABS:
+               if (is_event_supported(code, dev->absbit, ABS_MAX)) {
 
-                       break;
+                       value = input_defuzz_abs_event(value,
+                                       dev->abs[code], dev->absfuzz[code]);
+
+                       if (dev->abs[code] != value) {
+                               dev->abs[code] = value;
+                               disposition = INPUT_PASS_TO_HANDLERS;
+                       }
+               }
+               break;
 
-               case EV_LED:
+       case EV_REL:
+               if (is_event_supported(code, dev->relbit, REL_MAX) && value)
+                       disposition = INPUT_PASS_TO_HANDLERS;
 
-                       if (code > LED_MAX || !test_bit(code, dev->ledbit) || !!test_bit(code, dev->led) == value)
-                               return;
+               break;
 
-                       change_bit(code, dev->led);
+       case EV_MSC:
+               if (is_event_supported(code, dev->mscbit, MSC_MAX))
+                       disposition = INPUT_PASS_TO_ALL;
 
-                       if (dev->event)
-                               dev->event(dev, type, code, value);
+               break;
 
-                       break;
+       case EV_LED:
+               if (is_event_supported(code, dev->ledbit, LED_MAX) &&
+                   !!test_bit(code, dev->led) != value) {
 
-               case EV_SND:
+                       __change_bit(code, dev->led);
+                       disposition = INPUT_PASS_TO_ALL;
+               }
+               break;
 
-                       if (code > SND_MAX || !test_bit(code, dev->sndbit))
-                               return;
+       case EV_SND:
+               if (is_event_supported(code, dev->sndbit, SND_MAX)) {
 
                        if (!!test_bit(code, dev->snd) != !!value)
-                               change_bit(code, dev->snd);
+                               __change_bit(code, dev->snd);
+                       disposition = INPUT_PASS_TO_ALL;
+               }
+               break;
 
-                       if (dev->event)
-                               dev->event(dev, type, code, value);
+       case EV_REP:
+               if (code <= REP_MAX && value >= 0 && dev->rep[code] != value) {
+                       dev->rep[code] = value;
+                       disposition = INPUT_PASS_TO_ALL;
+               }
+               break;
 
-                       break;
+       case EV_FF:
+               if (value >= 0)
+                       disposition = INPUT_PASS_TO_ALL;
+               break;
+       }
 
-               case EV_REP:
+       if (type != EV_SYN)
+               dev->sync = 0;
 
-                       if (code > REP_MAX || value < 0 || dev->rep[code] == value)
-                               return;
+       if ((disposition & INPUT_PASS_TO_DEVICE) && dev->event)
+               dev->event(dev, type, code, value);
 
-                       dev->rep[code] = value;
-                       if (dev->event)
-                               dev->event(dev, type, code, value);
+       if (disposition & INPUT_PASS_TO_HANDLERS)
+               input_pass_event(dev, type, code, value);
+}
 
-                       break;
+/**
+ * input_event() - report new input event
+ * @dev: device that generated the event
+ * @type: type of the event
+ * @code: event code
+ * @value: value of the event
+ *
+ * This function should be used by drivers implementing various input
+ * devices. See also input_inject_event().
+ */
 
-               case EV_FF:
+void input_event(struct input_dev *dev,
+                unsigned int type, unsigned int code, int value)
+{
+       unsigned long flags;
 
-                       if (value < 0)
-                               return;
+       if (is_event_supported(type, dev->evbit, EV_MAX)) {
 
-                       if (dev->event)
-                               dev->event(dev, type, code, value);
-                       break;
+               spin_lock_irqsave(&dev->event_lock, flags);
+               add_input_randomness(type, code, value);
+               input_handle_event(dev, type, code, value);
+               spin_unlock_irqrestore(&dev->event_lock, flags);
        }
-
-       if (type != EV_SYN)
-               dev->sync = 0;
-
-       if (dev->grab)
-               dev->grab->handler->event(dev->grab, type, code, value);
-       else
-               list_for_each_entry(handle, &dev->h_list, d_node)
-                       if (handle->open)
-                               handle->handler->event(handle, type, code, value);
 }
 EXPORT_SYMBOL(input_event);
 
@@ -202,102 +280,228 @@ EXPORT_SYMBOL(input_event);
  * @code: event code
  * @value: value of the event
  *
- * Similar to input_event() but will ignore event if device is "grabbed" and handle
- * injecting event is not the one that owns the device.
+ * Similar to input_event() but will ignore event if device is
+ * "grabbed" and handle injecting event is not the one that owns
+ * the device.
  */
-void input_inject_event(struct input_handle *handle, unsigned int type, unsigned int code, int value)
-{
-       if (!handle->dev->grab || handle->dev->grab == handle)
-               input_event(handle->dev, type, code, value);
-}
-EXPORT_SYMBOL(input_inject_event);
-
-static void input_repeat_key(unsigned long data)
+void input_inject_event(struct input_handle *handle,
+                       unsigned int type, unsigned int code, int value)
 {
-       struct input_dev *dev = (void *) data;
+       struct input_dev *dev = handle->dev;
+       struct input_handle *grab;
+       unsigned long flags;
 
-       if (!test_bit(dev->repeat_key, dev->key))
-               return;
+       if (is_event_supported(type, dev->evbit, EV_MAX)) {
+               spin_lock_irqsave(&dev->event_lock, flags);
 
-       input_event(dev, EV_KEY, dev->repeat_key, 2);
-       input_sync(dev);
+               rcu_read_lock();
+               grab = rcu_dereference(dev->grab);
+               if (!grab || grab == handle)
+                       input_handle_event(dev, type, code, value);
+               rcu_read_unlock();
 
-       if (dev->rep[REP_PERIOD])
-               mod_timer(&dev->timer, jiffies + msecs_to_jiffies(dev->rep[REP_PERIOD]));
+               spin_unlock_irqrestore(&dev->event_lock, flags);
+       }
 }
+EXPORT_SYMBOL(input_inject_event);
 
+/**
+ * input_grab_device - grabs device for exclusive use
+ * @handle: input handle that wants to own the device
+ *
+ * When a device is grabbed by an input handle all events generated by
+ * the device are delivered only to this handle. Also events injected
+ * by other input handles are ignored while device is grabbed.
+ */
 int input_grab_device(struct input_handle *handle)
 {
-       if (handle->dev->grab)
-               return -EBUSY;
+       struct input_dev *dev = handle->dev;
+       int retval;
 
-       handle->dev->grab = handle;
-       return 0;
+       retval = mutex_lock_interruptible(&dev->mutex);
+       if (retval)
+               return retval;
+
+       if (dev->grab) {
+               retval = -EBUSY;
+               goto out;
+       }
+
+       rcu_assign_pointer(dev->grab, handle);
+       synchronize_rcu();
+
+ out:
+       mutex_unlock(&dev->mutex);
+       return retval;
 }
 EXPORT_SYMBOL(input_grab_device);
 
-void input_release_device(struct input_handle *handle)
+static void __input_release_device(struct input_handle *handle)
 {
        struct input_dev *dev = handle->dev;
 
        if (dev->grab == handle) {
-               dev->grab = NULL;
+               rcu_assign_pointer(dev->grab, NULL);
+               /* Make sure input_pass_event() notices that grab is gone */
+               synchronize_rcu();
 
                list_for_each_entry(handle, &dev->h_list, d_node)
-                       if (handle->handler->start)
+                       if (handle->open && handle->handler->start)
                                handle->handler->start(handle);
        }
 }
+
+/**
+ * input_release_device - release previously grabbed device
+ * @handle: input handle that owns the device
+ *
+ * Releases previously grabbed device so that other input handles can
+ * start receiving input events. Upon release all handlers attached
+ * to the device have their start() method called so they have a change
+ * to synchronize device state with the rest of the system.
+ */
+void input_release_device(struct input_handle *handle)
+{
+       struct input_dev *dev = handle->dev;
+
+       mutex_lock(&dev->mutex);
+       __input_release_device(handle);
+       mutex_unlock(&dev->mutex);
+}
 EXPORT_SYMBOL(input_release_device);
 
+/**
+ * input_open_device - open input device
+ * @handle: handle through which device is being accessed
+ *
+ * This function should be called by input handlers when they
+ * want to start receive events from given input device.
+ */
 int input_open_device(struct input_handle *handle)
 {
        struct input_dev *dev = handle->dev;
-       int err;
+       int retval;
 
-       err = mutex_lock_interruptible(&dev->mutex);
-       if (err)
-               return err;
+       retval = mutex_lock_interruptible(&dev->mutex);
+       if (retval)
+               return retval;
+
+       if (dev->going_away) {
+               retval = -ENODEV;
+               goto out;
+       }
 
        handle->open++;
 
        if (!dev->users++ && dev->open)
-               err = dev->open(dev);
-
-       if (err)
-               handle->open--;
+               retval = dev->open(dev);
+
+       if (retval) {
+               dev->users--;
+               if (!--handle->open) {
+                       /*
+                        * Make sure we are not delivering any more events
+                        * through this handle
+                        */
+                       synchronize_rcu();
+               }
+       }
 
+ out:
        mutex_unlock(&dev->mutex);
-
-       return err;
+       return retval;
 }
 EXPORT_SYMBOL(input_open_device);
 
-int input_flush_device(struct input_handle* handle, struct file* file)
+int input_flush_device(struct input_handle *handle, struct file *file)
 {
-       if (handle->dev->flush)
-               return handle->dev->flush(handle->dev, file);
+       struct input_dev *dev = handle->dev;
+       int retval;
 
-       return 0;
+       retval = mutex_lock_interruptible(&dev->mutex);
+       if (retval)
+               return retval;
+
+       if (dev->flush)
+               retval = dev->flush(dev, file);
+
+       mutex_unlock(&dev->mutex);
+       return retval;
 }
 EXPORT_SYMBOL(input_flush_device);
 
+/**
+ * input_close_device - close input device
+ * @handle: handle through which device is being accessed
+ *
+ * This function should be called by input handlers when they
+ * want to stop receive events from given input device.
+ */
 void input_close_device(struct input_handle *handle)
 {
        struct input_dev *dev = handle->dev;
 
-       input_release_device(handle);
-
        mutex_lock(&dev->mutex);
 
+       __input_release_device(handle);
+
        if (!--dev->users && dev->close)
                dev->close(dev);
-       handle->open--;
+
+       if (!--handle->open) {
+               /*
+                * synchronize_rcu() makes sure that input_pass_event()
+                * completed and that no more input events are delivered
+                * through this handle
+                */
+               synchronize_rcu();
+       }
 
        mutex_unlock(&dev->mutex);
 }
 EXPORT_SYMBOL(input_close_device);
 
+/*
+ * Prepare device for unregistering
+ */
+static void input_disconnect_device(struct input_dev *dev)
+{
+       struct input_handle *handle;
+       int code;
+
+       /*
+        * Mark device as going away. Note that we take dev->mutex here
+        * not to protect access to dev->going_away but rather to ensure
+        * that there are no threads in the middle of input_open_device()
+        */
+       mutex_lock(&dev->mutex);
+       dev->going_away = 1;
+       mutex_unlock(&dev->mutex);
+
+       spin_lock_irq(&dev->event_lock);
+
+       /*
+        * Simulate keyup events for all pressed keys so that handlers
+        * are not left with "stuck" keys. The driver may continue
+        * generate events even after we done here but they will not
+        * reach any handlers.
+        */
+       if (is_event_supported(EV_KEY, dev->evbit, EV_MAX)) {
+               for (code = 0; code <= KEY_MAX; code++) {
+                       if (is_event_supported(code, dev->keybit, KEY_MAX) &&
+                           test_bit(code, dev->key)) {
+                               input_pass_event(dev, EV_KEY, code, 0);
+                       }
+               }
+               input_pass_event(dev, EV_SYN, SYN_REPORT, 1);
+       }
+
+       list_for_each_entry(handle, &dev->h_list, d_node)
+               handle->open = 0;
+
+       spin_unlock_irq(&dev->event_lock);
+}
+
 static int input_fetch_keycode(struct input_dev *dev, int scancode)
 {
        switch (dev->keycodesize) {
@@ -473,7 +677,8 @@ static unsigned int input_proc_devices_poll(struct file *file, poll_table *wait)
 
 static void *input_devices_seq_start(struct seq_file *seq, loff_t *pos)
 {
-       /* acquire lock here ... Yes, we do need locking, I knowi, I know... */
+       if (mutex_lock_interruptible(&input_mutex))
+               return NULL;
 
        return seq_list_start(&input_dev_list, *pos);
 }
@@ -485,7 +690,7 @@ static void *input_devices_seq_next(struct seq_file *seq, void *v, loff_t *pos)
 
 static void input_devices_seq_stop(struct seq_file *seq, void *v)
 {
-       /* release lock here */
+       mutex_unlock(&input_mutex);
 }
 
 static void input_seq_print_bitmap(struct seq_file *seq, const char *name,
@@ -569,7 +774,9 @@ static const struct file_operations input_devices_fileops = {
 
 static void *input_handlers_seq_start(struct seq_file *seq, loff_t *pos)
 {
-       /* acquire lock here ... Yes, we do need locking, I knowi, I know... */
+       if (mutex_lock_interruptible(&input_mutex))
+               return NULL;
+
        seq->private = (void *)(unsigned long)*pos;
        return seq_list_start(&input_handler_list, *pos);
 }
@@ -582,7 +789,7 @@ static void *input_handlers_seq_next(struct seq_file *seq, void *v, loff_t *pos)
 
 static void input_handlers_seq_stop(struct seq_file *seq, void *v)
 {
-       /* release lock here */
+       mutex_unlock(&input_mutex);
 }
 
 static int input_handlers_seq_show(struct seq_file *seq, void *v)
@@ -983,6 +1190,7 @@ struct input_dev *input_allocate_device(void)
                dev->dev.class = &input_class;
                device_initialize(&dev->dev);
                mutex_init(&dev->mutex);
+               spin_lock_init(&dev->event_lock);
                INIT_LIST_HEAD(&dev->h_list);
                INIT_LIST_HEAD(&dev->node);
 
@@ -1000,7 +1208,7 @@ EXPORT_SYMBOL(input_allocate_device);
  * This function should only be used if input_register_device()
  * was not called yet or if it failed. Once device was registered
  * use input_unregister_device() and memory will be freed once last
- * refrence to the device is dropped.
+ * reference to the device is dropped.
  *
  * Device should be allocated by input_allocate_device().
  *
@@ -1070,6 +1278,18 @@ void input_set_capability(struct input_dev *dev, unsigned int type, unsigned int
 }
 EXPORT_SYMBOL(input_set_capability);
 
+/**
+ * input_register_device - register device with input core
+ * @dev: device to be registered
+ *
+ * This function registers device with input core. The device must be
+ * allocated with input_allocate_device() and all it's capabilities
+ * set up before registering.
+ * If function fails the device must be freed with input_free_device().
+ * Once device has been successfully registered it can be unregistered
+ * with input_unregister_device(); input_free_device() should not be
+ * called in this case.
+ */
 int input_register_device(struct input_dev *dev)
 {
        static atomic_t input_no = ATOMIC_INIT(0);
@@ -1077,7 +1297,7 @@ int input_register_device(struct input_dev *dev)
        const char *path;
        int error;
 
-       set_bit(EV_SYN, dev->evbit);
+       __set_bit(EV_SYN, dev->evbit);
 
        /*
         * If delay and period are pre-set by the driver, then autorepeating
@@ -1098,8 +1318,6 @@ int input_register_device(struct input_dev *dev)
        if (!dev->setkeycode)
                dev->setkeycode = input_default_setkeycode;
 
-       list_add_tail(&dev->node, &input_dev_list);
-
        snprintf(dev->dev.bus_id, sizeof(dev->dev.bus_id),
                 "input%ld", (unsigned long) atomic_inc_return(&input_no) - 1);
 
@@ -1115,49 +1333,79 @@ int input_register_device(struct input_dev *dev)
                dev->name ? dev->name : "Unspecified device", path ? path : "N/A");
        kfree(path);
 
+       error = mutex_lock_interruptible(&input_mutex);
+       if (error) {
+               device_del(&dev->dev);
+               return error;
+       }
+
+       list_add_tail(&dev->node, &input_dev_list);
+
        list_for_each_entry(handler, &input_handler_list, node)
                input_attach_handler(dev, handler);
 
        input_wakeup_procfs_readers();
 
+       mutex_unlock(&input_mutex);
+
        return 0;
 }
 EXPORT_SYMBOL(input_register_device);
 
+/**
+ * input_unregister_device - unregister previously registered device
+ * @dev: device to be unregistered
+ *
+ * This function unregisters an input device. Once device is unregistered
+ * the caller should not try to access it as it may get freed at any moment.
+ */
 void input_unregister_device(struct input_dev *dev)
 {
        struct input_handle *handle, *next;
-       int code;
 
-       for (code = 0; code <= KEY_MAX; code++)
-               if (test_bit(code, dev->key))
-                       input_report_key(dev, code, 0);
-       input_sync(dev);
+       input_disconnect_device(dev);
 
-       del_timer_sync(&dev->timer);
+       mutex_lock(&input_mutex);
 
        list_for_each_entry_safe(handle, next, &dev->h_list, d_node)
                handle->handler->disconnect(handle);
        WARN_ON(!list_empty(&dev->h_list));
 
+       del_timer_sync(&dev->timer);
        list_del_init(&dev->node);
 
-       device_unregister(&dev->dev);
-
        input_wakeup_procfs_readers();
+
+       mutex_unlock(&input_mutex);
+
+       device_unregister(&dev->dev);
 }
 EXPORT_SYMBOL(input_unregister_device);
 
+/**
+ * input_register_handler - register a new input handler
+ * @handler: handler to be registered
+ *
+ * This function registers a new input handler (interface) for input
+ * devices in the system and attaches it to all input devices that
+ * are compatible with the handler.
+ */
 int input_register_handler(struct input_handler *handler)
 {
        struct input_dev *dev;
+       int retval;
+
+       retval = mutex_lock_interruptible(&input_mutex);
+       if (retval)
+               return retval;
 
        INIT_LIST_HEAD(&handler->h_list);
 
        if (handler->fops != NULL) {
-               if (input_table[handler->minor >> 5])
-                       return -EBUSY;
-
+               if (input_table[handler->minor >> 5]) {
+                       retval = -EBUSY;
+                       goto out;
+               }
                input_table[handler->minor >> 5] = handler;
        }
 
@@ -1167,14 +1415,26 @@ int input_register_handler(struct input_handler *handler)
                input_attach_handler(dev, handler);
 
        input_wakeup_procfs_readers();
-       return 0;
+
+ out:
+       mutex_unlock(&input_mutex);
+       return retval;
 }
 EXPORT_SYMBOL(input_register_handler);
 
+/**
+ * input_unregister_handler - unregisters an input handler
+ * @handler: handler to be unregistered
+ *
+ * This function disconnects a handler from its input devices and
+ * removes it from lists of known handlers.
+ */
 void input_unregister_handler(struct input_handler *handler)
 {
        struct input_handle *handle, *next;
 
+       mutex_lock(&input_mutex);
+
        list_for_each_entry_safe(handle, next, &handler->h_list, h_node)
                handler->disconnect(handle);
        WARN_ON(!list_empty(&handler->h_list));
@@ -1185,14 +1445,45 @@ void input_unregister_handler(struct input_handler *handler)
                input_table[handler->minor >> 5] = NULL;
 
        input_wakeup_procfs_readers();
+
+       mutex_unlock(&input_mutex);
 }
 EXPORT_SYMBOL(input_unregister_handler);
 
+/**
+ * input_register_handle - register a new input handle
+ * @handle: handle to register
+ *
+ * This function puts a new input handle onto device's
+ * and handler's lists so that events can flow through
+ * it once it is opened using input_open_device().
+ *
+ * This function is supposed to be called from handler's
+ * connect() method.
+ */
 int input_register_handle(struct input_handle *handle)
 {
        struct input_handler *handler = handle->handler;
+       struct input_dev *dev = handle->dev;
+       int error;
+
+       /*
+        * We take dev->mutex here to prevent race with
+        *&