]> nv-tegra.nvidia Code Review - linux-2.6.git/commitdiff
Merge branches 'core/softlockup', 'core/softirq', 'core/resources', 'core/printk...
authorIngo Molnar <mingo@elte.hu>
Wed, 15 Oct 2008 10:48:44 +0000 (12:48 +0200)
committerIngo Molnar <mingo@elte.hu>
Wed, 15 Oct 2008 10:48:44 +0000 (12:48 +0200)
327 files changed:
Documentation/ABI/testing/sysfs-class-regulator
Documentation/feature-removal-schedule.txt
Documentation/filesystems/ocfs2.txt
Documentation/i2c/busses/i2c-viapro
Documentation/i2c/dev-interface
Documentation/i2c/smbus-protocol
Documentation/i2c/writing-clients
Documentation/power/regulator/machine.txt
Documentation/power/regulator/regulator.txt
MAINTAINERS
arch/arm/mach-omap1/board-h3.c
arch/arm/mach-omap2/board-h4.c
arch/m32r/mm/discontig.c
arch/m68k/Kconfig
arch/m68k/amiga/config.c
arch/m68k/atari/Makefile
arch/m68k/atari/ataints.c
arch/m68k/atari/atakeyb.c
arch/m68k/atari/config.c
arch/m68k/atari/hades-pci.c [deleted file]
arch/m68k/atari/time.c
arch/m68k/bvme6000/config.c
arch/m68k/bvme6000/rtc.c
arch/m68k/kernel/Makefile
arch/m68k/kernel/bios32.c [deleted file]
arch/m68k/kernel/dma.c
arch/m68k/kernel/ints.c
arch/m68k/kernel/process.c
arch/m68k/kernel/traps.c
arch/m68k/kernel/vmlinux-std.lds
arch/m68k/mm/kmap.c
arch/m68k/mvme16x/rtc.c
arch/m68k/q40/config.c
arch/m68k/sun3x/time.c
arch/powerpc/Kconfig
arch/powerpc/include/asm/types.h
arch/powerpc/platforms/Kconfig.cputype
arch/powerpc/sysdev/ppc4xx_pci.c
arch/x86/Kconfig
arch/x86/kernel/e820.c
arch/x86/kernel/rtc.c
arch/x86/mm/ioremap.c
drivers/acpi/glue.c
drivers/block/ataflop.c
drivers/char/vt.c
drivers/firmware/dmi_scan.c
drivers/hid/Kconfig
drivers/hid/Makefile
drivers/hid/hid-a4tech.c [new file with mode: 0644]
drivers/hid/hid-apple.c [new file with mode: 0644]
drivers/hid/hid-belkin.c [new file with mode: 0644]
drivers/hid/hid-bright.c [new file with mode: 0644]
drivers/hid/hid-cherry.c [new file with mode: 0644]
drivers/hid/hid-chicony.c [new file with mode: 0644]
drivers/hid/hid-core.c
drivers/hid/hid-cypress.c [new file with mode: 0644]
drivers/hid/hid-dell.c [new file with mode: 0644]
drivers/hid/hid-dummy.c [new file with mode: 0644]
drivers/hid/hid-ezkey.c [new file with mode: 0644]
drivers/hid/hid-gyration.c [new file with mode: 0644]
drivers/hid/hid-ids.h [new file with mode: 0644]
drivers/hid/hid-input-quirks.c [deleted file]
drivers/hid/hid-input.c
drivers/hid/hid-lg.c [new file with mode: 0644]
drivers/hid/hid-lg.h [new file with mode: 0644]
drivers/hid/hid-lg2ff.c [moved from drivers/hid/usbhid/hid-lg2ff.c with 90% similarity]
drivers/hid/hid-lgff.c [moved from drivers/hid/usbhid/hid-lgff.c with 81% similarity]
drivers/hid/hid-microsoft.c [new file with mode: 0644]
drivers/hid/hid-monterey.c [new file with mode: 0644]
drivers/hid/hid-petalynx.c [new file with mode: 0644]
drivers/hid/hid-pl.c [moved from drivers/hid/usbhid/hid-plff.c with 68% similarity]
drivers/hid/hid-samsung.c [new file with mode: 0644]
drivers/hid/hid-sony.c [new file with mode: 0644]
drivers/hid/hid-sunplus.c [new file with mode: 0644]
drivers/hid/hid-tmff.c [moved from drivers/hid/usbhid/hid-tmff.c with 60% similarity]
drivers/hid/hid-zpff.c [moved from drivers/hid/usbhid/hid-zpff.c with 67% similarity]
drivers/hid/hidraw.c
drivers/hid/usbhid/Kconfig
drivers/hid/usbhid/Makefile
drivers/hid/usbhid/hid-core.c
drivers/hid/usbhid/hid-ff.c [deleted file]
drivers/hid/usbhid/hid-pidff.c
drivers/hid/usbhid/hid-quirks.c
drivers/hid/usbhid/hiddev.c
drivers/hid/usbhid/usbhid.h
drivers/hid/usbhid/usbkbd.c
drivers/hid/usbhid/usbmouse.c
drivers/hwmon/dme1737.c
drivers/i2c/busses/Kconfig
drivers/i2c/busses/Makefile
drivers/i2c/busses/i2c-highlander.c [new file with mode: 0644]
drivers/i2c/busses/i2c-parport-light.c
drivers/i2c/busses/i2c-pca-isa.c
drivers/i2c/busses/i2c-viapro.c
drivers/i2c/chips/isp1301_omap.c
drivers/i2c/chips/tps65010.c
drivers/i2c/i2c-core.c
drivers/input/misc/hp_sdc_rtc.c
drivers/input/serio/hp_sdc.c
drivers/isdn/mISDN/dsp_cmx.c
drivers/isdn/mISDN/timerdev.c
drivers/md/Kconfig
drivers/mfd/Kconfig
drivers/mfd/Makefile
drivers/mfd/wm8350-core.c [new file with mode: 0644]
drivers/mfd/wm8350-gpio.c [new file with mode: 0644]
drivers/mfd/wm8350-i2c.c [new file with mode: 0644]
drivers/mfd/wm8350-regmap.c [new file with mode: 0644]
drivers/mfd/wm8400-core.c [new file with mode: 0644]
drivers/net/3c501.c
drivers/net/3c515.c
drivers/net/Kconfig
drivers/net/appletalk/cops.c
drivers/net/cxgb3/adapter.h
drivers/net/cxgb3/ael1002.c
drivers/net/cxgb3/common.h
drivers/net/cxgb3/cxgb3_ctl_defs.h
drivers/net/cxgb3/cxgb3_defs.h
drivers/net/cxgb3/cxgb3_ioctl.h
drivers/net/cxgb3/cxgb3_main.c
drivers/net/cxgb3/cxgb3_offload.c
drivers/net/cxgb3/cxgb3_offload.h
drivers/net/cxgb3/firmware_exports.h
drivers/net/cxgb3/l2t.c
drivers/net/cxgb3/l2t.h
drivers/net/cxgb3/mc5.c
drivers/net/cxgb3/sge.c
drivers/net/cxgb3/t3_cpl.h
drivers/net/cxgb3/t3_hw.c
drivers/net/cxgb3/t3cdev.h
drivers/net/cxgb3/version.h
drivers/net/cxgb3/vsc8211.c
drivers/net/cxgb3/xgmac.c
drivers/net/eexpress.c
drivers/net/enic/enic_main.c
drivers/net/ibmlana.c
drivers/net/jme.c
drivers/net/macmace.c
drivers/net/pcmcia/3c589_cs.c
drivers/net/pcmcia/nmclan_cs.c
drivers/net/phy/mdio_bus.c
drivers/net/phy/phy_device.c
drivers/net/qlge/qlge.h
drivers/net/qlge/qlge_main.c
drivers/net/tlan.c
drivers/net/tokenring/smctr.c
drivers/net/tulip/de2104x.c
drivers/net/tulip/dmfe.c
drivers/net/via-velocity.c
drivers/net/wan/z85230.c
drivers/net/wan/z85230.h
drivers/net/wireless/wavelan.c
drivers/net/wireless/wavelan.p.h
drivers/pci/hotplug/ibmphp_ebda.c
drivers/pci/setup-bus.c
drivers/regulator/Kconfig
drivers/regulator/Makefile
drivers/regulator/bq24022.c
drivers/regulator/core.c
drivers/regulator/da903x.c [new file with mode: 0644]
drivers/regulator/wm8350-regulator.c [new file with mode: 0644]
drivers/regulator/wm8400-regulator.c [new file with mode: 0644]
drivers/rtc/rtc-cmos.c
drivers/s390/net/claw.c
drivers/scsi/Kconfig
drivers/scsi/atari_dma_emul.c [deleted file]
drivers/scsi/atari_scsi.c
drivers/serial/8250.c
drivers/video/console/fbcon.c
drivers/video/console/mdacon.c
drivers/video/console/sticon.c
drivers/video/console/vgacon.c
fs/Kconfig
fs/Makefile
fs/lockd/Makefile
fs/lockd/clntlock.c
fs/lockd/grace.c [new file with mode: 0644]
fs/lockd/host.c
fs/lockd/mon.c
fs/lockd/svc.c
fs/lockd/svc4proc.c
fs/lockd/svclock.c
fs/lockd/svcproc.c
fs/lockd/svcsubs.c
fs/lockd/xdr.c
fs/lockd/xdr4.c
fs/nfs/callback.c
fs/nfsd/lockd.c
fs/nfsd/nfs3proc.c
fs/nfsd/nfs4callback.c
fs/nfsd/nfs4proc.c
fs/nfsd/nfs4state.c
fs/nfsd/nfs4xdr.c
fs/nfsd/nfsctl.c
fs/nfsd/nfsfh.c
fs/nfsd/nfsproc.c
fs/nfsd/nfssvc.c
fs/nfsd/vfs.c
fs/ocfs2/Makefile
fs/ocfs2/alloc.c
fs/ocfs2/alloc.h
fs/ocfs2/aops.c
fs/ocfs2/buffer_head_io.c
fs/ocfs2/buffer_head_io.h
fs/ocfs2/cluster/masklog.c
fs/ocfs2/cluster/masklog.h
fs/ocfs2/dir.c
fs/ocfs2/dlmglue.c
fs/ocfs2/extent_map.c
fs/ocfs2/extent_map.h
fs/ocfs2/file.c
fs/ocfs2/file.h
fs/ocfs2/inode.c
fs/ocfs2/inode.h
fs/ocfs2/ioctl.c
fs/ocfs2/journal.c
fs/ocfs2/journal.h
fs/ocfs2/localalloc.c
fs/ocfs2/localalloc.h
fs/ocfs2/locks.c
fs/ocfs2/locks.h
fs/ocfs2/namei.c
fs/ocfs2/ocfs2.h
fs/ocfs2/ocfs2_fs.h
fs/ocfs2/ocfs2_jbd_compat.h [new file with mode: 0644]
fs/ocfs2/resize.c
fs/ocfs2/slot_map.c
fs/ocfs2/stack_user.c
fs/ocfs2/stackglue.c
fs/ocfs2/stackglue.h
fs/ocfs2/suballoc.c
fs/ocfs2/suballoc.h
fs/ocfs2/super.c
fs/ocfs2/symlink.c
fs/ocfs2/uptodate.c
fs/ocfs2/uptodate.h
fs/ocfs2/xattr.c [new file with mode: 0644]
fs/ocfs2/xattr.h [new file with mode: 0644]
fs/proc/proc_misc.c
include/asm-m68k/atarihw.h
include/asm-m68k/dma-mapping.h
include/asm-m68k/dma.h
include/asm-m68k/entry.h
include/asm-m68k/io.h
include/asm-m68k/pci.h
include/asm-m68k/virtconvert.h
include/asm-x86/page_32.h
include/asm-x86/page_64.h
include/asm-x86/xen/page.h
include/linux/console_struct.h
include/linux/fs.h
include/linux/hid.h
include/linux/hiddev.h
include/linux/if_ether.h
include/linux/if_fddi.h
include/linux/if_hippi.h
include/linux/igmp.h
include/linux/interrupt.h
include/linux/ioport.h
include/linux/kernel.h
include/linux/lockd/bind.h
include/linux/lockd/lockd.h
include/linux/lockd/xdr.h
include/linux/mfd/wm8350/audio.h [new file with mode: 0644]
include/linux/mfd/wm8350/comparator.h [new file with mode: 0644]
include/linux/mfd/wm8350/core.h [new file with mode: 0644]
include/linux/mfd/wm8350/gpio.h [new file with mode: 0644]
include/linux/mfd/wm8350/pmic.h [new file with mode: 0644]
include/linux/mfd/wm8350/rtc.h [new file with mode: 0644]
include/linux/mfd/wm8350/supply.h [new file with mode: 0644]
include/linux/mfd/wm8350/wdt.h [new file with mode: 0644]
include/linux/mfd/wm8400-audio.h [new file with mode: 0644]
include/linux/mfd/wm8400-private.h [new file with mode: 0644]
include/linux/mfd/wm8400.h [new file with mode: 0644]
include/linux/mod_devicetable.h
include/linux/netdevice.h
include/linux/nfsd/nfsd.h
include/linux/pfn.h
include/linux/regulator/driver.h
include/linux/regulator/machine.h
include/linux/sunrpc/clnt.h
include/linux/sunrpc/svc.h
include/linux/sunrpc/svc_rdma.h
include/linux/sunrpc/svcsock.h
include/linux/types.h
init/do_mounts_md.c
init/main.c
kernel/printk.c
kernel/resource.c
kernel/softirq.c
kernel/sys_ni.c
kernel/sysctl.c
kernel/time/tick-sched.c
kernel/timer.c
mm/Kconfig
net/802/psnap.c
net/appletalk/ddp.c
net/bluetooth/hidp/core.c
net/bluetooth/hidp/hidp.h
net/core/datagram.c
net/core/dev_mcast.c
net/core/pktgen.c
net/core/skbuff.c
net/core/stream.c
net/dsa/Kconfig
net/ipv4/icmp.c
net/ipv4/igmp.c
net/ipv4/ip_fragment.c
net/ipv4/ip_input.c
net/ipv4/ipip.c
net/ipv4/ipmr.c
net/ipv4/udp.c
net/ipv6/af_inet6.c
net/netlink/af_netlink.c
net/rfkill/rfkill-input.c
net/sunrpc/clnt.c
net/sunrpc/rpcb_clnt.c
net/sunrpc/svc.c
net/sunrpc/svc_xprt.c
net/sunrpc/svcsock.c
net/sunrpc/xprtrdma/svc_rdma_recvfrom.c
net/sunrpc/xprtrdma/svc_rdma_sendto.c
net/sunrpc/xprtrdma/svc_rdma_transport.c
net/sunrpc/xprtsock.c
net/unix/af_unix.c
scripts/bootgraph.pl [new file with mode: 0644]
scripts/mod/file2alias.c

index 79a4a75b2d2ceb7af8e07c3efa6289c3722d6342..3731f6f29bcb3f07d5551d7fa41f00f8c0c0ed9a 100644 (file)
@@ -1,7 +1,7 @@
 What:          /sys/class/regulator/.../state
 Date:          April 2008
 KernelVersion: 2.6.26
-Contact:       Liam Girdwood <lg@opensource.wolfsonmicro.com>
+Contact:       Liam Girdwood <lrg@slimlogic.co.uk>
 Description:
                Each regulator directory will contain a field called
                state. This holds the regulator output state.
@@ -27,7 +27,7 @@ Description:
 What:          /sys/class/regulator/.../type
 Date:          April 2008
 KernelVersion: 2.6.26
-Contact:       Liam Girdwood <lg@opensource.wolfsonmicro.com>
+Contact:       Liam Girdwood <lrg@slimlogic.co.uk>
 Description:
                Each regulator directory will contain a field called
                type. This holds the regulator type.
@@ -51,7 +51,7 @@ Description:
 What:          /sys/class/regulator/.../microvolts
 Date:          April 2008
 KernelVersion: 2.6.26
-Contact:       Liam Girdwood <lg@opensource.wolfsonmicro.com>
+Contact:       Liam Girdwood <lrg@slimlogic.co.uk>
 Description:
                Each regulator directory will contain a field called
                microvolts. This holds the regulator output voltage setting
@@ -65,7 +65,7 @@ Description:
 What:          /sys/class/regulator/.../microamps
 Date:          April 2008
 KernelVersion: 2.6.26
-Contact:       Liam Girdwood <lg@opensource.wolfsonmicro.com>
+Contact:       Liam Girdwood <lrg@slimlogic.co.uk>
 Description:
                Each regulator directory will contain a field called
                microamps. This holds the regulator output current limit
@@ -79,7 +79,7 @@ Description:
 What:          /sys/class/regulator/.../opmode
 Date:          April 2008
 KernelVersion: 2.6.26
-Contact:       Liam Girdwood <lg@opensource.wolfsonmicro.com>
+Contact:       Liam Girdwood <lrg@slimlogic.co.uk>
 Description:
                Each regulator directory will contain a field called
                opmode. This holds the regulator operating mode setting.
@@ -102,7 +102,7 @@ Description:
 What:          /sys/class/regulator/.../min_microvolts
 Date:          April 2008
 KernelVersion: 2.6.26
-Contact:       Liam Girdwood <lg@opensource.wolfsonmicro.com>
+Contact:       Liam Girdwood <lrg@slimlogic.co.uk>
 Description:
                Each regulator directory will contain a field called
                min_microvolts. This holds the minimum safe working regulator
@@ -116,7 +116,7 @@ Description:
 What:          /sys/class/regulator/.../max_microvolts
 Date:          April 2008
 KernelVersion: 2.6.26
-Contact:       Liam Girdwood <lg@opensource.wolfsonmicro.com>
+Contact:       Liam Girdwood <lrg@slimlogic.co.uk>
 Description:
                Each regulator directory will contain a field called
                max_microvolts. This holds the maximum safe working regulator
@@ -130,7 +130,7 @@ Description:
 What:          /sys/class/regulator/.../min_microamps
 Date:          April 2008
 KernelVersion: 2.6.26
-Contact:       Liam Girdwood <lg@opensource.wolfsonmicro.com>
+Contact:       Liam Girdwood <lrg@slimlogic.co.uk>
 Description:
                Each regulator directory will contain a field called
                min_microamps. This holds the minimum safe working regulator
@@ -145,7 +145,7 @@ Description:
 What:          /sys/class/regulator/.../max_microamps
 Date:          April 2008
 KernelVersion: 2.6.26
-Contact:       Liam Girdwood <lg@opensource.wolfsonmicro.com>
+Contact:       Liam Girdwood <lrg@slimlogic.co.uk>
 Description:
                Each regulator directory will contain a field called
                max_microamps. This holds the maximum safe working regulator
@@ -157,10 +157,23 @@ Description:
                platform code.
 
 
+What:          /sys/class/regulator/.../name
+Date:          October 2008
+KernelVersion: 2.6.28
+Contact:       Liam Girdwood <lrg@slimlogic.co.uk>
+Description:
+               Each regulator directory will contain a field called
+               name. This holds a string identifying the regulator for
+               display purposes.
+
+               NOTE: this will be empty if no suitable name is provided
+               by platform or regulator drivers.
+
+
 What:          /sys/class/regulator/.../num_users
 Date:          April 2008
 KernelVersion: 2.6.26
-Contact:       Liam Girdwood <lg@opensource.wolfsonmicro.com>
+Contact:       Liam Girdwood <lrg@slimlogic.co.uk>
 Description:
                Each regulator directory will contain a field called
                num_users. This holds the number of consumer devices that
@@ -170,7 +183,7 @@ Description:
 What:          /sys/class/regulator/.../requested_microamps
 Date:          April 2008
 KernelVersion: 2.6.26
-Contact:       Liam Girdwood <lg@opensource.wolfsonmicro.com>
+Contact:       Liam Girdwood <lrg@slimlogic.co.uk>
 Description:
                Each regulator directory will contain a field called
                requested_microamps. This holds the total requested load
@@ -181,7 +194,7 @@ Description:
 What:          /sys/class/regulator/.../parent
 Date:          April 2008
 KernelVersion: 2.6.26
-Contact:       Liam Girdwood <lg@opensource.wolfsonmicro.com>
+Contact:       Liam Girdwood <lrg@slimlogic.co.uk>
 Description:
                Some regulator directories will contain a link called parent.
                This points to the parent or supply regulator if one exists.
@@ -189,7 +202,7 @@ Description:
 What:          /sys/class/regulator/.../suspend_mem_microvolts
 Date:          May 2008
 KernelVersion: 2.6.26
-Contact:       Liam Girdwood <lg@opensource.wolfsonmicro.com>
+Contact:       Liam Girdwood <lrg@slimlogic.co.uk>
 Description:
                Each regulator directory will contain a field called
                suspend_mem_microvolts. This holds the regulator output
@@ -203,7 +216,7 @@ Description:
 What:          /sys/class/regulator/.../suspend_disk_microvolts
 Date:          May 2008
 KernelVersion: 2.6.26
-Contact:       Liam Girdwood <lg@opensource.wolfsonmicro.com>
+Contact:       Liam Girdwood <lrg@slimlogic.co.uk>
 Description:
                Each regulator directory will contain a field called
                suspend_disk_microvolts. This holds the regulator output
@@ -217,7 +230,7 @@ Description:
 What:          /sys/class/regulator/.../suspend_standby_microvolts
 Date:          May 2008
 KernelVersion: 2.6.26
-Contact:       Liam Girdwood <lg@opensource.wolfsonmicro.com>
+Contact:       Liam Girdwood <lrg@slimlogic.co.uk>
 Description:
                Each regulator directory will contain a field called
                suspend_standby_microvolts. This holds the regulator output
@@ -231,7 +244,7 @@ Description:
 What:          /sys/class/regulator/.../suspend_mem_mode
 Date:          May 2008
 KernelVersion: 2.6.26
-Contact:       Liam Girdwood <lg@opensource.wolfsonmicro.com>
+Contact:       Liam Girdwood <lrg@slimlogic.co.uk>
 Description:
                Each regulator directory will contain a field called
                suspend_mem_mode. This holds the regulator operating mode
@@ -245,7 +258,7 @@ Description:
 What:          /sys/class/regulator/.../suspend_disk_mode
 Date:          May 2008
 KernelVersion: 2.6.26
-Contact:       Liam Girdwood <lg@opensource.wolfsonmicro.com>
+Contact:       Liam Girdwood <lrg@slimlogic.co.uk>
 Description:
                Each regulator directory will contain a field called
                suspend_disk_mode. This holds the regulator operating mode
@@ -258,7 +271,7 @@ Description:
 What:          /sys/class/regulator/.../suspend_standby_mode
 Date:          May 2008
 KernelVersion: 2.6.26
-Contact:       Liam Girdwood <lg@opensource.wolfsonmicro.com>
+Contact:       Liam Girdwood <lrg@slimlogic.co.uk>
 Description:
                Each regulator directory will contain a field called
                suspend_standby_mode. This holds the regulator operating mode
@@ -272,7 +285,7 @@ Description:
 What:          /sys/class/regulator/.../suspend_mem_state
 Date:          May 2008
 KernelVersion: 2.6.26
-Contact:       Liam Girdwood <lg@opensource.wolfsonmicro.com>
+Contact:       Liam Girdwood <lrg@slimlogic.co.uk>
 Description:
                Each regulator directory will contain a field called
                suspend_mem_state. This holds the regulator operating state
@@ -287,7 +300,7 @@ Description:
 What:          /sys/class/regulator/.../suspend_disk_state
 Date:          May 2008
 KernelVersion: 2.6.26
-Contact:       Liam Girdwood <lg@opensource.wolfsonmicro.com>
+Contact:       Liam Girdwood <lrg@slimlogic.co.uk>
 Description:
                Each regulator directory will contain a field called
                suspend_disk_state. This holds the regulator operating state
@@ -302,7 +315,7 @@ Description:
 What:          /sys/class/regulator/.../suspend_standby_state
 Date:          May 2008
 KernelVersion: 2.6.26
-Contact:       Liam Girdwood <lg@opensource.wolfsonmicro.com>
+Contact:       Liam Girdwood <lrg@slimlogic.co.uk>
 Description:
                Each regulator directory will contain a field called
                suspend_standby_state. This holds the regulator operating
index cc8093c15cf5ce4c063aed880acfce7555903986..4d2566a7d16836d04f77c26bad5ebe6d38019dd9 100644 (file)
@@ -287,6 +287,13 @@ Who:       Glauber Costa <gcosta@redhat.com>
 
 ---------------------------
 
+What:  remove HID compat support
+When:  2.6.29
+Why:   needed only as a temporary solution until distros fix themselves up
+Who:   Jiri Slaby <jirislaby@gmail.com>
+
+---------------------------
+
 What:  /sys/o2cb symlink
 When:  January 2010
 Why:   /sys/fs/o2cb is the proper location for this information - /sys/o2cb
index c318a8bbb1ef1efdbd68930ed687595f3c397121..4340cc825796e87d0af97df690ef4ae13fd4e320 100644 (file)
@@ -76,3 +76,9 @@ localalloc=8(*)               Allows custom localalloc size in MB. If the value is too
                        large, the fs will silently revert it to the default.
                        Localalloc is not enabled for local mounts.
 localflocks            This disables cluster aware flock.
+inode64                        Indicates that Ocfs2 is allowed to create inodes at
+                       any location in the filesystem, including those which
+                       will result in inode numbers occupying more than 32
+                       bits of significance.
+user_xattr     (*)     Enables Extended User Attributes.
+nouser_xattr           Disables Extended User Attributes.
index 1405fb69984c7533c22030226698866fead7569f..22efedf60c872869e5230eb5ccf66c166634e22c 100644 (file)
@@ -16,6 +16,9 @@ Supported adapters:
   * VIA Technologies, Inc. CX700
     Datasheet: available on request and under NDA from VIA
 
+  * VIA Technologies, Inc. VX800/VX820
+    Datasheet: available on http://linux.via.com.tw
+
 Authors:
        Kyösti Mälkki <kmalkki@cc.hut.fi>,
        Mark D. Studebaker <mdsxyz123@yahoo.com>,
@@ -49,6 +52,7 @@ Your lspci -n listing must show one of these :
  device 1106:3372   (VT8237S)
  device 1106:3287   (VT8251)
  device 1106:8324   (CX700)
+ device 1106:8353   (VX800/VX820)
 
 If none of these show up, you should look in the BIOS for settings like
 enable ACPI / SMBus or even USB.
@@ -57,5 +61,5 @@ Except for the oldest chips (VT82C596A/B, VT82C686A and most probably
 VT8231), this driver supports I2C block transactions. Such transactions
 are mainly useful to read from and write to EEPROMs.
 
-The CX700 additionally appears to support SMBus PEC, although this driver
-doesn't implement it yet.
+The CX700/VX800/VX820 additionally appears to support SMBus PEC, although
+this driver doesn't implement it yet.
index 9dd79123ddd9949de863a0c2c20e98f99269a8c4..3e742ba25536123dc4108c6eb2db860584d9b442 100644 (file)
@@ -4,6 +4,10 @@ the /dev interface. You need to load module i2c-dev for this.
 
 Each registered i2c adapter gets a number, counting from 0. You can
 examine /sys/class/i2c-dev/ to see what number corresponds to which adapter.
+Alternatively, you can run "i2cdetect -l" to obtain a formated list of all
+i2c adapters present on your system at a given time. i2cdetect is part of
+the i2c-tools package.
+
 I2C device files are character device files with major device number 89
 and a minor device number corresponding to the number assigned as 
 explained above. They should be called "i2c-%d" (i2c-0, i2c-1, ..., 
@@ -17,30 +21,34 @@ So let's say you want to access an i2c adapter from a C program. The
 first thing to do is "#include <linux/i2c-dev.h>". Please note that
 there are two files named "i2c-dev.h" out there, one is distributed
 with the Linux kernel and is meant to be included from kernel
-driver code, the other one is distributed with lm_sensors and is
+driver code, the other one is distributed with i2c-tools and is
 meant to be included from user-space programs. You obviously want
 the second one here.
 
 Now, you have to decide which adapter you want to access. You should
-inspect /sys/class/i2c-dev/ to decide this. Adapter numbers are assigned
-somewhat dynamically, so you can not even assume /dev/i2c-0 is the
-first adapter.
+inspect /sys/class/i2c-dev/ or run "i2cdetect -l" to decide this.
+Adapter numbers are assigned somewhat dynamically, so you can not
+assume much about them. They can even change from one boot to the next.
 
 Next thing, open the device file, as follows:
+
   int file;
   int adapter_nr = 2; /* probably dynamically determined */
   char filename[20];
   
-  sprintf(filename,"/dev/i2c-%d",adapter_nr);
-  if ((file = open(filename,O_RDWR)) < 0) {
+  snprintf(filename, 19, "/dev/i2c-%d", adapter_nr);
+  file = open(filename, O_RDWR);
+  if (file < 0) {
     /* ERROR HANDLING; you can check errno to see what went wrong */
     exit(1);
   }
 
 When you have opened the device, you must specify with what device
 address you want to communicate:
+
   int addr = 0x40; /* The I2C address */
-  if (ioctl(file,I2C_SLAVE,addr) < 0) {
+
+  if (ioctl(file, I2C_SLAVE, addr) < 0) {
     /* ERROR HANDLING; you can check errno to see what went wrong */
     exit(1);
   }
@@ -48,31 +56,41 @@ address you want to communicate:
 Well, you are all set up now. You can now use SMBus commands or plain
 I2C to communicate with your device. SMBus commands are preferred if
 the device supports them. Both are illustrated below.
+
   __u8 register = 0x10; /* Device register to access */
   __s32 res;
   char buf[10];
+
   /* Using SMBus commands */
-  res = i2c_smbus_read_word_data(file,register);
+  res = i2c_smbus_read_word_data(file, register);
   if (res < 0) {
     /* ERROR HANDLING: i2c transaction failed */
   } else {
     /* res contains the read word */
   }
+
   /* Using I2C Write, equivalent of 
-           i2c_smbus_write_word_data(file,register,0x6543) */
+     i2c_smbus_write_word_data(file, register, 0x6543) */
   buf[0] = register;
   buf[1] = 0x43;
   buf[2] = 0x65;
-  if ( write(file,buf,3) != 3) {
+  if (write(file, buf, 3) ! =3) {
     /* ERROR HANDLING: i2c transaction failed */
   }
+
   /* Using I2C Read, equivalent of i2c_smbus_read_byte(file) */
-  if (read(file,buf,1) != 1) {
+  if (read(file, buf, 1) != 1) {
     /* ERROR HANDLING: i2c transaction failed */
   } else {
     /* buf[0] contains the read byte */
   }
 
+Note that only a subset of the I2C and SMBus protocols can be achieved by
+the means of read() and write() calls. In particular, so-called combined
+transactions (mixing read and write messages in the same transaction)
+aren't supported. For this reason, this interface is almost never used by
+user-space programs.
+
 IMPORTANT: because of the use of inline functions, you *have* to use
 '-O' or some variation when you compile your program!
 
@@ -80,31 +98,29 @@ IMPORTANT: because of the use of inline functions, you *have* to use
 Full interface description
 ==========================
 
-The following IOCTLs are defined and fully supported 
-(see also i2c-dev.h):
+The following IOCTLs are defined:
 
-ioctl(file,I2C_SLAVE,long addr)
+ioctl(file, I2C_SLAVE, long addr)
   Change slave address. The address is passed in the 7 lower bits of the
   argument (except for 10 bit addresses, passed in the 10 lower bits in this
   case).
 
-ioctl(file,I2C_TENBIT,long select)
+ioctl(file, I2C_TENBIT, long select)
   Selects ten bit addresses if select not equals 0, selects normal 7 bit
   addresses if select equals 0. Default 0.  This request is only valid
   if the adapter has I2C_FUNC_10BIT_ADDR.
 
-ioctl(file,I2C_PEC,long select)
+ioctl(file, I2C_PEC, long select)
   Selects SMBus PEC (packet error checking) generation and verification
   if select not equals 0, disables if select equals 0. Default 0.
   Used only for SMBus transactions.  This request only has an effect if the
   the adapter has I2C_FUNC_SMBUS_PEC; it is still safe if not, it just
   doesn't have any effect.
 
-ioctl(file,I2C_FUNCS,unsigned long *funcs)
+ioctl(file, I2C_FUNCS, unsigned long *funcs)
   Gets the adapter functionality and puts it in *funcs.
 
-ioctl(file,I2C_RDWR,struct i2c_rdwr_ioctl_data *msgset)
-
+ioctl(file, I2C_RDWR, struct i2c_rdwr_ioctl_data *msgset)
   Do combined read/write transaction without stop in between.
   Only valid if the adapter has I2C_FUNC_I2C.  The argument is
   a pointer to a
@@ -120,10 +136,9 @@ ioctl(file,I2C_RDWR,struct i2c_rdwr_ioctl_data *msgset)
   The slave address and whether to use ten bit address mode has to be
   set in each message, overriding the values set with the above ioctl's.
 
-
-Other values are NOT supported at this moment, except for I2C_SMBUS,
-which you should never directly call; instead, use the access functions
-below.
+ioctl(file, I2C_SMBUS, struct i2c_smbus_ioctl_data *args)
+  Not meant to be called  directly; instead, use the access functions
+  below.
 
 You can do plain i2c transactions by using read(2) and write(2) calls.
 You do not need to pass the address byte; instead, set it through
@@ -148,7 +163,52 @@ what happened. The 'write' transactions return 0 on success; the
 returns the number of values read. The block buffers need not be longer
 than 32 bytes.
 
-The above functions are all macros, that resolve to calls to the
-i2c_smbus_access function, that on its turn calls a specific ioctl
+The above functions are all inline functions, that resolve to calls to
+the i2c_smbus_access function, that on its turn calls a specific ioctl
 with the data in a specific format. Read the source code if you
 want to know what happens behind the screens.
+
+
+Implementation details
+======================
+
+For the interested, here's the code flow which happens inside the kernel
+when you use the /dev interface to I2C:
+
+1* Your program opens /dev/i2c-N and calls ioctl() on it, as described in
+section "C example" above.
+
+2* These open() and ioctl() calls are handled by the i2c-dev kernel
+driver: see i2c-dev.c:i2cdev_open() and i2c-dev.c:i2cdev_ioctl(),
+respectively. You can think of i2c-dev as a generic I2C chip driver
+that can be programmed from user-space.
+
+3* Some ioctl() calls are for administrative tasks and are handled by
+i2c-dev directly. Examples include I2C_SLAVE (set the address of the
+device you want to access) and I2C_PEC (enable or disable SMBus error
+checking on future transactions.)
+
+4* Other ioctl() calls are converted to in-kernel function calls by
+i2c-dev. Examples include I2C_FUNCS, which queries the I2C adapter
+functionality using i2c.h:i2c_get_functionality(), and I2C_SMBUS, which
+performs an SMBus transaction using i2c-core.c:i2c_smbus_xfer().
+
+The i2c-dev driver is responsible for checking all the parameters that
+come from user-space for validity. After this point, there is no
+difference between these calls that came from user-space through i2c-dev
+and calls that would have been performed by kernel I2C chip drivers
+directly. This means that I2C bus drivers don't need to implement
+anything special to support access from user-space.
+
+5* These i2c-core.c/i2c.h functions are wrappers to the actual
+implementation of your I2C bus driver. Each adapter must declare
+callback functions implementing these standard calls.
+i2c.h:i2c_get_functionality() calls i2c_adapter.algo->functionality(),
+while i2c-core.c:i2c_smbus_xfer() calls either
+adapter.algo->smbus_xfer() if it is implemented, or if not,
+i2c-core.c:i2c_smbus_xfer_emulated() which in turn calls
+i2c_adapter.algo->master_xfer().
+
+After your I2C bus driver has processed these requests, execution runs
+up the call chain, with almost no processing done, except by i2c-dev to
+package the returned data, if any, in suitable format for the ioctl.
index 24bfb65da17dbe53db1d6f8c98562ff9c85ce5ea..9df47441f0e73063c39f39fe22acc1fb3a753416 100644 (file)
@@ -109,8 +109,8 @@ specified through the Comm byte.
 S Addr Wr [A] Comm [A] DataLow [A] DataHigh [A] P
 
 
-SMBus Process Call
-==================
+SMBus Process Call:  i2c_smbus_process_call()
+=============================================
 
 This command selects a device register (through the Comm byte), sends
 16 bits of data to it, and reads 16 bits of data in return.
index 6b61b3a2e90bee2b7cbed065160bd029db89ba08..d73ee117a8caecfaf0585aeb4239c27828a7b5df 100644 (file)
@@ -606,6 +606,8 @@ SMBus communication
   extern s32 i2c_smbus_read_word_data(struct i2c_client * client, u8 command);
   extern s32 i2c_smbus_write_word_data(struct i2c_client * client,
                                        u8 command, u16 value);
+  extern s32 i2c_smbus_process_call(struct i2c_client *client,
+                                    u8 command, u16 value);
   extern s32 i2c_smbus_read_block_data(struct i2c_client * client,
                                        u8 command, u8 *values);
   extern s32 i2c_smbus_write_block_data(struct i2c_client * client,
@@ -621,8 +623,6 @@ These ones were removed from i2c-core because they had no users, but could
 be added back later if needed:
 
   extern s32 i2c_smbus_write_quick(struct i2c_client * client, u8 value);
-  extern s32 i2c_smbus_process_call(struct i2c_client * client,
-                                    u8 command, u16 value);
   extern s32 i2c_smbus_block_process_call(struct i2c_client *client,
                                           u8 command, u8 length,
                                           u8 *values)
index c9a35665cf7072c52c92027a715e980c9479a3b7..ce3487d99abedef8b3616740bc3a980d3607bdd3 100644 (file)
@@ -2,17 +2,8 @@ Regulator Machine Driver Interface
 ===================================
 
 The regulator machine driver interface is intended for board/machine specific
-initialisation code to configure the regulator subsystem. Typical things that
-machine drivers would do are :-
+initialisation code to configure the regulator subsystem.
 
- 1. Regulator -> Device mapping.
- 2. Regulator supply configuration.
- 3. Power Domain constraint setting.
-
-
-
-1. Regulator -> device mapping
-==============================
 Consider the following machine :-
 
   Regulator-1 -+-> Regulator-2 --> [Consumer A @ 1.8 - 2.0V]
@@ -21,81 +12,82 @@ Consider the following machine :-
 
 The drivers for consumers A & B must be mapped to the correct regulator in
 order to control their power supply. This mapping can be achieved in machine
-initialisation code by calling :-
+initialisation code by creating a struct regulator_consumer_supply for
+each regulator.
+
+struct regulator_consumer_supply {
+       struct device *dev;     /* consumer */
+       const char *supply;     /* consumer supply - e.g. "vcc" */
+};
 
-int regulator_set_device_supply(const char *regulator, struct device *dev,
-                               const char *supply);
+e.g. for the machine above
 
-and is shown with the following code :-
+static struct regulator_consumer_supply regulator1_consumers[] = {
+{
+       .dev    = &platform_consumerB_device.dev,
+       .supply = "Vcc",
+},};
 
-regulator_set_device_supply("Regulator-1", devB, "Vcc");
-regulator_set_device_supply("Regulator-2", devA, "Vcc");
+static struct regulator_consumer_supply regulator2_consumers[] = {
+{
+       .dev    = &platform_consumerA_device.dev,
+       .supply = "Vcc",
+},};
 
 This maps Regulator-1 to the 'Vcc' supply for Consumer B and maps Regulator-2
 to the 'Vcc' supply for Consumer A.
 
-
-2. Regulator supply configuration.
-==================================
-Consider the following machine (again) :-
-
-  Regulator-1 -+-> Regulator-2 --> [Consumer A @ 1.8 - 2.0V]
-               |
-               +-> [Consumer B @ 3.3V]
+Constraints can now be registered by defining a struct regulator_init_data
+for each regulator power domain. This structure also maps the consumers
+to their supply regulator :-
+
+static struct regulator_init_data regulator1_data = {
+       .constraints = {
+               .min_uV = 3300000,
+               .max_uV = 3300000,
+               .valid_modes_mask = REGULATOR_MODE_NORMAL,
+       },
+       .num_consumer_supplies = ARRAY_SIZE(regulator1_consumers),
+       .consumer_supplies = regulator1_consumers,
+};
 
 Regulator-1 supplies power to Regulator-2. This relationship must be registered
 with the core so that Regulator-1 is also enabled when Consumer A enables it's
-supply (Regulator-2).
-
-This relationship can be register with the core via :-
-
-int regulator_set_supply(const char *regulator, const char *regulator_supply);
-
-In this example we would use the following code :-
-
-regulator_set_supply("Regulator-2", "Regulator-1");
-
-Relationships can be queried by calling :-
-
-const char *regulator_get_supply(const char *regulator);
-
-
-3. Power Domain constraint setting.
-===================================
-Each power domain within a system has physical constraints on voltage and
-current. This must be defined in software so that the power domain is always
-operated within specifications.
-
-Consider the following machine (again) :-
-
-  Regulator-1 -+-> Regulator-2 --> [Consumer A @ 1.8 - 2.0V]
-               |
-               +-> [Consumer B @ 3.3V]
-
-This gives us two regulators and two power domains:
-
-                   Domain 1: Regulator-2, Consumer B.
-                   Domain 2: Consumer A.
-
-Constraints can be registered by calling :-
-
-int regulator_set_platform_constraints(const char *regulator,
-       struct regulation_constraints *constraints);
-
-The example is defined as follows :-
-
-struct regulation_constraints domain_1 = {
-       .min_uV = 3300000,
-       .max_uV = 3300000,
-       .valid_modes_mask = REGULATOR_MODE_NORMAL,
+supply (Regulator-2). The supply regulator is set by the supply_regulator_dev
+field below:-
+
+static struct regulator_init_data regulator2_data = {
+       .supply_regulator_dev = &platform_regulator1_device.dev,
+       .constraints = {
+               .min_uV = 1800000,
+               .max_uV = 2000000,
+               .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE,
+               .valid_modes_mask = REGULATOR_MODE_NORMAL,
+       },
+       .num_consumer_supplies = ARRAY_SIZE(regulator2_consumers),
+       .consumer_supplies = regulator2_consumers,
 };
 
-struct regulation_constraints domain_2 = {
-       .min_uV = 1800000,
-       .max_uV = 2000000,
-       .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE,
-       .valid_modes_mask = REGULATOR_MODE_NORMAL,
+Finally the regulator devices must be registered in the usual manner.
+
+static struct platform_device regulator_devices[] = {
+{
+       .name = "regulator",
+       .id = DCDC_1,
+       .dev = {
+               .platform_data = &regulator1_data,
+       },
+},
+{
+       .name = "regulator",
+       .id = DCDC_2,
+       .dev = {
+               .platform_data = &regulator2_data,
+       },
+},
 };
+/* register regulator 1 device */
+platform_device_register(&wm8350_regulator_devices[0]);
 
-regulator_set_platform_constraints("Regulator-1", &domain_1);
-regulator_set_platform_constraints("Regulator-2", &domain_2);
+/* register regulator 2 device */
+platform_device_register(&wm8350_regulator_devices[1]);
index a69050143592236cdcf39655027c30bb8aeccb47..4200accb9bba981b0c582c6089a6a07fcc215fae 100644 (file)
@@ -10,11 +10,11 @@ Registration
 
 Drivers can register a regulator by calling :-
 
-struct regulator_dev *regulator_register(struct regulator_desc *regulator_desc,
-                                         void *reg_data);
+struct regulator_dev *regulator_register(struct device *dev,
+       struct regulator_desc *regulator_desc);
 
-This will register the regulators capabilities and operations the regulator
-core. The core does not touch reg_data (private to regulator driver).
+This will register the regulators capabilities and operations to the regulator
+core.
 
 Regulators can be unregistered by calling :-
 
index 988b0a85289066bc354504df53e34b3e72c7f124..5d0b8a23d63968e8d1c57640024fb2951cb1efcc 100644 (file)
@@ -4520,10 +4520,11 @@ S:      Maintained
 
 VOLTAGE AND CURRENT REGULATOR FRAMEWORK
 P:     Liam Girdwood
-M:     lg@opensource.wolfsonmicro.com
+M:     lrg@slimlogic.co.uk
 P:     Mark Brown
 M:     broonie@opensource.wolfsonmicro.com
 W:     http://opensource.wolfsonmicro.com/node/15
+W:     http://www.slimlogic.co.uk/?page_id=5
 T:     git kernel.org/pub/scm/linux/kernel/git/lrg/voltage-2.6.git
 S:     Supported
 
index 2ced6d9984d27802844a448aa37720f54889d62a..adfcd7b513930cdaa677aa835e7e3d24f9d1171d 100644 (file)
@@ -476,6 +476,10 @@ static struct i2c_board_info __initdata h3_i2c_board_info[] = {
                I2C_BOARD_INFO("tps65013", 0x48),
                /* .irq         = OMAP_GPIO_IRQ(??), */
        },
+       {
+               I2C_BOARD_INFO("isp1301_omap", 0x2d),
+               .irq            = OMAP_GPIO_IRQ(14),
+       },
 };
 
 static struct omap_gpio_switch h3_gpio_switches[] __initdata = {
index d4e3b6fc4705fe9335835911a13828ecc5fbfae7..2fef2c84508390daa193ef4e9c189872c44d91aa 100644 (file)
@@ -18,6 +18,7 @@
 #include <linux/mtd/partitions.h>
 #include <linux/delay.h>
 #include <linux/workqueue.h>
+#include <linux/i2c.h>
 #include <linux/input.h>
 #include <linux/err.h>
 #include <linux/clk.h>
@@ -391,6 +392,13 @@ static struct omap_board_config_kernel h4_config[] = {
        { OMAP_TAG_LCD,         &h4_lcd_config },
 };
 
+static struct i2c_board_info __initdata h4_i2c_board_info[] = {
+       {
+               I2C_BOARD_INFO("isp1301_omap", 0x2d),
+               .irq            = OMAP_GPIO_IRQ(125),
+       },
+};
+
 static void __init omap_h4_init(void)
 {
        /*
@@ -411,6 +419,9 @@ static void __init omap_h4_init(void)
        }
 #endif
 
+       i2c_register_board_info(1, h4_i2c_board_info,
+                       ARRAY_SIZE(h4_i2c_board_info));
+
        platform_add_devices(h4_devices, ARRAY_SIZE(h4_devices));
        omap_board_config = h4_config;
        omap_board_config_size = ARRAY_SIZE(h4_config);
index cbc3c4c54566bbfb3f7fb418968e5798798ec740..7daf897292cf65c2ec12832265f26035cda399af 100644 (file)
@@ -111,9 +111,9 @@ unsigned long __init setup_memory(void)
                                initrd_start, INITRD_SIZE);
                } else {
                        printk("initrd extends beyond end of memory "
-                               "(0x%08lx > 0x%08lx)\ndisabling initrd\n",
+                               "(0x%08lx > 0x%08llx)\ndisabling initrd\n",
                                INITRD_START + INITRD_SIZE,
-                               PFN_PHYS(max_low_pfn));
+                               (unsigned long long)PFN_PHYS(max_low_pfn));
 
                        initrd_start = 0;
                }
index 41e5bf02e230716f860290555066df3d532440cb..677c93a490f6f49501e4c7d2e375fd42bb4dabc5 100644 (file)
@@ -105,21 +105,9 @@ config PCMCIA
          To compile this driver as modules, choose M here: the
          modules will be called pcmcia_core and ds.
 
-config SUN3
-       bool "Sun3 support"
-       select M68020
-       select MMU_SUN3 if MMU
-       help
-         This option enables support for the Sun 3 series of workstations
-         (3/50, 3/60, 3/1xx, 3/2xx systems). Enabling this option requires
-         that all other hardware types must be disabled, as Sun 3 kernels
-         are incompatible with all other m68k targets (including Sun 3x!).
-
-         If you don't want to compile a kernel exclusively for a Sun 3, say N.
-
 config AMIGA
        bool "Amiga support"
-       depends on !MMU_SUN3
+       select MMU_MOTOROLA if MMU
        help
          This option enables support for the Amiga series of computers. If
          you plan to use this kernel on an Amiga, say Y here and browse the
@@ -127,33 +115,16 @@ config AMIGA
 
 config ATARI
        bool "Atari support"
-       depends on !MMU_SUN3
+       select MMU_MOTOROLA if MMU
        help
          This option enables support for the 68000-based Atari series of
          computers (including the TT, Falcon and Medusa). If you plan to use
          this kernel on an Atari, say Y here and browse the material
          available in <file:Documentation/m68k>; otherwise say N.
 
-config HADES
-       bool "Hades support"
-       depends on ATARI && BROKEN
-       help
-         This option enables support for the Hades Atari clone. If you plan
-         to use this kernel on a Hades, say Y here; otherwise say N.
-
-config PCI
-       bool
-       depends on HADES
-       default y
-       help
-         Find out whether you have a PCI motherboard. PCI is the name of a
-         bus system, i.e. the way the CPU talks to the other stuff inside
-         your box. Other bus systems are ISA, EISA, MicroChannel (MCA) or
-         VESA. If you have PCI, say Y, otherwise N.
-
 config MAC
        bool "Macintosh support"
-       depends on !MMU_SUN3
+       select MMU_MOTOROLA if MMU
        help
          This option enables support for the Apple Macintosh series of
          computers (yes, there is experimental support now, at least for part
@@ -174,14 +145,14 @@ config M68K_L2_CACHE
 
 config APOLLO
        bool "Apollo support"
-       depends on !MMU_SUN3
+       select MMU_MOTOROLA if MMU
        help
          Say Y here if you want to run Linux on an MC680x0-based Apollo
          Domain workstation such as the DN3500.
 
 config VME
        bool "VME (Motorola and BVM) support"
-       depends on !MMU_SUN3
+       select MMU_MOTOROLA if MMU
        help
          Say Y here if you want to build a kernel for a 680x0 based VME
          board.  Boards currently supported include Motorola boards MVME147,
@@ -218,7 +189,7 @@ config BVME6000
 
 config HP300
        bool "HP9000/300 and HP9000/400 support"
-       depends on !MMU_SUN3
+       select MMU_MOTOROLA if MMU
        help
          This option enables support for the HP9000/300 and HP9000/400 series
          of workstations. Support for these machines is still somewhat
@@ -237,7 +208,7 @@ config DIO
 
 config SUN3X
        bool "Sun3x support"
-       depends on !MMU_SUN3
+       select MMU_MOTOROLA if MMU
        select M68030
        help
          This option enables support for the Sun 3x series of workstations.
@@ -250,7 +221,7 @@ config SUN3X
 
 config Q40
        bool "Q40/Q60 support"
-       depends on !MMU_SUN3
+       select MMU_MOTOROLA if MMU
        help
          The Q40 is a Motorola 68040-based successor to the Sinclair QL
          manufactured in Germany.  There is an official Q40 home page at
@@ -258,6 +229,19 @@ config Q40
          Q60. Select your CPU below.  For 68LC060 don't forget to enable FPU
          emulation.
 
+config SUN3
+       bool "Sun3 support"
+       depends on !MMU_MOTOROLA
+       select MMU_SUN3 if MMU
+       select M68020
+       help
+         This option enables support for the Sun 3 series of workstations
+         (3/50, 3/60, 3/1xx, 3/2xx systems). Enabling this option requires
+         that all other hardware types must be disabled, as Sun 3 kernels
+         are incompatible with all other m68k targets (including Sun 3x!).
+
+         If you don't want to compile a kernel exclusively for a Sun 3, say N.
+
 comment "Processor type"
 
 config M68020
@@ -295,10 +279,10 @@ config M68060
 config MMU_MOTOROLA
        bool
        depends on MMU && !MMU_SUN3
-       default y
 
 config MMU_SUN3
        bool
+       depends on MMU && !MMU_MOTOROLA
 
 config M68KFPU_EMU
        bool "Math emulation support (EXPERIMENTAL)"
index df679d96b1cb790de2b64478cd46306d407f9451..0a3f9e8ebde0db468365abf2a429a11a04c1e934 100644 (file)
@@ -24,6 +24,7 @@
 #include <linux/interrupt.h>
 #include <linux/zorro.h>
 #include <linux/module.h>
+#include <linux/keyboard.h>
 
 #include <asm/bootinfo.h>
 #include <asm/setup.h>
@@ -984,3 +985,11 @@ static int amiga_get_hardware_list(char *buffer)
 
        return len;
 }
+
+/*
+ * The Amiga keyboard driver needs key_maps, but we cannot export it in
+ * drivers/char/defkeymap.c, as it is autogenerated
+ */
+#ifdef CONFIG_HW_CONSOLE
+EXPORT_SYMBOL_GPL(key_maps);
+#endif
index 2cd905efe63abb15572d653fd6ee71b485c952b3..0cac723306f936befae82e5e21dc673a36507c5f 100644 (file)
@@ -5,7 +5,4 @@
 obj-y          := config.o time.o debug.o ataints.o stdma.o \
                        atasound.o stram.o
 
-ifeq ($(CONFIG_PCI),y)
-obj-$(CONFIG_HADES)    += hades-pci.o
-endif
 obj-$(CONFIG_ATARI_KBD_CORE)   += atakeyb.o
index b45593a60bddf67bfdb57e611ded063f3b386993..dba4afabb444a41bb7c294475825726e607f1881 100644 (file)
@@ -407,10 +407,8 @@ void __init atari_init_IRQ(void)
                 * gets overruns)
                 */
 
-               if (!MACH_IS_HADES) {
-                       vectors[VEC_INT2] = falcon_hblhandler;
-                       vectors[VEC_INT4] = falcon_hblhandler;
-               }
+               vectors[VEC_INT2] = falcon_hblhandler;
+               vectors[VEC_INT4] = falcon_hblhandler;
        }
 
        if (ATARIHW_PRESENT(PCM_8BIT) && ATARIHW_PRESENT(MICROWIRE)) {
index bb959fbab2dc2b74805052b409142ffd2c43b965..c038b7c7eff0e8ea20f009b83eb4b1658040a229 100644 (file)
@@ -635,15 +635,3 @@ int atari_keyb_init(void)
        return 0;
 }
 EXPORT_SYMBOL_GPL(atari_keyb_init);
-
-int atari_kbd_translate(unsigned char keycode, unsigned char *keycodep, char raw_mode)
-{
-#ifdef CONFIG_MAGIC_SYSRQ
-       /* ALT+HELP pressed? */
-       if ((keycode == 98) && ((shift_state & 0xff) == 8))
-               *keycodep = 0xff;
-       else
-#endif
-               *keycodep = keycode;
-       return 1;
-}
index 5945e15055582821ee0b1adcab4d640c8d3b1b56..af031855f796f4b61bd716f092d5065c97d1bc12 100644 (file)
@@ -231,7 +231,7 @@ void __init config_atari(void)
         */
 
        printk("Atari hardware found: ");
-       if (MACH_IS_MEDUSA || MACH_IS_HADES) {
+       if (MACH_IS_MEDUSA) {
                /* There's no Atari video hardware on the Medusa, but all the
                 * addresses below generate a DTACK so no bus error occurs! */
        } else if (hwreg_present(f030_xreg)) {
@@ -269,10 +269,6 @@ void __init config_atari(void)
                ATARIHW_SET(SCSI_DMA);
                printk("TT_SCSI_DMA ");
        }
-       if (!MACH_IS_HADES && hwreg_present(&st_dma.dma_hi)) {
-               ATARIHW_SET(STND_DMA);
-               printk("STND_DMA ");
-       }
        /*
         * The ST-DMA address registers aren't readable
         * on all Medusas, so the test below may fail
@@ -294,12 +290,11 @@ void __init config_atari(void)
                ATARIHW_SET(YM_2149);
                printk("YM2149 ");
        }
-       if (!MACH_IS_MEDUSA && !MACH_IS_HADES &&
-               hwreg_present(&tt_dmasnd.ctrl)) {
+       if (!MACH_IS_MEDUSA && hwreg_present(&tt_dmasnd.ctrl)) {
                ATARIHW_SET(PCM_8BIT);
                printk("PCM ");
        }
-       if (!MACH_IS_HADES && hwreg_present(&falcon_codec.unused5)) {
+       if (hwreg_present(&falcon_codec.unused5)) {
                ATARIHW_SET(CODEC);
                printk("CODEC ");
        }
@@ -313,7 +308,7 @@ void __init config_atari(void)
            (tt_scc_dma.dma_ctrl = 0x01, (tt_scc_dma.dma_ctrl & 1) == 1) &&
            (tt_scc_dma.dma_ctrl = 0x00, (tt_scc_dma.dma_ctrl & 1) == 0)
 #else
-           !MACH_IS_MEDUSA && !MACH_IS_HADES
+           !MACH_IS_MEDUSA
 #endif
            ) {
                ATARIHW_SET(SCC_DMA);
@@ -327,10 +322,7 @@ void __init config_atari(void)
                ATARIHW_SET(ST_ESCC);
                printk("ST_ESCC ");
        }
-       if (MACH_IS_HADES) {
-               ATARIHW_SET(VME);
-               printk("VME ");
-       } else if (hwreg_present(&tt_scu.sys_mask)) {
+       if (hwreg_present(&tt_scu.sys_mask)) {
                ATARIHW_SET(SCU);
                /* Assume a VME bus if there's a SCU */
                ATARIHW_SET(VME);
@@ -340,7 +332,7 @@ void __init config_atari(void)
                ATARIHW_SET(ANALOG_JOY);
                printk("ANALOG_JOY ");
        }
-       if (!MACH_IS_HADES && hwreg_present(blitter.halftone)) {
+       if (hwreg_present(blitter.halftone)) {
                ATARIHW_SET(BLITTER);
                printk("BLITTER ");
        }
@@ -349,8 +341,7 @@ void __init config_atari(void)
                printk("IDE ");
        }
 #if 1 /* This maybe wrong */
-       if (!MACH_IS_MEDUSA && !MACH_IS_HADES &&
-           hwreg_present(&tt_microwire.data) &&
+       if (!MACH_IS_MEDUSA && hwreg_present(&tt_microwire.data) &&
            hwreg_present(&tt_microwire.mask) &&
            (tt_microwire.mask = 0x7ff,
             udelay(1),
@@ -369,19 +360,18 @@ void __init config_atari(void)
                mach_hwclk = atari_tt_hwclk;
                mach_set_clock_mmss = atari_tt_set_clock_mmss;
        }
-       if (!MACH_IS_HADES && hwreg_present(&mste_rtc.sec_ones)) {
+       if (hwreg_present(&mste_rtc.sec_ones)) {
                ATARIHW_SET(MSTE_CLK);
                printk("MSTE_CLK ");
                mach_hwclk = atari_mste_hwclk;
                mach_set_clock_mmss = atari_mste_set_clock_mmss;
        }
-       if (!MACH_IS_MEDUSA && !MACH_IS_HADES &&
-           hwreg_present(&dma_wd.fdc_speed) &&
+       if (!MACH_IS_MEDUSA && hwreg_present(&dma_wd.fdc_speed) &&
            hwreg_write(&dma_wd.fdc_speed, 0)) {
                ATARIHW_SET(FDCSPEED);
                printk("FDC_SPEED ");
        }
-       if (!MACH_IS_HADES && !ATARIHW_PRESENT(ST_SCSI)) {
+       if (!ATARIHW_PRESENT(ST_SCSI)) {
                ATARIHW_SET(ACSI);
                printk("ACSI ");
        }
@@ -449,7 +439,7 @@ void __init config_atari(void)
         * 0xFFxxxxxx -> 0x00xxxxxx, so that the first 16MB is accessible
         * in the last 16MB of the address space.
         */
-       tos_version = (MACH_IS_MEDUSA || MACH_IS_HADES) ?
+       tos_version = (MACH_IS_MEDUSA) ?
                        0xfff : *(unsigned short *)0xff000002;
        atari_rtc_year_offset = (tos_version < 0x306) ? 70 : 68;
 }
@@ -511,8 +501,7 @@ static void atari_reset(void)
         * On the Medusa, phys. 0x4 may contain garbage because it's no
         * ROM.  See above for explanation why we cannot use PTOV(4).
         */
-       reset_addr = MACH_IS_HADES ? 0x7fe00030 :
-                    MACH_IS_MEDUSA || MACH_IS_AB40 ? 0xe00030 :
+       reset_addr = MACH_IS_MEDUSA || MACH_IS_AB40 ? 0xe00030 :
                     *(unsigned long *) 0xff000004;
 
        /* reset ACIA for switch off OverScan, if it's active */
@@ -606,8 +595,6 @@ static void atari_get_model(char *model)
                if (MACH_IS_MEDUSA)
                        /* Medusa has TT _MCH cookie */
                        strcat(model, "Medusa");
-               else if (MACH_IS_HADES)
-                       strcat(model, "Hades");
                else
                        strcat(model, "TT");
                break;
diff --git a/arch/m68k/atari/hades-pci.c b/arch/m68k/atari/hades-pci.c
deleted file mode 100644 (file)
index 2bbabc0..0000000
+++ /dev/null
@@ -1,440 +0,0 @@
-/*
- * hades-pci.c - Hardware specific PCI BIOS functions the Hades Atari clone.
- *
- * Written by Wout Klaren.
- */
-
-#include <linux/init.h>
-#include <linux/kernel.h>
-#include <asm/io.h>
-
-#if 0
-# define DBG_DEVS(args)                printk args
-#else
-# define DBG_DEVS(args)
-#endif
-
-#if defined(CONFIG_PCI) && defined(CONFIG_HADES)
-
-#include <linux/slab.h>
-#include <linux/mm.h>
-#include <linux/pci.h>
-
-#include <asm/atarihw.h>
-#include <asm/atariints.h>
-#include <asm/byteorder.h>
-#include <asm/pci.h>
-
-#define HADES_MEM_BASE         0x80000000
-#define HADES_MEM_SIZE         0x20000000
-#define HADES_CONFIG_BASE      0xA0000000
-#define HADES_CONFIG_SIZE      0x10000000
-#define HADES_IO_BASE          0xB0000000
-#define HADES_IO_SIZE          0x10000000
-#define HADES_VIRT_IO_SIZE     0x00010000      /* Only 64k is remapped and actually used. */
-
-#define N_SLOTS                                4                       /* Number of PCI slots. */
-
-static const char pci_mem_name[] = "PCI memory space";
-static const char pci_io_name[] = "PCI I/O space";
-static const char pci_config_name[] = "PCI config space";
-
-static struct resource config_space = {
-    .name = pci_config_name,
-    .start = HADES_CONFIG_BASE,
-    .end = HADES_CONFIG_BASE + HADES_CONFIG_SIZE - 1
-};
-static struct resource io_space = {
-    .name = pci_io_name,
-    .start = HADES_IO_BASE,
-    .end = HADES_IO_BASE + HADES_IO_SIZE - 1
-};
-
-static const unsigned long pci_conf_base_phys[] = {
-    0xA0080000, 0xA0040000, 0xA0020000, 0xA0010000
-};
-static unsigned long pci_conf_base_virt[N_SLOTS];
-static unsigned long pci_io_base_virt;
-
-/*
- * static void *mk_conf_addr(unsigned char bus, unsigned char device_fn,
- *                          unsigned char where)
- *
- * Calculate the address of the PCI configuration area of the given
- * device.
- *
- * BUG: boards with multiple functions are probably not correctly
- * supported.
- */
-
-static void *mk_conf_addr(struct pci_dev *dev, int where)
-{
-       int device = dev->devfn >> 3, function = dev->devfn & 7;
-       void *result;
-
-       DBG_DEVS(("mk_conf_addr(bus=%d ,device_fn=0x%x, where=0x%x, pci_addr=0x%p)\n",
-                 dev->bus->number, dev->devfn, where, pci_addr));
-
-       if (device > 3)
-       {
-               DBG_DEVS(("mk_conf_addr: device (%d) > 3, returning NULL\n", device));
-               return NULL;
-       }
-
-       if (dev->bus->number != 0)
-       {
-               DBG_DEVS(("mk_conf_addr: bus (%d) > 0, returning NULL\n", device));
-               return NULL;
-       }
-
-       result = (void *) (pci_conf_base_virt[device] | (function << 8) | (where));
-       DBG_DEVS(("mk_conf_addr: returning pci_addr 0x%lx\n", (unsigned long) result));
-       return result;
-}
-
-static int hades_read_config_byte(struct pci_dev *dev, int where, u8 *value)
-{
-       volatile unsigned char *pci_addr;
-
-       *value = 0xff;
-
-       if ((pci_addr = (unsigned char *) mk_conf_addr(dev, where)) == NULL)
-               return PCIBIOS_DEVICE_NOT_FOUND;
-
-       *value = *pci_addr;
-
-       return PCIBIOS_SUCCESSFUL;
-}
-
-static int hades_read_config_word(struct pci_dev *dev, int where, u16 *value)
-{
-       volatile unsigned short *pci_addr;
-
-       *value = 0xffff;
-
-       if (where & 0x1)
-               return PCIBIOS_BAD_REGISTER_NUMBER;
-
-       if ((pci_addr = (unsigned short *) mk_conf_addr(dev, where)) == NULL)
-               return PCIBIOS_DEVICE_NOT_FOUND;
-
-       *value = le16_to_cpu(*pci_addr);
-
-       return PCIBIOS_SUCCESSFUL;
-}
-
-static int hades_read_config_dword(struct pci_dev *dev, int where, u32 *value)
-{
-       volatile unsigned int *pci_addr;
-       unsigned char header_type;
-       int result;
-
-       *value = 0xffffffff;
-
-       if (where & 0x3)
-               return PCIBIOS_BAD_REGISTER_NUMBER;
-
-       if ((pci_addr = (unsigned int *) mk_conf_addr(dev, where)) == NULL)
-               return PCIBIOS_DEVICE_NOT_FOUND;
-
-       *value = le32_to_cpu(*pci_addr);
-
-       /*
-        * Check if the value is an address on the bus. If true, add the
-        * base address of the PCI memory or PCI I/O area on the Hades.
-        */
-
-       if ((result = hades_read_config_byte(dev, PCI_HEADER_TYPE,
-                                            &header_type)) != PCIBIOS_SUCCESSFUL)
-               return result;
-
-       if (((where >= PCI_BASE_ADDRESS_0) && (where <= PCI_BASE_ADDRESS_1)) ||
-           ((header_type != PCI_HEADER_TYPE_BRIDGE) && ((where >= PCI_BASE_ADDRESS_2) &&
-                                                        (where <= PCI_BASE_ADDRESS_5))))
-       {
-               if ((*value & PCI_BASE_ADDRESS_SPACE) == PCI_BASE_ADDRESS_SPACE_IO)
-               {
-                       /*
-                        * Base address register that contains an I/O address. If the
-                        * address is valid on the Hades (0 <= *value < HADES_VIRT_IO_SIZE),
-                        * add 'pci_io_base_virt' to the value.
-                        */
-
-                       if (*value < HADES_VIRT_IO_SIZE)
-                               *value += pci_io_base_virt;
-               }
-               else
-               {
-                       /*
-                        * Base address register that contains an memory address. If the
-                        * address is valid on the Hades (0 <= *value < HADES_MEM_SIZE),
-                        * add HADES_MEM_BASE to the value.
-                        */
-
-                       if (*value == 0)
-                       {
-                               /*
-                                * Base address is 0. Test if this base
-                                * address register is used.
-                                */
-
-                               *pci_addr = 0xffffffff;
-                               if (*pci_addr != 0)
-                               {
-                                       *pci_addr = *value;
-                                       if (*value < HADES_MEM_SIZE)
-                                               *value += HADES_MEM_BASE;
-                               }
-                       }
-                       else
-                       {
-                               if (*value < HADES_MEM_SIZE)
-                                       *value += HADES_MEM_BASE;
-                       }
-               }
-       }
-
-       return PCIBIOS_SUCCESSFUL;
-}
-
-static int hades_write_config_byte(struct pci_dev *dev, int where, u8 value)
-{
-       volatile unsigned char *pci_addr;
-
-       if ((pci_addr = (unsigned char *) mk_conf_addr(dev, where)) == NULL)
-               return PCIBIOS_DEVICE_NOT_FOUND;
-
-       *pci_addr = value;
-
-       return PCIBIOS_SUCCESSFUL;
-}
-
-static int hades_write_config_word(struct pci_dev *dev, int where, u16 value)
-{
-       volatile unsigned short *pci_addr;
-
-       if ((pci_addr = (unsigned short *) mk_conf_addr(dev, where)) == NULL)
-               return PCIBIOS_DEVICE_NOT_FOUND;
-
-       *pci_addr = cpu_to_le16(value);
-
-       return PCIBIOS_SUCCESSFUL;
-}
-
-static int hades_write_config_dword(struct pci_dev *dev, int where, u32 value)
-{
-       volatile unsigned int *pci_addr;
-       unsigned char header_type;
-       int result;
-
-       if ((pci_addr = (unsigned int *) mk_conf_addr(dev, where)) == NULL)
-               return PCIBIOS_DEVICE_NOT_FOUND;
-
-       /*
-        * Check if the value is an address on the bus. If true, subtract the
-        * base address of the PCI memory or PCI I/O area on the Hades.
-        */
-
-       if ((result = hades_read_config_byte(dev, PCI_HEADER_TYPE,
-                                            &header_type)) != PCIBIOS_SUCCESSFUL)
-               return result;
-
-       if (((where >= PCI_BASE_ADDRESS_0) && (where <= PCI_BASE_ADDRESS_1)) ||
-           ((header_type != PCI_HEADER_TYPE_BRIDGE) && ((where >= PCI_BASE_ADDRESS_2) &&
-                                                        (where <= PCI_BASE_ADDRESS_5))))
-       {
-               if ((value & PCI_BASE_ADDRESS_SPACE) ==
-                   PCI_BASE_ADDRESS_SPACE_IO)
-               {
-                       /*
-                        * I/O address. Check if the address is valid address on
-                        * the Hades (pci_io_base_virt <= value < pci_io_base_virt +
-                        * HADES_VIRT_IO_SIZE) or if the value is 0xffffffff. If not
-                        * true do not write the base address register. If it is a
-                        * valid base address subtract 'pci_io_base_virt' from the value.
-                        */
-
-                       if ((value >= pci_io_base_virt) && (value < (pci_io_base_virt +
-                                                                                                                HADES_VIRT_IO_SIZE)))
-                               value -= pci_io_base_virt;
-                       else
-                       {
-                               if (value != 0xffffffff)
-                                       return PCIBIOS_SET_FAILED;
-                       }
-               }
-               else
-               {
-                       /*
-                        * Memory address. Check if the address is valid address on
-                        * the Hades (HADES_MEM_BASE <= value < HADES_MEM_BASE + HADES_MEM_SIZE) or
-                        * if the value is 0xffffffff. If not true do not write
-                        * the base address register. If it is a valid base address
-                        * subtract HADES_MEM_BASE from the value.
-                        */
-
-                       if ((value >= HADES_MEM_BASE) && (value < (HADES_MEM_BASE + HADES_MEM_SIZE)))
-                               value -= HADES_MEM_BASE;
-                       else
-                       {
-                               if (value != 0xffffffff)
-                                       return PCIBIOS_SET_FAILED;
-                       }
-               }
-       }
-
-       *pci_addr = cpu_to_le32(value);
-
-       return PCIBIOS_SUCCESSFUL;
-}
-
-/*
- * static inline void hades_fixup(void)
- *
- * Assign IRQ numbers as used by Linux to the interrupt pins
- * of the PCI cards.
- */
-
-static void __init hades_fixup(int pci_modify)
-{
-       char irq_tab[4] = {
-               [0] = IRQ_TT_MFP_IO0,           /* Slot 0. */
-               [1] = IRQ_TT_MFP_IO1,           /* Slot 1. */
-               [2] = IRQ_TT_MFP_SCC,           /* Slot 2. */
-               [3] = IRQ_TT_MFP_SCSIDMA        /* Slot 3. */
-       };
-       struct pci_dev *dev = NULL;
-       unsigned char slot;
-
-       /*
-        * Go through all devices, fixing up irqs as we see fit:
-        */
-
-       while ((dev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, dev)) != NULL)
-       {
-               if (dev->class >> 16 != PCI_BASE_CLASS_BRIDGE)
-               {
-                       slot = PCI_SLOT(dev->devfn);    /* Determine slot number. */
-                       dev->irq = irq_tab[slot];
-                       if (pci_modify)
-                               pci_write_config_byte(dev, PCI_INTERRUPT_LINE, dev->irq);
-               }
-       }
-}
-
-/*
- * static void hades_conf_device(struct pci_dev *dev)
- *
- * Machine dependent Configure the given device.
- *
- * Parameters:
- *
- * dev         - the pci device.
- */
-
-static void __init hades_conf_device(struct pci_dev *dev)
-{
-       pci_write_config_byte(dev, PCI_CACHE_LINE_SIZE, 0);
-}
-
-static struct pci_ops hades_pci_ops = {
-       .read_byte =    hades_read_config_byte,
-       .read_word =    hades_read_config_word,
-       .read_dword =   hades_read_config_dword,
-       .write_byte =   hades_write_config_byte,
-       .write_word =   hades_write_config_word,
-       .write_dword =  hades_write_config_dword
-};
-
-/*
- * struct pci_bus_info *init_hades_pci(void)
- *
- * Machine specific initialisation:
- *
- * - Allocate and initialise a 'pci_bus_info' structure
- * - Initialise hardware
- *
- * Result: pointer to 'pci_bus_info' structure.
- */
-
-struct pci_bus_info * __init init_hades_pci(void)
-{
-       struct pci_bus_info *bus;
-       int i;
-
-       /*
-        * Remap I/O and configuration space.
-        */
-
-       pci_io_base_virt = (unsigned long) ioremap(HADES_IO_BASE, HADES_VIRT_IO_SIZE);
-
-       for (i = 0; i < N_SLOTS; i++)
-               pci_conf_base_virt[i] = (unsigned long) ioremap(pci_conf_base_phys[i], 0x10000);
-
-       /*
-        * Allocate memory for bus info structure.
-        */
-
-       bus = kzalloc(sizeof(struct pci_bus_info), GFP_KERNEL);
-       if (unlikely(!bus))
-               goto iounmap_base_virt;
-
-       /*
-        * Claim resources. The m68k has no separate I/O space, both
-        * PCI memory space and PCI I/O space are in memory space. Therefore
-        * the I/O resources are requested in memory space as well.
-        */
-
-       if (unlikely(request_resource(&iomem_resource, &config_space) != 0))
-               goto free_bus;
-
-       if (unlikely(request_resource(&iomem_resource, &io_space) != 0))
-               goto release_config_space;
-
-       bus->mem_space.start = HADES_MEM_BASE;
-       bus->mem_space.end = HADES_MEM_BASE + HADES_MEM_SIZE - 1;
-       bus->mem_space.name = pci_mem_name;
-#if 1
-       if (unlikely(request_resource(&iomem_resource, &bus->mem_space) != 0))
-               goto release_io_space;
-#endif
-       bus->io_space.start = pci_io_base_virt;
-       bus->io_space.end = pci_io_base_virt + HADES_VIRT_IO_SIZE - 1;
-       bus->io_space.name = pci_io_name;
-#if 1
-       if (unlikely(request_resource(&ioport_resource, &bus->io_space) != 0))
-               goto release_bus_mem_space;
-#endif
-       /*
-        * Set hardware dependent functions.
-        */
-
-       bus->m68k_pci_ops = &hades_pci_ops;
-       bus->fixup = hades_fixup;
-       bus->conf_device = hades_conf_device;
-
-       /*
-        * Select high to low edge for PCI interrupts.
-        */
-
-       tt_mfp.active_edge &= ~0x27;
-
-       return bus;
-
-release_bus_mem_space:
-       release_resource(&bus->mem_space);
-release_io_space:
-       release_resource(&io_space);
-release_config_space:
-       release_resource(&config_space);
-free_bus:
-       kfree(bus);
-iounmap_base_virt:
-       iounmap((void *)pci_io_base_virt);
-
-       for (i = 0; i < N_SLOTS; i++)
-               iounmap((void *)pci_conf_base_virt[i]);
-
-       return NULL;
-}
-#endif
index e0d3c8bfb40864791b6c333e3926be8212ba7bc5..1edde27fa32dbb600dd5d07822cfd0bb5281b992 100644 (file)
@@ -20,6 +20,9 @@
 
 #include <asm/atariints.h>
 
+DEFINE_SPINLOCK(rtc_lock);
+EXPORT_SYMBOL_GPL(rtc_lock);
+
 void __init
 atari_sched_init(irq_handler_t timer_routine)
 {
@@ -191,13 +194,14 @@ int atari_tt_hwclk( int op, struct rtc_time *t )
         }
 
         if (!(ctrl & RTC_DM_BINARY)) {
-            BIN_TO_BCD(sec);
-            BIN_TO_BCD(min);
-            BIN_TO_BCD(hour);
-            BIN_TO_BCD(day);
-            BIN_TO_BCD(mon);
-            BIN_TO_BCD(year);
-            if (wday >= 0) BIN_TO_BCD(wday);
+           sec = bin2bcd(sec);
+           min = bin2bcd(min);
+           hour = bin2bcd(hour);
+           day = bin2bcd(day);
+           mon = bin2bcd(mon);
+           year = bin2bcd(year);
+           if (wday >= 0)
+               wday = bin2bcd(wday);
         }
     }
 
@@ -252,13 +256,13 @@ int atari_tt_hwclk( int op, struct rtc_time *t )
        }
 
        if (!(ctrl & RTC_DM_BINARY)) {
-            BCD_TO_BIN(sec);
-            BCD_TO_BIN(min);
-            BCD_TO_BIN(hour);
-            BCD_TO_BIN(day);
-            BCD_TO_BIN(mon);
-            BCD_TO_BIN(year);
-            BCD_TO_BIN(wday);
+           sec = bcd2bin(sec);
+           min = bcd2bin(min);
+           hour = bcd2bin(hour);
+           day = bcd2bin(day);
+           mon = bcd2bin(mon);
+           year = bcd2bin(year);
+           wday = bcd2bin(wday);
         }
 
         if (!(ctrl & RTC_24H)) {
@@ -318,7 +322,7 @@ int atari_tt_set_clock_mmss (unsigned long nowtime)
 
     rtc_minutes = RTC_READ (RTC_MINUTES);
     if (!(save_control & RTC_DM_BINARY))
-        BCD_TO_BIN (rtc_minutes);
+       rtc_minutes = bcd2bin(rtc_minutes);
 
     /* Since we're only adjusting minutes and seconds, don't interfere
        with hour overflow.  This avoids messing with unknown time zones
@@ -329,8 +333,8 @@ int atari_tt_set_clock_mmss (unsigned long nowtime)
         {
             if (!(save_control & RTC_DM_BINARY))
                 {
-                    BIN_TO_BCD (real_seconds);
-                    BIN_TO_BCD (real_minutes);
+                   real_seconds = bin2bcd(real_seconds);
+                   real_minutes = bin2bcd(real_minutes);
                 }
             RTC_WRITE (RTC_SECONDS, real_seconds);
             RTC_WRITE (RTC_MINUTES, real_minutes);
index 9433a88a33c4093866e6ea603182f0975e6b0e54..65c9204ab9ac626aaec3066967bdce9b1792ea60 100644 (file)
@@ -25,6 +25,7 @@
 #include <linux/genhd.h>
 #include <linux/rtc.h>
 #include <linux/interrupt.h>
+#include <linux/bcd.h>
 
 #include <asm/bootinfo.h>
 #include <asm/system.h>
@@ -46,9 +47,6 @@ extern void bvme6000_reset (void);
 extern void bvme6000_waitbut(void);
 void bvme6000_set_vectors (void);
 
-static unsigned char bcd2bin (unsigned char b);
-static unsigned char bin2bcd (unsigned char b);
-
 /* Save tick handler routine pointer, will point to do_timer() in
  * kernel/sched.c, called via bvme6000_process_int() */
 
@@ -264,17 +262,6 @@ unsigned long bvme6000_gettimeoffset (void)
     return v;
 }
 
-static unsigned char bcd2bin (unsigned char b)
-{
-       return ((b>>4)*10 + (b&15));
-}
-
-static unsigned char bin2bcd (unsigned char b)
-{
-       return (((b/10)*16) + (b%10));
-}
-
-
 /*
  * Looks like op is non-zero for setting the clock, and zero for
  * reading the clock.
index e8ac3f7d72df6dd457aded95444e69655d16f7c1..808c9018b115022dd2ba8dee88a070fc003448ce 100644 (file)
@@ -57,16 +57,16 @@ static int rtc_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
                rtc->msr = 0x40;
                memset(&wtime, 0, sizeof(struct rtc_time));
                do {
-                       wtime.tm_sec =  BCD2BIN(rtc->bcd_sec);
-                       wtime.tm_min =  BCD2BIN(rtc->bcd_min);
-                       wtime.tm_hour = BCD2BIN(rtc->bcd_hr);
-                       wtime.tm_mday =  BCD2BIN(rtc->bcd_dom);
-                       wtime.tm_mon =  BCD2BIN(rtc->bcd_mth)-1;
-                       wtime.tm_year = BCD2BIN(rtc->bcd_year);
+                       wtime.tm_sec =  bcd2bin(rtc->bcd_sec);
+                       wtime.tm_min =  bcd2bin(rtc->bcd_min);
+                       wtime.tm_hour = bcd2bin(rtc->bcd_hr);
+                       wtime.tm_mday =  bcd2bin(rtc->bcd_dom);
+                       wtime.tm_mon =  bcd2bin(rtc->bcd_mth)-1;
+                       wtime.tm_year = bcd2bin(rtc->bcd_year);
                        if (wtime.tm_year < 70)
                                wtime.tm_year += 100;
-                       wtime.tm_wday = BCD2BIN(rtc->bcd_dow)-1;
-               } while (wtime.tm_sec != BCD2BIN(rtc->bcd_sec));
+                       wtime.tm_wday = bcd2bin(rtc->bcd_dow)-1;
+               } while (wtime.tm_sec != bcd2bin(rtc->bcd_sec));
                rtc->msr = msr;
                local_irq_restore(flags);
                return copy_to_user(argp, &wtime, sizeof wtime) ?
@@ -114,14 +114,14 @@ static int rtc_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
 
                rtc->t0cr_rtmr = yrs%4;
                rtc->bcd_tenms = 0;
-               rtc->bcd_sec   = BIN2BCD(sec);
-               rtc->bcd_min   = BIN2BCD(min);
-               rtc->bcd_hr    = BIN2BCD(hrs);
-               rtc->bcd_dom   = BIN2BCD(day);
-               rtc->bcd_mth   = BIN2BCD(mon);
-               rtc->bcd_year  = BIN2BCD(yrs%100);
+               rtc->bcd_sec   = bin2bcd(sec);
+               rtc->bcd_min   = bin2bcd(min);
+               rtc->bcd_hr    = bin2bcd(hrs);
+               rtc->bcd_dom   = bin2bcd(day);
+               rtc->bcd_mth   = bin2bcd(mon);
+               rtc->bcd_year  = bin2bcd(yrs%100);
                if (rtc_tm.tm_wday >= 0)
-                       rtc->bcd_dow = BIN2BCD(rtc_tm.tm_wday+1);
+                       rtc->bcd_dow = bin2bcd(rtc_tm.tm_wday+1);
                rtc->t0cr_rtmr = yrs%4 | 0x08;
 
                rtc->msr = msr;
index 3a7f622255045e958a1d4f733978b06eb89f1950..55d5d6b680a211376390499ee2aee7d2e5d41b61 100644 (file)
@@ -14,5 +14,4 @@ obj-y := entry.o process.o traps.o ints.o signal.o ptrace.o module.o \
 
 devres-y = ../../../kernel/irq/devres.o
 
-obj-$(CONFIG_PCI)      += bios32.o
 obj-y$(CONFIG_MMU_SUN3) += dma.o       # no, it's not a typo
diff --git a/arch/m68k/kernel/bios32.c b/arch/m68k/kernel/bios32.c
deleted file mode 100644 (file)
index af170c2..0000000
+++ /dev/null
@@ -1,514 +0,0 @@
-/*
- * bios32.c - PCI BIOS functions for m68k systems.
- *
- * Written by Wout Klaren.
- *
- * Based on the DEC Alpha bios32.c by Dave Rusling and David Mosberger.
- */
-
-#include <linux/init.h>
-#include <linux/kernel.h>
-
-#if 0
-# define DBG_DEVS(args)                printk args
-#else
-# define DBG_DEVS(args)
-#endif
-
-#ifdef CONFIG_PCI
-
-/*
- * PCI support for Linux/m68k. Currently only the Hades is supported.
- *
- * The support for PCI bridges in the DEC Alpha version has
- * been removed in this version.
- */
-
-#include <linux/pci.h>
-#include <linux/slab.h>
-#include <linux/mm.h>
-
-#include <asm/io.h>
-#include <asm/pci.h>
-#include <asm/uaccess.h>
-
-#define KB             1024
-#define MB             (1024*KB)
-#define GB             (1024*MB)
-
-#define MAJOR_REV      0
-#define MINOR_REV      5
-
-/*
- * Align VAL to ALIGN, which must be a power of two.
- */
-
-#define ALIGN(val,align)       (((val) + ((align) - 1)) & ~((align) - 1))
-
-/*
- * Offsets relative to the I/O and memory base addresses from where resources
- * are allocated.
- */
-
-#define IO_ALLOC_OFFSET                0x00004000
-#define MEM_ALLOC_OFFSET       0x04000000
-
-/*
- * Declarations of hardware specific initialisation functions.
- */
-
-extern struct pci_bus_info *init_hades_pci(void);
-
-/*
- * Bus info structure of the PCI bus. A pointer to this structure is
- * put in the sysdata member of the pci_bus structure.
- */
-
-static struct pci_bus_info *bus_info;
-
-static int pci_modify = 1;             /* If set, layout the PCI bus ourself. */
-static int skip_vga;                   /* If set do not modify base addresses
-                                          of vga cards.*/
-static int disable_pci_burst;          /* If set do not allow PCI bursts. */
-
-static unsigned int io_base;
-static unsigned int mem_base;
-
-/*
- * static void disable_dev(struct pci_dev *dev)
- *
- * Disable PCI device DEV so that it does not respond to I/O or memory
- * accesses.
- *
- * Parameters:
- *
- * dev - device to disable.
- */
-
-static void __init disable_dev(struct pci_dev *dev)
-{
-       unsigned short cmd;
-
-       if (((dev->class >> 8 == PCI_CLASS_NOT_DEFINED_VGA) ||
-            (dev->class >> 8 == PCI_CLASS_DISPLAY_VGA) ||
-            (dev->class >> 8 == PCI_CLASS_DISPLAY_XGA)) && skip_vga)
-               return;
-
-       pci_read_config_word(dev, PCI_COMMAND, &cmd);
-
-       cmd &= (~PCI_COMMAND_IO & ~PCI_COMMAND_MEMORY & ~PCI_COMMAND_MASTER);
-       pci_write_config_word(dev, PCI_COMMAND, cmd);
-}
-
-/*
- * static void layout_dev(struct pci_dev *dev)
- *
- * Layout memory and I/O for a device.
- *
- * Parameters:
- *
- * device      - device to layout memory and I/O for.
- */
-
-static void __init layout_dev(struct pci_dev *dev)
-{
-       unsigned short cmd;
-       unsigned int base, mask, size, reg;
-       unsigned int alignto;
-       int i;
-
-       /*
-        * Skip video cards if requested.
-        */
-
-       if (((dev->class >> 8 == PCI_CLASS_NOT_DEFINED_VGA) ||
-            (dev->class >> 8 == PCI_CLASS_DISPLAY_VGA) ||
-            (dev->class >> 8 == PCI_CLASS_DISPLAY_XGA)) && skip_vga)
-               return;
-
-       pci_read_config_word(dev, PCI_COMMAND, &cmd);
-
-       for (reg = PCI_BASE_ADDRESS_0, i = 0; reg <= PCI_BASE_ADDRESS_5; reg += 4, i++)
-       {
-               /*
-                * Figure out how much space and of what type this
-                * device wants.
-                */
-
-               pci_write_config_dword(dev, reg, 0xffffffff);
-               pci_read_config_dword(dev, reg, &base);
-
-               if (!base)
-               {
-                       /* this base-address register is unused */
-                       dev->resource[i].start = 0;
-                       dev->resource[i].end = 0;
-                       dev->resource[i].flags = 0;
-                       continue;
-               }
-
-               /*
-                * We've read the base address register back after
-                * writing all ones and so now we must decode it.
-                */
-
-               if (base & PCI_BASE_ADDRESS_SPACE_IO)
-               {
-                       /*
-                        * I/O space base address register.
-                        */
-
-                       cmd |= PCI_COMMAND_IO;
-
-                       base &= PCI_BASE_ADDRESS_IO_MASK;
-                       mask = (~base << 1) | 0x1;
-                       size = (mask & base) & 0xffffffff;
-
-                       /*
-                        * Align to multiple of size of minimum base.
-                        */
-
-                       alignto = max_t(unsigned int, 0x040, size);
-                       base = ALIGN(io_base, alignto);
-                       io_base = base + size;
-                       pci_write_config_dword(dev, reg, base | PCI_BASE_ADDRESS_SPACE_IO);
-
-                       dev->resource[i].start = base;
-                       dev->resource[i].end = dev->resource[i].start + size - 1;
-                       dev->resource[i].flags = IORESOURCE_IO | PCI_BASE_ADDRESS_SPACE_IO;
-
-                       DBG_DEVS(("layout_dev: IO address: %lX\n", base));
-               }
-               else
-               {
-                       unsigned int type;
-
-                       /*
-                        * Memory space base address register.
-                        */
-
-                       cmd |= PCI_COMMAND_MEMORY;
-                       type = base & PCI_BASE_ADDRESS_MEM_TYPE_MASK;
-                       base &= PCI_BASE_ADDRESS_MEM_MASK;
-                       mask = (~base << 1) | 0x1;
-                       size = (mask & base) & 0xffffffff;
-                       switch (type)
-                       {
-                       case PCI_BASE_ADDRESS_MEM_TYPE_32:
-                       case PCI_BASE_ADDRESS_MEM_TYPE_64:
-                               break;
-
-                       case PCI_BASE_ADDRESS_MEM_TYPE_1M:
-                               printk("bios32 WARNING: slot %d, function %d "
-                                      "requests memory below 1MB---don't "
-                                      "know how to do that.\n",
-                                      PCI_SLOT(dev->devfn),
-                                      PCI_FUNC(dev->devfn));
-                               continue;
-                       }
-
-                       /*
-                        * Align to multiple of size of minimum base.
-                        */
-
-                       alignto = max_t(unsigned int, 0x1000, size);
-                       base = ALIGN(mem_base, alignto);
-                       mem_base = base + size;
-                       pci_write_config_dword(dev, reg, base);
-
-                       dev->resource[i].start = base;
-                       dev->resource[i].end = dev->resource[i].start + size - 1;
-                       dev->resource[i].flags = IORESOURCE_MEM;
-
-                       if (type == PCI_BASE_ADDRESS_MEM_TYPE_64)
-                       {
-                               /*
-                                * 64-bit address, set the highest 32 bits
-                                * to zero.
-                                */
-
-                               reg += 4;
-                               pci_write_config_dword(dev, reg, 0);
-
-                               i++;
-                               dev->resource[i].start = 0;
-                               dev->resource[i].end = 0;
-                               dev->resource[i].flags = 0;
-                       }
-               }
-       }
-
-       /*
-        * Enable device:
-        */
-
-       if (dev->class >> 8 == PCI_CLASS_NOT_DEFINED ||
-           dev->class >> 8 == PCI_CLASS_NOT_DEFINED_VGA ||
-           dev->class >> 8 == PCI_CLASS_DISPLAY_VGA ||
-           dev->class >> 8 == PCI_CLASS_DISPLAY_XGA)
-       {
-               /*
-                * All of these (may) have I/O scattered all around
-                * and may not use i/o-base address registers at all.
-                * So we just have to always enable I/O to these
-                * devices.
-                */
-               cmd |= PCI_COMMAND_IO;
-       }
-
-       pci_write_config_word(dev, PCI_COMMAND, cmd | PCI_COMMAND_MASTER);
-
-       pci_write_config_byte(dev, PCI_LATENCY_TIMER, (disable_pci_burst) ? 0 : 32);
-
-       if (bus_info != NULL)
-               bus_info->conf_device(dev);     /* Machine dependent configuration. */
-
-       DBG_DEVS(("layout_dev: bus %d  slot 0x%x  VID 0x%x  DID 0x%x  class 0x%x\n",
-                 dev->bus->number, PCI_SLOT(dev->devfn), dev->vendor, dev->device, dev->class));
-}
-
-/*
- * static void layout_bus(struct pci_bus *bus)
- *
- * Layout memory and I/O for all devices on the given bus.
- *
- * Parameters:
- *
- * bus - bus.
- */
-
-static void __init layout_bus(struct pci_bus *bus)
-{
-       unsigned int bio, bmem;
-       struct pci_dev *dev;
-
-       DBG_DEVS(("layout_bus: starting bus %d\n", bus->number));
-
-       if (!bus->devices && !bus->children)
-               return;
-
-       /*
-        * Align the current bases on appropriate boundaries (4K for
-        * IO and 1MB for memory).
-        */
-
-       bio = io_base = ALIGN(io_base, 4*KB);
-       bmem = mem_base = ALIGN(mem_base, 1*MB);
-
-       /*
-        * PCI devices might have been setup by a PCI BIOS emulation
-        * running under TOS. In these cases there is a
-        * window during which two devices may have an overlapping
-        * address range. To avoid this causing trouble, we first
-        * turn off the I/O and memory address decoders for all PCI
-        * devices.  They'll be re-enabled only once all address
-        * decoders are programmed consistently.
-        */
-
-       DBG_DEVS(("layout_bus: disable_dev for bus %d\n", bus->number));
-
-       for (dev = bus->devices; dev; dev = dev->sibling)
-       {
-               if ((dev->class >> 16 != PCI_BASE_CLASS_BRIDGE) ||
-                   (dev->class >> 8 == PCI_CLASS_BRIDGE_PCMCIA))
-                       disable_dev(dev);
-       }
-
-       /*
-        * Allocate space to each device:
-        */
-
-       DBG_DEVS(("layout_bus: starting bus %d devices\n", bus->number));
-
-       for (dev = bus->devices; dev; dev = dev->sibling)
-       {
-               if ((dev->class >> 16 != PCI_BASE_CLASS_BRIDGE) ||
-                   (dev->class >> 8 == PCI_CLASS_BRIDGE_PCMCIA))
-                       layout_dev(dev);
-       }
-
-       DBG_DEVS(("layout_bus: bus %d finished\n", bus->number));
-}
-
-/*
- * static void pcibios_fixup(void)
- *
- * Layout memory and I/O of all devices on the PCI bus if 'pci_modify' is
- * true. This might be necessary because not every m68k machine with a PCI
- * bus has a PCI BIOS. This function should be called right after
- * pci_scan_bus() in pcibios_init().
- */
-
-static void __init pcibios_fixup(void)
-{
-       if (pci_modify)
-       {
-               /*
-                * Set base addresses for allocation of I/O and memory space.
-                */
-
-               io_base = bus_info->io_space.start + IO_ALLOC_OFFSET;
-               mem_base = bus_info->mem_space.start + MEM_ALLOC_OFFSET;
-
-               /*
-                * Scan the tree, allocating PCI memory and I/O space.
-                */
-
-               layout_bus(pci_bus_b(pci_root.next));
-       }
-
-       /*
-        * Fix interrupt assignments, etc.
-        */
-
-       bus_info->fixup(pci_modify);
-}
-
-/*
- * static void pcibios_claim_resources(struct pci_bus *bus)
- *
- * Claim all resources that are assigned to devices on the given bus.
- *
- * Parameters:
- *
- * bus - bus.
- */
-
-static void __init pcibios_claim_resources(struct pci_bus *bus)
-{
-       struct pci_dev *dev;
-       int i;
-
-       while (bus)
-       {
-               for (dev = bus->devices; (dev != NULL); dev = dev->sibling)
-               {
-                       for (i = 0; i < PCI_NUM_RESOURCES; i++)
-                       {
-                               struct resource *r = &dev->resource[i];
-                               struct resource *pr;
-                               struct pci_bus_info *bus_info = (struct pci_bus_info *) dev->sysdata;
-
-                               if ((r->start == 0) || (r->parent != NULL))
-                                       continue;
-#if 1
-                               if (r->flags & IORESOURCE_IO)
-                                       pr = &bus_info->io_space;
-                               else
-                                       pr = &bus_info->mem_space;
-#else
-                               if (r->flags & IORESOURCE_IO)
-                                       pr = &ioport_resource;
-                               else
-                                       pr = &iomem_resource;
-#endif
-                               if (request_resource(pr, r) < 0)
-                               {
-                                       printk(KERN_ERR "PCI: Address space collision on region %d of device %s\n", i, dev->name);
-                               }
-                       }
-               }
-
-               if (bus->children)
-                       pcibios_claim_resources(bus->children);
-
-               bus = bus->next;
-       }
-}
-
-/*
- * int pcibios_assign_resource(struct pci_dev *dev, int i)
- *
- * Assign a new address to a PCI resource.
- *
- * Parameters:
- *
- * dev - device.
- * i   - resource.
- *
- * Result: 0 if successful.
- */
-
-int __init pcibios_assign_resource(struct pci_dev *dev, int i)
-{
-       struct resource *r = &dev->resource[i];
-       struct resource *pr = pci_find_parent_resource(dev, r);
-       unsigned long size = r->end + 1;
-
-       if (!pr)
-               return -EINVAL;
-
-       if (r->flags & IORESOURCE_IO)
-       {
-               if (size > 0x100)
-                       return -EFBIG;
-
-               if (allocate_resource(pr, r, size, bus_info->io_space.start +
-                                     IO_ALLOC_OFFSET,  bus_info->io_space.end, 1024))
-                       return -EBUSY;
-       }
-       else
-       {
-               if (allocate_resource(pr, r, size, bus_info->mem_space.start +
-                                     MEM_ALLOC_OFFSET, bus_info->mem_space.end, size))
-                       return -EBUSY;
-       }
-
-       if (i < 6)
-               pci_write_config_dword(dev, PCI_BASE_ADDRESS_0 + 4 * i, r->start);
-
-       return 0;
-}
-
-void __init pcibios_fixup_bus(struct pci_bus *bus)
-{
-       struct pci_dev *dev;
-       void *sysdata;
-
-       sysdata = (bus->parent) ? bus->parent->sysdata : bus->sysdata;
-
-       for (dev = bus->devices; (dev != NULL); dev = dev->sibling)
-               dev->sysdata = sysdata;
-}
-
-void __init pcibios_init(void)
-{
-       printk("Linux/m68k PCI BIOS32 revision %x.%02x\n", MAJOR_REV, MINOR_REV);
-
-       bus_info = NULL;
-#ifdef CONFIG_HADES
-       if (MACH_IS_HADES)
-               bus_info = init_hades_pci();
-#endif
-       if (bus_info != NULL)
-       {
-               printk("PCI: Probing PCI hardware\n");
-               pci_scan_bus(0, bus_info->m68k_pci_ops, bus_info);
-               pcibios_fixup();
-               pcibios_claim_resources(pci_root);
-       }
-       else
-               printk("PCI: No PCI bus detected\n");
-}
-
-char * __init pcibios_setup(char *str)
-{
-       if (!strcmp(str, "nomodify"))
-       {
-               pci_modify = 0;
-               return NULL;
-       }
-       else if (!strcmp(str, "skipvga"))
-       {
-               skip_vga = 1;
-               return NULL;
-       }
-       else if (!strcmp(str, "noburst"))
-       {
-               disable_pci_burst = 1;
-               return NULL;
-       }
-
-       return str;
-}
-#endif /* CONFIG_PCI */
index 6f8c080dd9f9d24979f23adefb086810b7c2ae65..2bb4245404d836ca73e770ed24146f7c6c78e9b4 100644 (file)
@@ -66,8 +66,8 @@ void dma_free_coherent(struct device *dev, size_t size,
 }
 EXPORT_SYMBOL(dma_free_coherent);
 
-inline void dma_sync_single_for_device(struct device *dev, dma_addr_t handle, size_t size,
-                                      enum dma_data_direction dir)
+void dma_sync_single_for_device(struct device *dev, dma_addr_t handle,
+                               size_t size, enum dma_data_direction dir)
 {
        switch (dir) {
        case DMA_TO_DEVICE:
index ded7dd2f67b2803688965a49f0abb72faf1e0463..7e8a0d394e6184cbf63a5fb257befa4082ce0148 100644 (file)
@@ -429,8 +429,9 @@ int show_interrupts(struct seq_file *p, void *v)
        return 0;
 }
 
+#ifdef CONFIG_PROC_FS
 void init_irq_proc(void)
 {
        /* Insert /proc/irq driver here */
 }
-
+#endif
index 7888cdf91f5dafba1d157547d8ddb4b1a7f268a9..3042c2bc8c58490083ef4a2718c6a839c4e7e978 100644 (file)
@@ -78,7 +78,7 @@ unsigned long thread_saved_pc(struct task_struct *tsk)
 static void default_idle(void)
 {
        if (!need_resched())
-#if defined(MACH_ATARI_ONLY) && !defined(CONFIG_HADES)
+#if defined(MACH_ATARI_ONLY)
                /* block out HSYNC on the atari (falcon) */
                __asm__("stop #0x2200" : : : "cc");
 #else
index 75b8340b254be08caa07f9e29eac3cbb5e5f0161..6d813de2baf183472f9a95379505e602508845c7 100644 (file)
@@ -883,8 +883,7 @@ void show_trace(unsigned long *stack)
                        if (i % 5 == 0)
                                printk("\n       ");
 #endif
-                       printk(" [<%08lx>]", addr);
-                       print_symbol(" %s\n", addr);
+                       printk(" [<%08lx>] %pS\n", addr, (void *)addr);
                        i++;
                }
        }
@@ -900,10 +899,8 @@ void show_registers(struct pt_regs *regs)
        int i;
 
        print_modules();
-       printk("PC: [<%08lx>]",regs->pc);
-       print_symbol(" %s", regs->pc);
-       printk("\nSR: %04x  SP: %p  a2: %08lx\n",
-              regs->sr, regs, regs->a2);
+       printk("PC: [<%08lx>] %pS\n", regs->pc, (void *)regs->pc);
+       printk("SR: %04x  SP: %p  a2: %08lx\n", regs->sr, regs, regs->a2);
        printk("d0: %08lx    d1: %08lx    d2: %08lx    d3: %08lx\n",
               regs->d0, regs->d1, regs->d2, regs->d3);
        printk("d4: %08lx    d5: %08lx    a0: %08lx    a1: %08lx\n",
index 99b0784c0552a094cb629f26ae3827e469d17a36..f846d4e3e5e1f23a179ec47e69bbb09488a0c3c8 100644 (file)
@@ -34,10 +34,10 @@ SECTIONS
        CONSTRUCTORS
        }
 
-  .bss : { *(.bss) }           /* BSS */
-
   . = ALIGN(16);
-  .data.cacheline_aligned : { *(.data.cacheline_aligned) } :data
+  .data.cacheline_aligned : { *(.data.cacheline_aligned) }
+
+  .bss : { *(.bss) }           /* BSS */
 
   _edata = .;                  /* End of data section */
 
@@ -48,7 +48,7 @@ SECTIONS
        _sinittext = .;
        INIT_TEXT
        _einittext = .;
-  }
+  } :data
   .init.data : { INIT_DATA }
   . = ALIGN(16);
   __setup_start = .;
@@ -74,6 +74,7 @@ SECTIONS
   .init.ramfs : { *(.init.ramfs) }
   __initramfs_end = .;
 #endif
+  NOTES
   . = ALIGN(8192);
   __init_end = .;
 
index 46b7d6035aabaab6a299f5c2cef9fee411c96bb4..df620ac2a296b7a7da77a87cf9e00c06ee0786e5 100644 (file)
@@ -66,8 +66,10 @@ static struct vm_struct *get_io_area(unsigned long size)
        for (p = &iolist; (tmp = *p) ; p = &tmp->next) {
                if (size + addr < (unsigned long)tmp->addr)
                        break;
-               if (addr > KMAP_END-size)
+               if (addr > KMAP_END-size) {
+                       kfree(area);
                        return NULL;
+               }
                addr = tmp->size + (unsigned long)tmp->addr;
        }
        area->addr = (void *)addr;
index 432a9f13b2ed6bc6955908096740c1154e3d4ee5..cea5e3e4e63646abf411aeb34be8172493214a34 100644 (file)
@@ -52,15 +52,15 @@ static int rtc_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
                /* Ensure clock and real-time-mode-register are accessible */
                rtc->ctrl = RTC_READ;
                memset(&wtime, 0, sizeof(struct rtc_time));
-               wtime.tm_sec =  BCD2BIN(rtc->bcd_sec);
-               wtime.tm_min =  BCD2BIN(rtc->bcd_min);
-               wtime.tm_hour = BCD2BIN(rtc->bcd_hr);
-               wtime.tm_mday =  BCD2BIN(rtc->bcd_dom);
-               wtime.tm_mon =  BCD2BIN(rtc->bcd_mth)-1;
-               wtime.tm_year = BCD2BIN(rtc->bcd_year);
+               wtime.tm_sec =  bcd2bin(rtc->bcd_sec);
+               wtime.tm_min =  bcd2bin(rtc->bcd_min);
+               wtime.tm_hour = bcd2bin(rtc->bcd_hr);
+               wtime.tm_mday =  bcd2bin(rtc->bcd_dom);
+               wtime.tm_mon =  bcd2bin(rtc->bcd_mth)-1;
+               wtime.tm_year = bcd2bin(rtc->bcd_year);
                if (wtime.tm_year < 70)
                        wtime.tm_year += 100;
-               wtime.tm_wday = BCD2BIN(rtc->bcd_dow)-1;
+               wtime.tm_wday = bcd2bin(rtc->bcd_dow)-1;
                rtc->ctrl = 0;
                local_irq_restore(flags);
                return copy_to_user(argp, &wtime, sizeof wtime) ?
@@ -104,12 +104,12 @@ static int rtc_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
                local_irq_save(flags);
                rtc->ctrl     = RTC_WRITE;
 
-               rtc->bcd_sec  = BIN2BCD(sec);
-               rtc->bcd_min  = BIN2BCD(min);
-               rtc->bcd_hr   = BIN2BCD(hrs);
-               rtc->bcd_dom  = BIN2BCD(day);
-               rtc->bcd_mth  = BIN2BCD(mon);
-               rtc->bcd_year = BIN2BCD(yrs%100);
+               rtc->bcd_sec  = bin2bcd(sec);
+               rtc->bcd_min  = bin2bcd(min);
+               rtc->bcd_hr   = bin2bcd(hrs);
+               rtc->bcd_dom  = bin2bcd(day);
+               rtc->bcd_mth  = bin2bcd(mon);
+               rtc->bcd_year = bin2bcd(yrs%100);
 
                rtc->ctrl     = 0;
                local_irq_restore(flags);
index be9de2f3dc48ee1da88283072e49a28890329122..9c7eefa3f98a133bd53d767b0edbf3f622993416 100644 (file)
@@ -23,6 +23,7 @@
 #include <linux/serial_reg.h>
 #include <linux/rtc.h>
 #include <linux/vt_kern.h>
+#include <linux/bcd.h>
 
 #include <asm/io.h>
 #include <asm/rtc.h>
@@ -216,17 +217,6 @@ int q40_parse_bootinfo(const struct bi_record *rec)
 }
 
 
-static inline unsigned char bcd2bin(unsigned char b)
-{
-       return (b >> 4) * 10 + (b & 15);
-}
-
-static inline unsigned char bin2bcd(unsigned char b)
-{
-       return (b / 10) * 16 + (b % 10);
-}
-
-
 static unsigned long q40_gettimeoffset(void)
 {
        return 5000 * (ql_ticks != 0);
index f5eaafb00d218ce9519545b3ddb1c2600bac9532..536a04aaf22f28b9de07fda6dd9420b1b31e3b3d 100644 (file)
@@ -47,23 +47,23 @@ int sun3x_hwclk(int set, struct rtc_time *t)
 
        if(set) {
                h->csr |= C_WRITE;
-               h->sec = BIN2BCD(t->tm_sec);
-               h->min = BIN2BCD(t->tm_min);
-               h->hour = BIN2BCD(t->tm_hour);
-               h->wday = BIN2BCD(t->tm_wday);
-               h->mday = BIN2BCD(t->tm_mday);
-               h->month = BIN2BCD(t->tm_mon);
-               h->year = BIN2BCD(t->tm_year);
+               h->sec = bin2bcd(t->tm_sec);
+               h->min = bin2bcd(t->tm_min);
+               h->hour = bin2bcd(t->tm_hour);
+               h->wday = bin2bcd(t->tm_wday);
+               h->mday = bin2bcd(t->tm_mday);
+               h->month = bin2bcd(t->tm_mon);
+               h->year = bin2bcd(t->tm_year);
                h->csr &= ~C_WRITE;
        } else {
                h->csr |= C_READ;
-               t->tm_sec = BCD2BIN(h->sec);
-               t->tm_min = BCD2BIN(h->min);
-               t->tm_hour = BCD2BIN(h->hour);
-               t->tm_wday = BCD2BIN(h->wday);
-               t->tm_mday = BCD2BIN(h->mday);
-               t->tm_mon = BCD2BIN(h->month);
-               t->tm_year = BCD2BIN(h->year);
+               t->tm_sec = bcd2bin(h->sec);
+               t->tm_min = bcd2bin(h->min);
+               t->tm_hour = bcd2bin(h->hour);
+               t->tm_wday = bcd2bin(h->wday);
+               t->tm_mday = bcd2bin(h->mday);
+               t->tm_mon = bcd2bin(h->month);
+               t->tm_year = bcd2bin(h->year);
                h->csr &= ~C_READ;
        }
 
index 587da5e0990f9504979f9357b2783305e6a23d02..f5f83ee6041126832b6c0f514b47e082759aac0c 100644 (file)
@@ -22,6 +22,9 @@ config WORD_SIZE
 config PPC_MERGE
        def_bool y
 
+config ARCH_PHYS_ADDR_T_64BIT
+       def_bool PPC64 || PHYS_64BIT
+
 config MMU
        bool
        default y
index d3374bc865ba565c803056ed33cc57d64241d670..c646f34c4e8b5879d1c4c4922b40bf43191f0445 100644 (file)
@@ -48,13 +48,6 @@ typedef struct {
 
 typedef __vector128 vector128;
 
-/* Physical address used by some IO functions */
-#if defined(CONFIG_PPC64) || defined(CONFIG_PHYS_64BIT)
-typedef u64 phys_addr_t;
-#else
-typedef u32 phys_addr_t;
-#endif
-
 #ifdef __powerpc64__
 typedef u64 dma_addr_t;
 #else
index 7f6512733862b02138f4e0370a994a223a1e9a07..be852fd407a85406c4f41594146b49eeb2a13c37 100644 (file)
@@ -135,7 +135,6 @@ config PTE_64BIT
 config PHYS_64BIT
        bool 'Large physical address support' if E500
        depends on 44x || E500
-       select RESOURCES_64BIT
        default y if 44x
        ---help---
          This option enables kernel support for larger than 32-bit physical
index fb368dfde5d472403a67c33ec88d8cde55beec6f..e8a76d9539dbb82e7aed662a19a6c0f71aa8b27e 100644 (file)
@@ -41,13 +41,10 @@ extern unsigned long total_memory;
 #define U64_TO_U32_LOW(val)    ((u32)((val) & 0x00000000ffffffffULL))
 #define U64_TO_U32_HIGH(val)   ((u32)((val) >> 32))
 
-#ifdef CONFIG_RESOURCES_64BIT
-#define RES_TO_U32_LOW(val)    U64_TO_U32_LOW(val)
-#define RES_TO_U32_HIGH(val)   U64_TO_U32_HIGH(val)
-#else
-#define RES_TO_U32_LOW(val)    (val)
-#define RES_TO_U32_HIGH(val)   (0)
-#endif
+#define RES_TO_U32_LOW(val)    \
+       ((sizeof(resource_size_t) > sizeof(u32)) ? U64_TO_U32_LOW(val) : (val))
+#define RES_TO_U32_HIGH(val)   \
+       ((sizeof(resource_size_t) > sizeof(u32)) ? U64_TO_U32_HIGH(val) : (0))
 
 static inline int ppc440spe_revA(void)
 {
@@ -145,12 +142,11 @@ static int __init ppc4xx_parse_dma_ranges(struct pci_controller *hose,
 
                /* Use that */
                res->start = pci_addr;
-#ifndef CONFIG_RESOURCES_64BIT
                /* Beware of 32 bits resources */
-               if ((pci_addr + size) > 0x100000000ull)
+               if (sizeof(resource_size_t) == sizeof(u32) &&
+                   (pci_addr + size) > 0x100000000ull)
                        res->end = 0xffffffff;
                else
-#endif
                        res->end = res->start + size - 1;
                break;
        }
index f65c2744d5733971caf75b253070b4e9a821d786..837a9aae0453f554fa89d60dc42875aa5fff81bc 100644 (file)
@@ -947,13 +947,15 @@ config X86_PAE
        def_bool n
        prompt "PAE (Physical Address Extension) Support"
        depends on X86_32 && !HIGHMEM4G
-       select RESOURCES_64BIT
        help
          PAE is required for NX support, and furthermore enables
          larger swapspace support for non-overcommit purposes. It
          has the cost of more pagetable lookup overhead, and also
          consumes more pagetable space per process.
 
+config ARCH_PHYS_ADDR_T_64BIT
+       def_bool X86_64 || X86_PAE
+
 # Common NUMA Features
 config NUMA
        bool "Numa Memory Allocation and Scheduler Support (EXPERIMENTAL)"
index 78e642feac30423f57369166cb76724efe474b7c..ce97bf3bed12d71ee6d1fce3ca5bb67b4ac53156 100644 (file)
@@ -1282,12 +1282,10 @@ void __init e820_reserve_resources(void)
        e820_res = res;
        for (i = 0; i < e820.nr_map; i++) {
                end = e820.map[i].addr + e820.map[i].size - 1;
-#ifndef CONFIG_RESOURCES_64BIT
-               if (end > 0x100000000ULL) {
+               if (end != (resource_size_t)end) {
                        res++;
                        continue;
                }
-#endif
                res->name = e820_type_to_string(e820.map[i].type);
                res->start = e820.map[i].addr;
                res->end = end;
index 05191bbc68b8bef23926a60b28b461ca559e5502..0a23b5795b25a87c38b117e4bfc168dfc565c2d5 100644 (file)
@@ -223,11 +223,25 @@ static struct platform_device rtc_device = {
 static __init int add_rtc_cmos(void)
 {
 #ifdef CONFIG_PNP
-       if (!pnp_platform_devices)
-               platform_device_register(&rtc_device);
-#else
+       static const char *ids[] __initconst =
+           { "PNP0b00", "PNP0b01", "PNP0b02", };
+       struct pnp_dev *dev;
+       struct pnp_id *id;
+       int i;
+
+       pnp_for_each_dev(dev) {
+               for (id = dev->id; id; id = id->next) {
+                       for (i = 0; i < ARRAY_SIZE(ids); i++) {
+                               if (compare_pnp_id(id, ids[i]) != 0)
+                                       return 0;
+                       }
+               }
+       }
+#endif
+
        platform_device_register(&rtc_device);
-#endif /* CONFIG_PNP */
+       dev_info(&rtc_device.dev,
+                "registered platform RTC device (no PNP device found)\n");
        return 0;
 }
 device_initcall(add_rtc_cmos);
index e4c43ec71b29ebeb40a40b63cd7f4394fb448b0a..ae71e11eb3e5e4ddeceadc9128d3afea564f27e0 100644 (file)
@@ -219,6 +219,12 @@ static void __iomem *__ioremap_caller(resource_size_t phys_addr,
        if (is_ISA_range(phys_addr, last_addr))
                return (__force void __iomem *)phys_to_virt(phys_addr);
 
+       /*
+        * Check if the request spans more than any BAR in the iomem resource
+        * tree.
+        */
+       WARN_ON(iomem_map_sanity_check(phys_addr, size));
+
        /*
         * Don't allow anybody to remap normal RAM that we're using..
         */
index 3c578ef78c4830f791039b83e2a44ab80d7538d8..24649ada08dfd353c8a9895df6773156520e66ea 100644 (file)
@@ -260,115 +260,3 @@ static int __init init_acpi_device_notify(void)
 }
 
 arch_initcall(init_acpi_device_notify);
-
-
-#if defined(CONFIG_RTC_DRV_CMOS) || defined(CONFIG_RTC_DRV_CMOS_MODULE)
-
-#ifdef CONFIG_PM
-static u32 rtc_handler(void *context)
-{
-       acpi_clear_event(ACPI_EVENT_RTC);
-       acpi_disable_event(ACPI_EVENT_RTC, 0);
-       return ACPI_INTERRUPT_HANDLED;
-}
-
-static inline void rtc_wake_setup(void)
-{
-       acpi_install_fixed_event_handler(ACPI_EVENT_RTC, rtc_handler, NULL);
-       /*
-        * After the RTC handler is installed, the Fixed_RTC event should
-        * be disabled. Only when the RTC alarm is set will it be enabled.
-        */
-       acpi_clear_event(ACPI_EVENT_RTC);
-       acpi_disable_event(ACPI_EVENT_RTC, 0);
-}
-
-static void rtc_wake_on(struct device *dev)
-{
-       acpi_clear_event(ACPI_EVENT_RTC);
-       acpi_enable_event(ACPI_EVENT_RTC, 0);
-}
-
-static void rtc_wake_off(struct device *dev)
-{
-       acpi_disable_event(ACPI_EVENT_RTC, 0);
-}
-#else
-#define rtc_wake_setup()       do{}while(0)
-#define rtc_wake_on            NULL
-#define rtc_wake_off           NULL
-#endif
-
-/* Every ACPI platform has a mc146818 compatible "cmos rtc".  Here we find
- * its device node and pass extra config data.  This helps its driver use
- * capabilities that the now-obsolete mc146818 didn't have, and informs it
- * that this board's RTC is wakeup-capable (per ACPI spec).
- */
-#include <linux/mc146818rtc.h>
-
-static struct cmos_rtc_board_info rtc_info;
-
-
-/* PNP devices are registered in a subsys_initcall();
- * ACPI specifies the PNP IDs to use.
- */
-#include <linux/pnp.h>
-
-static int __init pnp_match(struct device *dev, void *data)
-{
-       static const char *ids[] = { "PNP0b00", "PNP0b01", "PNP0b02", };
-       struct pnp_dev *pnp = to_pnp_dev(dev);
-       int i;
-
-       for (i = 0; i < ARRAY_SIZE(ids); i++) {
-               if (compare_pnp_id(pnp->id, ids[i]) != 0)
-                       return 1;
-       }
-       return 0;
-}
-
-static struct device *__init get_rtc_dev(void)
-{
-       return bus_find_device(&pnp_bus_type, NULL, NULL, pnp_match);
-}
-
-static int __init acpi_rtc_init(void)
-{
-       struct device *dev = get_rtc_dev();
-
-       if (acpi_disabled)
-               return 0;
-
-       if (dev) {
-               rtc_wake_setup();
-               rtc_info.wake_on = rtc_wake_on;
-               rtc_info.wake_off = rtc_wake_off;
-
-               /* workaround bug in some ACPI tables */
-               if (acpi_gbl_FADT.month_alarm && !acpi_gbl_FADT.day_alarm) {
-                       DBG("bogus FADT month_alarm\n");
-                       acpi_gbl_FADT.month_alarm = 0;
-               }
-
-               rtc_info.rtc_day_alarm = acpi_gbl_FADT.day_alarm;
-               rtc_info.rtc_mon_alarm = acpi_gbl_FADT.month_alarm;
-               rtc_info.rtc_century = acpi_gbl_FADT.century;
-
-               /* NOTE:  S4_RTC_WAKE is NOT currently useful to Linux */
-               if (acpi_gbl_FADT.flags & ACPI_FADT_S4_RTC_WAKE)
-                       printk(PREFIX "RTC can wake from S4\n");
-
-
-               dev->platform_data = &rtc_info;
-
-               /* RTC always wakes from S1/S2/S3, and often S4/STD */
-               device_init_wakeup(dev, 1);
-
-               put_device(dev);
-       } else
-               DBG("RTC unavailable?\n");
-       return 0;
-}
-module_init(acpi_rtc_init);
-
-#endif
index 49f274197b16a7ece82403a4118dc26c2d38b514..432cf40182916366b71fad0b9c4f8e1131b2ef66 100644 (file)
@@ -1882,10 +1882,6 @@ static int __init atari_floppy_init (void)
                /* Amiga, Mac, ... don't have Atari-compatible floppy :-) */
                return -ENODEV;
 
-       if (MACH_IS_HADES)
-               /* Hades doesn't have Atari-compatible floppy */
-               return -ENODEV;
-
        if (register_blkdev(FLOPPY_MAJOR,"fd"))
                return -EBUSY;
 
index 57029fefd64a65fec4bfbb0e69a1915e8aa4359a..a0f7ffb6808716679ed1bc6d4cefb088ad0ba972 100644 (file)
@@ -301,7 +301,7 @@ static void scrup(struct vc_data *vc, unsigned int t, unsigned int b, int nr)
        d = (unsigned short *)(vc->vc_origin + vc->vc_size_row * t);
        s = (unsigned short *)(vc->vc_origin + vc->vc_size_row * (t + nr));
        scr_memmovew(d, s, (b - t - nr) * vc->vc_size_row);
-       scr_memsetw(d + (b - t - nr) * vc->vc_cols, vc->vc_scrl_erase_char,
+       scr_memsetw(d + (b - t - nr) * vc->vc_cols, vc->vc_video_erase_char,
                    vc->vc_size_row * nr);
 }
 
@@ -319,7 +319,7 @@ static void scrdown(struct vc_data *vc, unsigned int t, unsigned int b, int nr)
        s = (unsigned short *)(vc->vc_origin + vc->vc_size_row * t);
        step = vc->vc_cols * nr;
        scr_memmovew(s + step, s, (b - t - nr) * vc->vc_size_row);
-       scr_memsetw(s, vc->vc_scrl_erase_char, 2 * step);
+       scr_memsetw(s, vc->vc_video_erase_char, 2 * step);
 }
 
 static void do_update_region(struct vc_data *vc, unsigned long start, int count)
@@ -434,7 +434,6 @@ static void update_attr(struct vc_data *vc)
                      vc->vc_blink, vc->vc_underline,
                      vc->vc_reverse ^ vc->vc_decscnm, vc->vc_italic);
        vc->vc_video_erase_char = (build_attr(vc, vc->vc_color, 1, vc->vc_blink, 0, vc->vc_decscnm, 0) << 8) | ' ';
-       vc->vc_scrl_erase_char = (build_attr(vc, vc->vc_def_color, 1, false, false, vc->vc_decscnm, false) << 8) | ' ';
 }
 
 /* Note: inverting the screen twice should revert to the original state */
index 455575be3560d2336c75d2cc8aaf42c64a728f9c..3e526b6d00cbff758be01772aa8663c002e4f5a4 100644 (file)
  */
 static char dmi_empty_string[] = "        ";
 
+/*
+ * Catch too early calls to dmi_check_system():
+ */
+static int dmi_initialized;
+
 static const char * __init dmi_string_nosave(const struct dmi_header *dm, u8 s)
 {
        const u8 *bp = ((u8 *) dm) + dm->length;
@@ -366,7 +371,7 @@ void __init dmi_scan_machine(void)
 
        if (efi_enabled) {
                if (efi.smbios == EFI_INVALID_TABLE_ADDR)
-                       goto out;
+                       goto error;
 
                /* This is called as a core_initcall() because it isn't
                 * needed during early boot.  This also means we can
@@ -374,13 +379,13 @@ void __init dmi_scan_machine(void)
                 */
                p = dmi_ioremap(efi.smbios, 32);
                if (p == NULL)
-                       goto out;
+                       goto error;
 
                rc = dmi_present(p + 0x10); /* offset of _DMI_ string */
                dmi_iounmap(p, 32);
                if (!rc) {
                        dmi_available = 1;
-                       return;
+                       goto out;
                }
        }
        else {
@@ -391,19 +396,22 @@ void __init dmi_scan_machine(void)
                 */
                p = dmi_ioremap(0xF0000, 0x10000);
                if (p == NULL)
-                       goto out;
+                       goto error;
 
                for (q = p; q < p + 0x10000; q += 16) {
                        rc = dmi_present(q);
                        if (!rc) {
                                dmi_available = 1;
                                dmi_iounmap(p, 0x10000);
-                               return;
+                               goto out;
                        }
                }
                dmi_iounmap(p, 0x10000);
        }
- out:  printk(KERN_INFO "DMI not present or invalid.\n");
+ error:
+       printk(KERN_INFO "DMI not present or invalid.\n");
+ out:
+       dmi_initialized = 1;
 }
 
 /**
@@ -424,6 +432,8 @@ int dmi_check_system(const struct dmi_system_id *list)
        int i, count = 0;
        const struct dmi_system_id *d = list;
 
+       WARN(!dmi_initialized, KERN_ERR "dmi check: not initialized yet.\n");
+
        while (d->ident) {
                for (i = 0; i < ARRAY_SIZE(d->matches); i++) {
                        int s = d->matches[i].slot;
index cacf89e65af4d9f96b1785c873b6f76b384775c3..da64108de775062b8f02c5f21a4d7749ba8a603c 100644 (file)
@@ -17,6 +17,25 @@ config HID
        tristate "Generic HID support"
        depends on INPUT
        default y
+        select HID_A4TECH if !EMBEDDED
+        select HID_APPLE if !EMBEDDED
+        select HID_BELKIN if !EMBEDDED
+        select HID_BRIGHT if !EMBEDDED
+        select HID_CHERRY if !EMBEDDED
+        select HID_CHICONY if !EMBEDDED
+        select HID_CYPRESS if !EMBEDDED
+        select HID_DELL if !EMBEDDED
+        select HID_EZKEY if !EMBEDDED
+        select HID_GYRATION if !EMBEDDED
+        select HID_LOGITECH if !EMBEDDED
+        select HID_MICROSOFT if !EMBEDDED
+        select HID_MONTEREY if !EMBEDDED
+        select HID_PANTHERLORD if !EMBEDDED
+        select HID_PETALYNX if !EMBEDDED
+        select HID_SAMSUNG if !EMBEDDED
+        select HID_SONY if !EMBEDDED
+        select HID_SUNPLUS if !EMBEDDED
+
        ---help---
          A human interface device (HID) is a type of computer device that
          interacts directly with and takes input from humans. The term "HID"
@@ -67,4 +86,206 @@ config HIDRAW
 
 source "drivers/hid/usbhid/Kconfig"
 
+menu "Special HID drivers"
+       depends on HID
+
+config HID_COMPAT
+       bool "Load all HID drivers on hid core load"
+       default y
+       ---help---
+       Compatible option for older userspace. If you have system without udev
+       support of module loading through aliases and also old
+       module-init-tools which can't handle hid bus, choose Y here. Otherwise
+       say N. If you say N and your userspace is old enough, the only
+       functionality you lose is modules autoloading.
+
+       If unsure, say Y.
+
+config HID_A4TECH
+       tristate "A4 tech"
+       default m
+       depends on USB_HID
+       ---help---
+       Support for A4 tech X5 and WOP-35 / Trust 450L mice.
+
+config HID_APPLE
+       tristate "Apple"
+       default m
+       depends on (USB_HID || BT_HIDP)
+       ---help---
+       Support for some Apple devices which less or more break
+       HID specification.
+
+       Say Y here if you want support for the special keys (Fn, Numlock) on
+       Apple iBooks, PowerBooks, MacBooks, MacBook Pros and aluminum USB
+       keyboards.
+
+       If unsure, say M.
+
+config HID_BELKIN
+       tristate "Belkin"
+       default m
+       depends on USB_HID
+       ---help---
+       Support for Belkin Flip KVM and Wireless keyboard.
+
+config HID_BRIGHT
+       tristate "Bright"
+       default m
+       depends on USB_HID
+       ---help---
+       Support for Bright ABNT-2 keyboard.
+
+config HID_CHERRY
+       tristate "Cherry"
+       default m
+       depends on USB_HID
+       ---help---
+       Support for Cherry Cymotion.
+
+config HID_CHICONY
+       tristate "Chicony"
+       default m
+       depends on USB_HID
+       ---help---
+       Support for Chicony Tactical pad.
+
+config HID_CYPRESS
+       tristate "Cypress"
+       default m
+       depends on USB_HID
+       ---help---
+       Support for Cypress mouse and barcodes.
+
+config HID_DELL
+       tristate "Dell"
+       default m
+       depends on USB_HID
+       ---help---
+       Support for Dell W7658.
+
+config HID_EZKEY
+       tristate "Ezkey"
+       default m
+       depends on USB_HID
+       ---help---
+       Support for Ezkey mouse and barcodes.
+
+config HID_GYRATION
+       tristate "Gyration"
+       default m
+       depends on USB_HID
+       ---help---
+       Support for Gyration remote.
+
+config HID_LOGITECH
+       tristate "Logitech"
+       default m
+       depends on USB_HID
+       ---help---
+       Support for some Logitech devices which breaks less or more
+       HID specification.
+
+config LOGITECH_FF
+       bool "Logitech force feedback"
+       depends on HID_LOGITECH
+       select INPUT_FF_MEMLESS
+       help
+         Say Y here if you have one of these devices:
+         - Logitech WingMan Cordless RumblePad
+         - Logitech WingMan Cordless RumblePad 2
+         - Logitech WingMan Force 3D
+         - Logitech Formula Force EX
+         - Logitech MOMO Force wheel
+
+         and if you want to enable force feedback for them.
+         Note: if you say N here, this device will still be supported, but without
+         force feedback.
+
+config LOGIRUMBLEPAD2_FF
+       bool "Logitech Rumblepad 2 force feedback"
+       depends on HID_LOGITECH
+       select INPUT_FF_MEMLESS
+       help
+         Say Y here if you want to enable force feedback support for Logitech
+         Rumblepad 2 devices.
+
+config HID_MICROSOFT
+       tristate "Microsoft"
+       default m
+       depends on USB_HID
+       ---help---
+       Support for some Microsoft devices which breaks less or more
+       HID specification.
+
+config HID_MONTEREY
+       tristate "Monterey"
+       default m
+       depends on USB_HID
+       ---help---
+       Support for Monterey Genius KB29E.
+
+config HID_PANTHERLORD
+       tristate "Pantherlord devices support"
+       default m
+       depends on USB_HID
+       ---help---
+       Support for PantherLord/GreenAsia based device support.
+
+
+config PANTHERLORD_FF
+       bool "Pantherlord force feedback support"
+       depends on HID_PANTHERLORD
+       select INPUT_FF_MEMLESS
+       help
+         Say Y here if you have a PantherLord/GreenAsia based game controller
+         or adapter and want to enable force feedback support for it.
+
+config HID_PETALYNX
+       tristate "Petalynx"
+       default m
+       depends on USB_HID
+       ---help---
+       Support for Petalynx Maxter remote.
+
+config HID_SAMSUNG
+       tristate "Samsung"
+       default m
+       depends on USB_HID
+       ---help---
+       Support for Samsung IR remote.
+
+config HID_SONY
+       tristate "Sony"
+       default m
+       depends on USB_HID
+       ---help---
+       Support for Sony PS3 controller.
+
+config HID_SUNPLUS
+       tristate "Sunplus"
+       default m
+       depends on USB_HID
+       ---help---
+       Support for Sunplus WDesktop input device.
+
+config THRUSTMASTER_FF
+       tristate "ThrustMaster devices support"
+       default m
+       depends on USB_HID
+       select INPUT_FF_MEMLESS
+       help
+         Say Y here if you have a THRUSTMASTER FireStore Dual Power 2 or
+         a THRUSTMASTER Ferrari GT Rumble Force or Force Feedback Wheel.
+
+config ZEROPLUS_FF
+       tristate "Zeroplus based game controller support"
+       default m
+       depends on USB_HID
+       select INPUT_FF_MEMLESS
+       help
+         Say Y here if you have a Zeroplus based game controller.
+
+endmenu
+
 endif # HID_SUPPORT
index 275dc522c7389489583f7424fb75427d2a04867d..b09e43e7413e2ebc2fce3b7ffe5c6c7f708fff94 100644 (file)
@@ -1,13 +1,46 @@
 #
 # Makefile for the HID driver
 #
-hid-objs                       := hid-core.o hid-input.o hid-input-quirks.o
+hid-objs                       := hid-core.o hid-input.o
 
 obj-$(CONFIG_HID)              += hid.o
 
 hid-$(CONFIG_HID_DEBUG)                += hid-debug.o
 hid-$(CONFIG_HIDRAW)           += hidraw.o
 
+ifdef CONFIG_HID_COMPAT
+obj-m                          += hid-dummy.o
+endif
+
+hid-logitech-objs              := hid-lg.o
+ifdef CONFIG_LOGITECH_FF
+       hid-logitech-objs       += hid-lgff.o
+endif
+ifdef CONFIG_LOGIRUMBLEPAD2_FF
+       hid-logitech-objs       += hid-lg2ff.o
+endif
+
+obj-$(CONFIG_HID_A4TECH)       += hid-a4tech.o
+obj-$(CONFIG_HID_APPLE)                += hid-apple.o
+obj-$(CONFIG_HID_BELKIN)       += hid-belkin.o
+obj-$(CONFIG_HID_BRIGHT)       += hid-bright.o
+obj-$(CONFIG_HID_CHERRY)       += hid-cherry.o
+obj-$(CONFIG_HID_CHICONY)      += hid-chicony.o
+obj-$(CONFIG_HID_CYPRESS)      += hid-cypress.o
+obj-$(CONFIG_HID_DELL)         += hid-dell.o
+obj-$(CONFIG_HID_EZKEY)                += hid-ezkey.o
+obj-$(CONFIG_HID_GYRATION)     += hid-gyration.o
+obj-$(CONFIG_HID_LOGITECH)     += hid-logitech.o
+obj-$(CONFIG_HID_MICROSOFT)    += hid-microsoft.o
+obj-$(CONFIG_HID_MONTEREY)     += hid-monterey.o
+obj-$(CONFIG_HID_PANTHERLORD)  += hid-pl.o
+obj-$(CONFIG_HID_PETALYNX)     += hid-petalynx.o
+obj-$(CONFIG_HID_SAMSUNG)      += hid-samsung.o
+obj-$(CONFIG_HID_SONY)         += hid-sony.o
+obj-$(CONFIG_HID_SUNPLUS)      += hid-sunplus.o
+obj-$(CONFIG_THRUSTMASTER_FF)  += hid-tmff.o
+obj-$(CONFIG_ZEROPLUS_FF)      += hid-zpff.o
+
 obj-$(CONFIG_USB_HID)          += usbhid/
 obj-$(CONFIG_USB_MOUSE)                += usbhid/
 obj-$(CONFIG_USB_KBD)          += usbhid/
diff --git a/drivers/hid/hid-a4tech.c b/drivers/hid/hid-a4tech.c
new file mode 100644 (file)
index 0000000..ebca00e
--- /dev/null
@@ -0,0 +1,162 @@
+/*
+ *  HID driver for some a4tech "special" devices
+ *
+ *  Copyright (c) 1999 Andreas Gal
+ *  Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
+ *  Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
+ *  Copyright (c) 2006-2007 Jiri Kosina
+ *  Copyright (c) 2007 Paul Walmsley
+ *  Copyright (c) 2008 Jiri Slaby
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ */
+
+#include <linux/device.h>
+#include <linux/input.h>
+#include <linux/hid.h>
+#include <linux/module.h>
+
+#include "hid-ids.h"
+
+#define A4_2WHEEL_MOUSE_HACK_7 0x01
+#define A4_2WHEEL_MOUSE_HACK_B8        0x02
+
+struct a4tech_sc {
+       unsigned long quirks;
+       unsigned int hw_wheel;
+       __s32 delayed_value;
+};
+
+static int a4_input_mapped(struct hid_device *hdev, struct hid_input *hi,
+               struct hid_field *field, struct hid_usage *usage,
+               unsigned long **bit, int *max)
+{
+       struct a4tech_sc *a4 = hid_get_drvdata(hdev);
+
+       if (usage->type == EV_REL && usage->code == REL_WHEEL)
+               set_bit(REL_HWHEEL, *bit);
+
+       if ((a4->quirks & A4_2WHEEL_MOUSE_HACK_7) && usage->hid == 0x00090007)
+               return -1;
+
+       return 0;
+}
+
+static int a4_event(struct hid_device *hdev, struct hid_field *field,
+               struct hid_usage *usage, __s32 value)
+{
+       struct a4tech_sc *a4 = hid_get_drvdata(hdev);
+       struct input_dev *input;
+
+       if (!(hdev->claimed & HID_CLAIMED_INPUT) || !field->hidinput ||
+                       !usage->type)
+               return 0;
+
+       input = field->hidinput->input;
+
+       if (a4->quirks & A4_2WHEEL_MOUSE_HACK_B8) {
+               if (usage->type == EV_REL && usage->code == REL_WHEEL) {
+                       a4->delayed_value = value;
+                       return 1;
+               }
+
+               if (usage->hid == 0x000100b8) {
+                       input_event(input, EV_REL, value ? REL_HWHEEL :
+                                       REL_WHEEL, a4->delayed_value);
+                       return 1;
+               }
+       }
+
+       if ((a4->quirks & A4_2WHEEL_MOUSE_HACK_7) && usage->hid == 0x00090007) {
+               a4->hw_wheel = !!value;
+               return 1;
+       }
+
+       if (usage->code == REL_WHEEL && a4->hw_wheel) {
+               input_event(input, usage->type, REL_HWHEEL, value);
+               return 1;
+       }
+
+       return 0;
+}
+
+static int a4_probe(struct hid_device *hdev, const struct hid_device_id *id)
+{
+       struct a4tech_sc *a4;
+       int ret;
+
+       a4 = kzalloc(sizeof(*a4), GFP_KERNEL);
+       if (a4 == NULL) {
+               dev_err(&hdev->dev, "can't alloc device descriptor\n");
+               ret = -ENOMEM;
+               goto err_free;
+       }
+
+       a4->quirks = id->driver_data;
+
+       hid_set_drvdata(hdev, a4);
+
+       ret = hid_parse(hdev);
+       if (ret) {
+               dev_err(&hdev->dev, "parse failed\n");
+               goto err_free;
+       }
+
+       ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
+       if (ret) {
+               dev_err(&hdev->dev, "hw start failed\n");
+               goto err_free;
+       }
+
+       return 0;
+err_free:
+       kfree(a4);
+       return ret;
+}
+
+static void a4_remove(struct hid_device *hdev)
+{
+       struct a4tech_sc *a4 = hid_get_drvdata(hdev);
+
+       hid_hw_stop(hdev);
+       kfree(a4);
+}
+
+static const struct hid_device_id a4_devices[] = {
+       { HID_USB_DEVICE(USB_VENDOR_ID_A4TECH, USB_DEVICE_ID_A4TECH_WCP32PU),
+               .driver_data = A4_2WHEEL_MOUSE_HACK_7 },
+       { HID_USB_DEVICE(USB_VENDOR_ID_A4TECH, USB_DEVICE_ID_A4TECH_X5_005D),
+               .driver_data = A4_2WHEEL_MOUSE_HACK_B8 },
+       { }
+};
+MODULE_DEVICE_TABLE(hid, a4_devices);
+
+static struct hid_driver a4_driver = {
+       .name = "a4tech",
+       .id_table = a4_devices,
+       .input_mapped = a4_input_mapped,
+       .event = a4_event,
+       .probe = a4_probe,
+       .remove = a4_remove,
+};
+
+static int a4_init(void)
+{
+       return hid_register_driver(&a4_driver);
+}
+
+static void a4_exit(void)
+{
+       hid_unregister_driver(&a4_driver);
+}
+
+module_init(a4_init);
+module_exit(a4_exit);
+MODULE_LICENSE("GPL");
+
+HID_COMPAT_LOAD_DRIVER(a4tech);
diff --git a/drivers/hid/hid-apple.c b/drivers/hid/hid-apple.c
new file mode 100644 (file)
index 0000000..fd7f896
--- /dev/null
@@ -0,0 +1,484 @@
+/*
+ *  USB HID quirks support for Linux
+ *
+ *  Copyright (c) 1999 Andreas Gal
+ *  Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
+ *  Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
+ *  Copyright (c) 2006-2007 Jiri Kosina
+ *  Copyright (c) 2007 Paul Walmsley
+ *  Copyright (c) 2008 Jiri Slaby <jirislaby@gmail.com>
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ */
+
+#include <linux/device.h>
+#include <linux/hid.h>
+#include <linux/module.h>
+#include <linux/usb.h>
+
+#include "hid-ids.h"
+
+#define APPLE_RDESC_JIS                0x0001
+#define APPLE_IGNORE_MOUSE     0x0002
+#define APPLE_HAS_FN           0x0004
+#define APPLE_HIDDEV           0x0008
+#define APPLE_ISO_KEYBOARD     0x0010
+#define APPLE_MIGHTYMOUSE      0x0020
+#define APPLE_INVERT_HWHEEL    0x0040
+#define APPLE_IGNORE_HIDINPUT  0x0080
+#define APPLE_NUMLOCK_EMULATION        0x0100
+
+#define APPLE_FLAG_FKEY                0x01
+
+static unsigned int fnmode = 1;
+module_param(fnmode, uint, 0644);
+MODULE_PARM_DESC(fnmode, "Mode of fn key on Apple keyboards (0 = disabled, "
+               "[1] = fkeyslast, 2 = fkeysfirst)");
+
+struct apple_sc {
+       unsigned long quirks;
+       unsigned int fn_on;
+       DECLARE_BITMAP(pressed_fn, KEY_CNT);
+       DECLARE_BITMAP(pressed_numlock, KEY_CNT);
+};
+
+struct apple_key_translation {
+       u16 from;
+       u16 to;
+       u8 flags;
+};
+
+static struct apple_key_translation apple_fn_keys[] = {
+       { KEY_BACKSPACE, KEY_DELETE },
+       { KEY_F1,       KEY_BRIGHTNESSDOWN, APPLE_FLAG_FKEY },
+       { KEY_F2,       KEY_BRIGHTNESSUP,   APPLE_FLAG_FKEY },
+       { KEY_F3,       KEY_FN_F5,          APPLE_FLAG_FKEY }, /* Exposé */
+       { KEY_F4,       KEY_FN_F4,          APPLE_FLAG_FKEY }, /* Dashboard */
+       { KEY_F5,       KEY_KBDILLUMDOWN,   APPLE_FLAG_FKEY },
+       { KEY_F6,       KEY_KBDILLUMUP,     APPLE_FLAG_FKEY },
+       { KEY_F7,       KEY_PREVIOUSSONG,   APPLE_FLAG_FKEY },
+       { KEY_F8,       KEY_PLAYPAUSE,      APPLE_FLAG_FKEY },
+       { KEY_F9,       KEY_NEXTSONG,       APPLE_FLAG_FKEY },
+       { KEY_F10,      KEY_MUTE,           APPLE_FLAG_FKEY },
+       { KEY_F11,      KEY_VOLUMEDOWN,     APPLE_FLAG_FKEY },
+       { KEY_F12,      KEY_VOLUMEUP,       APPLE_FLAG_FKEY },
+       { KEY_UP,       KEY_PAGEUP },
+       { KEY_DOWN,     KEY_PAGEDOWN },
+       { KEY_LEFT,     KEY_HOME },
+       { KEY_RIGHT,    KEY_END },
+       { }
+};
+
+static struct apple_key_translation powerbook_fn_keys[] = {
+       { KEY_BACKSPACE, KEY_DELETE },
+       { KEY_F1,       KEY_BRIGHTNESSDOWN,     APPLE_FLAG_FKEY },
+       { KEY_F2,       KEY_BRIGHTNESSUP,       APPLE_FLAG_FKEY },
+       { KEY_F3,       KEY_MUTE,               APPLE_FLAG_FKEY },
+       { KEY_F4,       KEY_VOLUMEDOWN,         APPLE_FLAG_FKEY },
+       { KEY_F5,       KEY_VOLUMEUP,           APPLE_FLAG_FKEY },
+       { KEY_F6,       KEY_NUMLOCK,            APPLE_FLAG_FKEY },
+       { KEY_F7,       KEY_SWITCHVIDEOMODE,    APPLE_FLAG_FKEY },
+       { KEY_F8,       KEY_KBDILLUMTOGGLE,     APPLE_FLAG_FKEY },
+       { KEY_F9,       KEY_KBDILLUMDOWN,       APPLE_FLAG_FKEY },
+       { KEY_F10,      KEY_KBDILLUMUP,         APPLE_FLAG_FKEY },
+       { KEY_UP,       KEY_PAGEUP },
+       { KEY_DOWN,     KEY_PAGEDOWN },
+       { KEY_LEFT,     KEY_HOME },
+       { KEY_RIGHT,    KEY_END },
+       { }
+};
+
+static struct apple_key_translation powerbook_numlock_keys[] = {
+       { KEY_J,        KEY_KP1 },
+       { KEY_K,        KEY_KP2 },
+       { KEY_L,        KEY_KP3 },
+       { KEY_U,        KEY_KP4 },
+       { KEY_I,        KEY_KP5 },
+       { KEY_O,        KEY_KP6 },
+       { KEY_7,        KEY_KP7 },
+       { KEY_8,        KEY_KP8 },
+       { KEY_9,        KEY_KP9 },
+       { KEY_M,        KEY_KP0 },
+       { KEY_DOT,      KEY_KPDOT },
+       { KEY_SLASH,    KEY_KPPLUS },
+       { KEY_SEMICOLON, KEY_KPMINUS },
+       { KEY_P,        KEY_KPASTERISK },
+       { KEY_MINUS,    KEY_KPEQUAL },
+       { KEY_0,        KEY_KPSLASH },
+       { KEY_F6,       KEY_NUMLOCK },
+       { KEY_KPENTER,  KEY_KPENTER },
+       { KEY_BACKSPACE, KEY_BACKSPACE },
+       { }
+};
+
+static struct apple_key_translation apple_iso_keyboard[] = {
+       { KEY_GRAVE,    KEY_102ND },
+       { KEY_102ND,    KEY_GRAVE },
+       { }
+};
+
+static struct apple_key_translation *apple_find_translation(
+               struct apple_key_translation *table, u16 from)
+{
+       struct apple_key_translation *trans;
+
+       /* Look for the translation */
+       for (trans = table; trans->from; trans++)
+               if (trans->from == from)
+                       return trans;
+
+       return NULL;
+}
+
+static int hidinput_apple_event(struct hid_device *hid, struct input_dev *input,
+               struct hid_usage *usage, __s32 value)
+{
+       struct apple_sc *asc = hid_get_drvdata(hid);
+       struct apple_key_translation *trans;
+
+       if (usage->code == KEY_FN) {
+               asc->fn_on = !!value;
+               input_event(input, usage->type, usage->code, value);
+               return 1;
+       }
+
+       if (fnmode) {
+               int do_translate;
+
+               trans = apple_find_translation((hid->product < 0x220 ||
+                                       hid->product >= 0x300) ?
+                                       powerbook_fn_keys : apple_fn_keys,
+                                       usage->code);
+               if (trans) {
+                       if (test_bit(usage->code, asc->pressed_fn))
+                               do_translate = 1;
+                       else if (trans->flags & APPLE_FLAG_FKEY)
+                               do_translate = (fnmode == 2 && asc->fn_on) ||
+                                       (fnmode == 1 && !asc->fn_on);
+                       else
+                               do_translate = asc->fn_on;
+
+                       if (do_translate) {
+                               if (value)
+                                       set_bit(usage->code, asc->pressed_fn);
+                               else
+                                       clear_bit(usage->code, asc->pressed_fn);
+
+                               input_event(input, usage->type, trans->to,
+                                               value);
+
+                               return 1;
+                       }
+               }
+
+               if (asc->quirks & APPLE_NUMLOCK_EMULATION &&
+                               (test_bit(usage->code, asc->pressed_numlock) ||
+                               test_bit(LED_NUML, input->led))) {
+                       trans = apple_find_translation(powerbook_numlock_keys,
+                                       usage->code);
+
+                       if (trans) {
+                               if (value)
+                                       set_bit(usage->code,
+                                                       asc->pressed_numlock);
+                               else
+                                       clear_bit(usage->code,
+                                                       asc->pressed_numlock);
+
+                               input_event(input, usage->type, trans->to,
+                                               value);
+                       }
+
+                       return 1;
+               }
+       }
+
+       if (asc->quirks & APPLE_ISO_KEYBOARD) {
+               trans = apple_find_translation(apple_iso_keyboard, usage->code);
+               if (trans) {
+                       input_event(input, usage->type, trans->to, value);
+                       return 1;
+               }
+       }
+
+       return 0;
+}
+
+static int apple_event(struct hid_device *hdev, struct hid_field *field,
+               struct hid_usage *usage, __s32 value)
+{
+       struct apple_sc *asc = hid_get_drvdata(hdev);
+
+       if (!(hdev->claimed & HID_CLAIMED_INPUT) || !field->hidinput ||
+                       !usage->type)
+               return 0;
+
+       if ((asc->quirks & APPLE_INVERT_HWHEEL) &&
+                       usage->code == REL_HWHEEL) {
+               input_event(field->hidinput->input, usage->type, usage->code,
+                               -value);
+               return 1;
+       }
+
+       if ((asc->quirks & APPLE_HAS_FN) &&
+                       hidinput_apple_event(hdev, field->hidinput->input,
+                               usage, value))
+               return 1;
+
+
+       return 0;
+}
+
+/*
+ * MacBook JIS keyboard has wrong logical maximum
+ */
+static void apple_report_fixup(struct hid_device *hdev, __u8 *rdesc,
+               unsigned int rsize)
+{
+       struct apple_sc *asc = hid_get_drvdata(hdev);
+
+       if ((asc->quirks & APPLE_RDESC_JIS) && rsize >= 60 &&
+                       rdesc[53] == 0x65 && rdesc[59] == 0x65) {
+               dev_info(&hdev->dev, "fixing up MacBook JIS keyboard report "
+                               "descriptor\n");
+               rdesc[53] = rdesc[59] = 0xe7;
+       }
+}
+
+static void apple_setup_input(struct input_dev *input)
+{
+       struct apple_key_translation *trans;
+
+       set_bit(KEY_NUMLOCK, input->keybit);
+
+       /* Enable all needed keys */
+       for (trans = apple_fn_keys; trans->from; trans++)
+               set_bit(trans->to, input->keybit);
+
+       for (trans = powerbook_fn_keys; trans->from; trans++)
+               set_bit(trans->to, input->keybit);
+
+       for (trans = powerbook_numlock_keys; trans->from; trans++)
+               set_bit(trans->to, input->keybit);
+
+       for (trans = apple_iso_keyboard; trans->from; trans++)
+               set_bit(trans->to, input->keybit);
+}
+
+static int apple_input_mapping(struct hid_device *hdev, struct hid_input *hi,
+               struct hid_field *field, struct hid_usage *usage,
+               unsigned long **bit, int *max)
+{
+       if (usage->hid == (HID_UP_CUSTOM | 0x0003)) {
+               /* The fn key on Apple USB keyboards */
+               set_bit(EV_REP, hi->input->evbit);
+               hid_map_usage_clear(hi, usage, bit, max, EV_KEY, KEY_FN);
+               apple_setup_input(hi->input);
+               return 1;
+       }
+
+       /* we want the hid layer to go through standard path (set and ignore) */
+       return 0;
+}
+
+static int apple_input_mapped(struct hid_device *hdev, struct hid_input *hi,
+               struct hid_field *field, struct hid_usage *usage,
+               unsigned long **bit, int *max)
+{
+       struct apple_sc *asc = hid_get_drvdata(hdev);
+
+       if (asc->quirks & APPLE_MIGHTYMOUSE) {
+               if (usage->hid == HID_GD_Z)
+                       hid_map_usage(hi, usage, bit, max, EV_REL, REL_HWHEEL);
+               else if (usage->code == BTN_1)
+                       hid_map_usage(hi, usage, bit, max, EV_KEY, BTN_2);
+               else if (usage->code == BTN_2)
+                       hid_map_usage(hi, usage, bit, max, EV_KEY, BTN_1);
+       }
+
+       return 0;
+}
+
+static int apple_probe(struct hid_device *hdev,
+               const struct hid_device_id *id)
+{
+       unsigned long quirks = id->driver_data;
+       struct apple_sc *asc;
+       unsigned int connect_mask = HID_CONNECT_DEFAULT;
+       int ret;
+
+       /* return something else or move to hid layer? device will reside
+          allocated */
+       if (id->bus == BUS_USB && (quirks & APPLE_IGNORE_MOUSE) &&
+                       to_usb_interface(hdev->dev.parent)->cur_altsetting->
+                       desc.bInterfaceProtocol == USB_INTERFACE_PROTOCOL_MOUSE)
+               return -ENODEV;
+
+       asc = kzalloc(sizeof(*asc), GFP_KERNEL);
+       if (asc == NULL) {
+               dev_err(&hdev->dev, "can't alloc apple descriptor\n");
+               return -ENOMEM;
+       }
+
+       asc->quirks = quirks;
+
+       hid_set_drvdata(hdev, asc);
+
+       ret = hid_parse(hdev);
+       if (ret) {
+               dev_err(&hdev->dev, "parse failed\n");
+               goto err_free;
+       }
+
+       if (quirks & APPLE_HIDDEV)
+               connect_mask |= HID_CONNECT_HIDDEV_FORCE;
+       if (quirks & APPLE_IGNORE_HIDINPUT)
+               connect_mask &= ~HID_CONNECT_HIDINPUT;
+
+       ret = hid_hw_start(hdev, connect_mask);
+       if (ret) {
+               dev_err(&hdev->dev, "hw start failed\n");
+               goto err_free;
+       }
+
+       return 0;
+err_free:
+       kfree(asc);
+       return ret;
+}
+
+static void apple_remove(struct hid_device *hdev)
+{
+       hid_hw_stop(hdev);
+       kfree(hid_get_drvdata(hdev));
+}
+
+static const struct hid_device_id apple_devices[] = {
+       { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ATV_IRCONTROL),
+               .driver_data = APPLE_HIDDEV | APPLE_IGNORE_HIDINPUT },
+       { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL4),
+               .driver_data = APPLE_HIDDEV | APPLE_IGNORE_HIDINPUT },
+       { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MIGHTYMOUSE),
+               .driver_data = APPLE_MIGHTYMOUSE | APPLE_INVERT_HWHEEL },
+
+       { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_FOUNTAIN_ANSI),
+               .driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN |
+                       APPLE_IGNORE_MOUSE },
+       { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_FOUNTAIN_ISO),
+               .driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN |
+                       APPLE_IGNORE_MOUSE },
+       { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER_ANSI),
+               .driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN |
+                       APPLE_IGNORE_MOUSE },
+       { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER_ISO),
+               .driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN |
+                       APPLE_IGNORE_MOUSE | APPLE_ISO_KEYBOARD },
+       { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER_JIS),
+               .driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN |
+                       APPLE_IGNORE_MOUSE },
+       { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER3_ANSI),
+               .driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN |
+                       APPLE_IGNORE_MOUSE },
+       { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER3_ISO),
+               .driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN |
+                       APPLE_IGNORE_MOUSE | APPLE_ISO_KEYBOARD },
+       { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER3_JIS),
+               .driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN |
+                       APPLE_IGNORE_MOUSE | APPLE_RDESC_JIS },
+       { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_ANSI),
+               .driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN |
+                       APPLE_IGNORE_MOUSE },
+       { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_ISO),
+               .driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN |
+                       APPLE_IGNORE_MOUSE | APPLE_ISO_KEYBOARD },
+       { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_JIS),
+               .driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN |
+                       APPLE_IGNORE_MOUSE | APPLE_RDESC_JIS},
+       { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_ANSI),
+               .driver_data = APPLE_HAS_FN },
+       { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_ISO),
+               .driver_data = APPLE_HAS_FN | APPLE_ISO_KEYBOARD },
+       { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_JIS),
+               .driver_data = APPLE_HAS_FN },
+       { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_HF_ANSI),
+               .driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN |
+                       APPLE_IGNORE_MOUSE },
+       { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_HF_ISO),
+               .driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN |
+                       APPLE_IGNORE_MOUSE },
+       { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_HF_JIS),
+               .driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN |
+                       APPLE_IGNORE_MOUSE | APPLE_RDESC_JIS },
+       { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_ANSI),
+               .driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN },
+       { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_ISO),
+               .driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN |
+                       APPLE_ISO_KEYBOARD },
+       { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_JIS),
+               .driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN },
+       { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING_ANSI),
+               .driver_data = APPLE_HAS_FN | APPLE_IGNORE_MOUSE },
+       { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING_ISO),
+               .driver_data = APPLE_HAS_FN | APPLE_ISO_KEYBOARD |
+                       APPLE_IGNORE_MOUSE },
+       { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING_JIS),
+               .driver_data = APPLE_HAS_FN | APPLE_IGNORE_MOUSE | APPLE_RDESC_JIS },
+       { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING2_ANSI),
+               .driver_data = APPLE_HAS_FN | APPLE_IGNORE_MOUSE },
+       { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING2_ISO),
+               .driver_data = APPLE_HAS_FN | APPLE_ISO_KEYBOARD |
+                       APPLE_IGNORE_MOUSE },
+       { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING2_JIS),
+               .driver_data = APPLE_HAS_FN | APPLE_IGNORE_MOUSE | APPLE_RDESC_JIS },
+       { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_FOUNTAIN_TP_ONLY),
+               .driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN |
+                       APPLE_IGNORE_MOUSE },
+       { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER1_TP_ONLY),
+               .driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN |
+                       APPLE_IGNORE_MOUSE },
+
+       /* Apple wireless Mighty Mouse */
+       { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, 0x030c),
+               .driver_data = APPLE_MIGHTYMOUSE | APPLE_INVERT_HWHEEL },
+
+       { }
+};
+MODULE_DEVICE_TABLE(hid, apple_devices);
+
+static struct hid_driver apple_driver = {
+       .name = "apple",
+       .id_table = apple_devices,
+       .report_fixup = apple_report_fixup,
+       .probe = apple_probe,
+       .remove = apple_remove,
+       .event = apple_event,
+       .input_mapping = apple_input_mapping,
+       .input_mapped = apple_input_mapped,
+};
+
+static int apple_init(void)
+{
+       int ret;
+
+       ret = hid_register_driver(&apple_driver);
+       if (ret)
+               printk(KERN_ERR "can't register apple driver\n");
+
+       return ret;
+}
+
+static void apple_exit(void)
+{
+       hid_unregister_driver(&apple_driver);
+}
+
+module_init(apple_init);
+module_exit(apple_exit);
+MODULE_LICENSE("GPL");
+
+HID_COMPAT_LOAD_DRIVER(apple);
diff --git a/drivers/hid/hid-belkin.c b/drivers/hid/hid-belkin.c
new file mode 100644 (file)
index 0000000..12c8a9b
--- /dev/null
@@ -0,0 +1,105 @@
+/*
+ *  HID driver for some belkin "special" devices
+ *
+ *  Copyright (c) 1999 Andreas Gal
+ *  Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
+ *  Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
+ *  Copyright (c) 2006-2007 Jiri Kosina
+ *  Copyright (c) 2007 Paul Walmsley
+ *  Copyright (c) 2008 Jiri Slaby
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ */
+
+#include <linux/device.h>
+#include <linux/hid.h>
+#include <linux/module.h>
+
+#include "hid-ids.h"
+
+#define BELKIN_HIDDEV  0x01
+#define BELKIN_WKBD    0x02
+
+#define belkin_map_key_clear(c)        hid_map_usage_clear(hi, usage, bit, max, \
+                                       EV_KEY, (c))
+static int belkin_input_mapping(struct hid_device *hdev, struct hid_input *hi,
+               struct hid_field *field, struct hid_usage *usage,
+               unsigned long **bit, int *max)
+{
+       unsigned long quirks = (unsigned long)hid_get_drvdata(hdev);
+
+       if ((usage->hid & HID_USAGE_PAGE) != HID_UP_CONSUMER ||
+                       !(quirks & BELKIN_WKBD))
+               return 0;
+
+       switch (usage->hid & HID_USAGE) {
+       case 0x03a: belkin_map_key_clear(KEY_SOUND);            break;
+       case 0x03b: belkin_map_key_clear(KEY_CAMERA);           break;
+       case 0x03c: belkin_map_key_clear(KEY_DOCUMENTS);        break;
+       default:
+               return 0;
+       }
+       return 1;
+}
+
+static int belkin_probe(struct hid_device *hdev, const struct hid_device_id *id)
+{
+       unsigned long quirks = id->driver_data;
+       int ret;
+
+       hid_set_drvdata(hdev, (void *)quirks);
+
+       ret = hid_parse(hdev);
+       if (ret) {
+               dev_err(&hdev->dev, "parse failed\n");
+               goto err_free;
+       }
+
+       ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT |
+               ((quirks & BELKIN_HIDDEV) ? HID_CONNECT_HIDDEV_FORCE : 0));
+       if (ret) {
+               dev_err(&hdev->dev, "hw start failed\n");
+               goto err_free;
+       }
+
+       return 0;
+err_free:
+       return ret;
+}
+
+static const struct hid_device_id belkin_devices[] = {
+       { HID_USB_DEVICE(USB_VENDOR_ID_BELKIN, USB_DEVICE_ID_FLIP_KVM),
+               .driver_data = BELKIN_HIDDEV },
+       { HID_USB_DEVICE(USB_VENDOR_ID_LABTEC, USB_DEVICE_ID_LABTEC_WIRELESS_KEYBOARD),
+               .driver_data = BELKIN_WKBD },
+       { }
+};
+MODULE_DEVICE_TABLE(hid, belkin_devices);
+
+static struct hid_driver belkin_driver = {
+       .name = "belkin",
+       .id_table = belkin_devices,
+       .input_mapping = belkin_input_mapping,
+       .probe = belkin_probe,
+};
+
+static int belkin_init(void)
+{
+       return hid_register_driver(&belkin_driver);
+}
+
+static void belkin_exit(void)
+{
+       hid_unregister_driver(&belkin_driver);
+}
+
+module_init(belkin_init);
+module_exit(belkin_exit);
+MODULE_LICENSE("GPL");
+
+HID_COMPAT_LOAD_DRIVER(belkin);
diff --git a/drivers/hid/hid-bright.c b/drivers/hid/hid-bright.c
new file mode 100644 (file)
index 0000000..38517a1
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+ *  HID driver for some bright "special" devices
+ *
+ *  Copyright (c) 2008 Mauro Carvalho Chehab <mchehab@redhat.com>
+ *
+ * Based on hid-dell driver
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ */
+
+#include <linux/device.h>
+#include <linux/hid.h>
+#include <linux/module.h>
+
+#include "hid-ids.h"
+
+static int bright_probe(struct hid_device *hdev, const struct hid_device_id *id)
+{
+       int ret;
+
+       ret = hid_parse(hdev);
+       if (ret) {
+               dev_err(&hdev->dev, "parse failed\n");
+               goto err_free;
+       }
+
+       ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
+       if (ret) {
+               dev_err(&hdev->dev, "hw start failed\n");
+               goto err_free;
+       }
+
+       usbhid_set_leds(hdev);
+
+       return 0;
+err_free:
+       return ret;
+}
+
+static const struct hid_device_id bright_devices[] = {
+       { HID_USB_DEVICE(USB_VENDOR_ID_BRIGHT, USB_DEVICE_ID_BRIGHT_ABNT2) },
+       { }
+};
+MODULE_DEVICE_TABLE(hid, bright_devices);
+
+static struct hid_driver bright_driver = {
+       .name = "bright",
+       .id_table = bright_devices,
+       .probe = bright_probe,
+};
+
+static int bright_init(void)
+{
+       return hid_register_driver(&bright_driver);
+}
+
+static void bright_exit(void)
+{
+       hid_unregister_driver(&bright_driver);
+}
+
+module_init(bright_init);
+module_exit(bright_exit);
+MODULE_LICENSE("GPL");
+
+HID_COMPAT_LOAD_DRIVER(bright);
diff --git a/drivers/hid/hid-cherry.c b/drivers/hid/hid-cherry.c
new file mode 100644 (file)
index 0000000..b833b97
--- /dev/null
@@ -0,0 +1,87 @@
+/*
+ *  HID driver for some cherry "special" devices
+ *
+ *  Copyright (c) 1999 Andreas Gal
+ *  Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
+ *  Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
+ *  Copyright (c) 2006-2007 Jiri Kosina
+ *  Copyright (c) 2007 Paul Walmsley
+ *  Copyright (c) 2008 Jiri Slaby
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ */
+
+#include <linux/device.h>
+#include <linux/hid.h>
+#include <linux/module.h>
+
+#include "hid-ids.h"
+
+/*
+ * Cherry Cymotion keyboard have an invalid HID report descriptor,
+ * that needs fixing before we can parse it.
+ */
+static void ch_report_fixup(struct hid_device *hdev, __u8 *rdesc,
+               unsigned int rsize)
+{
+       if (rsize >= 17 && rdesc[11] == 0x3c && rdesc[12] == 0x02) {
+               dev_info(&hdev->dev, "fixing up Cherry Cymotion report "
+                               "descriptor\n");
+               rdesc[11] = rdesc[16] = 0xff;
+               rdesc[12] = rdesc[17] = 0x03;
+       }
+}
+
+#define ch_map_key_clear(c)    hid_map_usage_clear(hi, usage, bit, max, \
+                                       EV_KEY, (c))
+static int ch_input_mapping(struct hid_device *hdev, struct hid_input *hi,
+               struct hid_field *field, struct hid_usage *usage,
+               unsigned long **bit, int *max)
+{
+       if ((usage->hid & HID_USAGE_PAGE) != HID_UP_CONSUMER)
+               return 0;
+
+       switch (usage->hid & HID_USAGE) {
+       case 0x301: ch_map_key_clear(KEY_PROG1);        break;
+       case 0x302: ch_map_key_clear(KEY_PROG2);        break;
+       case 0x303: ch_map_key_clear(KEY_PROG3);        break;
+       default:
+               return 0;
+       }
+
+       return 1;
+}
+
+static const struct hid_device_id ch_devices[] = {
+       { HID_USB_DEVICE(USB_VENDOR_ID_CHERRY, USB_DEVICE_ID_CHERRY_CYMOTION) },
+       { }
+};
+MODULE_DEVICE_TABLE(hid, ch_devices);
+
+static struct hid_driver ch_driver = {
+       .name = "cherry",
+       .id_table = ch_devices,
+       .report_fixup = ch_report_fixup,
+       .input_mapping = ch_input_mapping,
+};
+
+static int ch_init(void)
+{
+       return hid_register_driver(&ch_driver);
+}
+
+static void ch_exit(void)
+{
+       hid_unregister_driver(&ch_driver);
+}
+
+module_init(ch_init);
+module_exit(ch_exit);
+MODULE_LICENSE("GPL");
+
+HID_COMPAT_LOAD_DRIVER(cherry);
diff --git a/drivers/hid/hid-chicony.c b/drivers/hid/hid-chicony.c
new file mode 100644 (file)
index 0000000..a54d409
--- /dev/null
@@ -0,0 +1,80 @@
+/*
+ *  HID driver for some chicony "special" devices
+ *
+ *  Copyright (c) 1999 Andreas Gal
+ *  Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
+ *  Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
+ *  Copyright (c) 2006-2007 Jiri Kosina
+ *  Copyright (c) 2007 Paul Walmsley
+ *  Copyright (c) 2008 Jiri Slaby
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ */
+
+#include <linux/device.h>
+#include <linux/input.h>
+#include <linux/hid.h>
+#include <linux/module.h>
+
+#include "hid-ids.h"
+
+#define ch_map_key_clear(c)    hid_map_usage_clear(hi, usage, bit, max, \
+                                       EV_KEY, (c))
+static int ch_input_mapping(struct hid_device *hdev, struct hid_input *hi,
+               struct hid_field *field, struct hid_usage *usage,
+               unsigned long **bit, int *max)
+{
+       if ((usage->hid & HID_USAGE_PAGE) != HID_UP_MSVENDOR)
+               return 0;
+
+       set_bit(EV_REP, hi->input->evbit);
+       switch (usage->hid & HID_USAGE) {
+       case 0xff01: ch_map_key_clear(BTN_1);   break;
+       case 0xff02: ch_map_key_clear(BTN_2);   break;
+       case 0xff03: ch_map_key_clear(BTN_3);   break;
+       case 0xff04: ch_map_key_clear(BTN_4);   break;
+       case 0xff05: ch_map_key_clear(BTN_5);   break;
+       case 0xff06: ch_map_key_clear(BTN_6);   break;
+       case 0xff07: ch_map_key_clear(BTN_7);   break;
+       case 0xff08: ch_map_key_clear(BTN_8);   break;
+       case 0xff09: ch_map_key_clear(BTN_9);   break;
+       case 0xff0a: ch_map_key_clear(BTN_A);   break;
+       case 0xff0b: ch_map_key_clear(BTN_B);   break;
+       default:
+               return 0;
+       }
+       return 1;
+}
+
+static const struct hid_device_id ch_devices[] = {
+       { HID_USB_DEVICE(USB_VENDOR_ID_CHICONY, USB_DEVICE_ID_CHICONY_TACTICAL_PAD) },
+       { }
+};
+MODULE_DEVICE_TABLE(hid, ch_devices);
+
+static struct hid_driver ch_driver = {
+       .name = "chicony",
+       .id_table = ch_devices,
+       .input_mapping = ch_input_mapping,
+};
+
+static int ch_init(void)
+{
+       return hid_register_driver(&ch_driver);
+}
+
+static void ch_exit(void)
+{
+       hid_unregister_driver(&ch_driver);
+}
+
+module_init(ch_init);
+module_exit(ch_exit);
+MODULE_LICENSE("GPL");
+
+HID_COMPAT_LOAD_DRIVER(chicony);
index 426ac5add585c03df6dbdae3c8b2baca175ef043..8a7d9dbb4d07244b631458e024cbfe6fe21b39c6 100644 (file)
@@ -33,6 +33,8 @@
 #include <linux/hid-debug.h>
 #include <linux/hidraw.h>
 
+#include "hid-ids.h"
+
 /*
  * Version Information
  */
@@ -268,9 +270,9 @@ static int hid_add_field(struct hid_parser *parser, unsigned report_type, unsign
 static u32 item_udata(struct hid_item *item)
 {
        switch (item->size) {
-               case 1: return item->data.u8;
-               case 2: return item->data.u16;
-               case 4: return item->data.u32;
+       case 1: return item->data.u8;
+       case 2: return item->data.u16;
+       case 4: return item->data.u32;
        }
        return 0;
 }
@@ -278,9 +280,9 @@ static u32 item_udata(struct hid_item *item)
 static s32 item_sdata(struct hid_item *item)
 {
        switch (item->size) {
-               case 1: return item->data.s8;
-               case 2: return item->data.s16;
-               case 4: return item->data.s32;
+       case 1: return item->data.s8;
+       case 2: return item->data.s16;
+       case 4: return item->data.s32;
        }
        return 0;
 }
@@ -292,87 +294,91 @@ static s32 item_sdata(struct hid_item *item)
 static int hid_parser_global(struct hid_parser *parser, struct hid_item *item)
 {
        switch (item->tag) {
+       case HID_GLOBAL_ITEM_TAG_PUSH:
 
-               case HID_GLOBAL_ITEM_TAG_PUSH:
-
-                       if (parser->global_stack_ptr == HID_GLOBAL_STACK_SIZE) {
-                               dbg_hid("global enviroment stack overflow\n");
-                               return -1;
-                       }
+               if (parser->global_stack_ptr == HID_GLOBAL_STACK_SIZE) {
+                       dbg_hid("global enviroment stack overflow\n");
+                       return -1;
+               }
 
-                       memcpy(parser->global_stack + parser->global_stack_ptr++,
-                               &parser->global, sizeof(struct hid_global));
-                       return 0;
+               memcpy(parser->global_stack + parser->global_stack_ptr++,
+                       &parser->global, sizeof(struct hid_global));
+               return 0;
 
-               case HID_GLOBAL_ITEM_TAG_POP:
+       case HID_GLOBAL_ITEM_TAG_POP:
 
-                       if (!parser->global_stack_ptr) {
-                               dbg_hid("global enviroment stack underflow\n");
-                               return -1;
-                       }
-
-                       memcpy(&parser->global, parser->global_stack + --parser->global_stack_ptr,
-                               sizeof(struct hid_global));
-                       return 0;
+               if (!parser->global_stack_ptr) {
+                       dbg_hid("global enviroment stack underflow\n");
+                       return -1;
+               }
 
-               case HID_GLOBAL_ITEM_TAG_USAGE_PAGE:
-                       parser->global.usage_page = item_udata(item);
-                       return 0;
+               memcpy(&parser->global, parser->global_stack +
+                       --parser->global_stack_ptr, sizeof(struct hid_global));
+               return 0;
 
-               case HID_GLOBAL_ITEM_TAG_LOGICAL_MINIMUM:
-                       parser->global.logical_minimum = item_sdata(item);
-                       return 0;
+       case HID_GLOBAL_ITEM_TAG_USAGE_PAGE:
+               parser->global.usage_page = item_udata(item);
+               return 0;
 
-               case HID_GLOBAL_ITEM_TAG_LOGICAL_MAXIMUM:
-                       if (parser->global.logical_minimum < 0)
-                               parser->global.logical_maximum = item_sdata(item);
-                       else
-                               parser->global.logical_maximum = item_udata(item);
-                       return 0;
+       case HID_GLOBAL_ITEM_TAG_LOGICAL_MINIMUM:
+               parser->global.logical_minimum = item_sdata(item);
+               return 0;
 
-               case HID_GLOBAL_ITEM_TAG_PHYSICAL_MINIMUM:
-                       parser->global.physical_minimum = item_sdata(item);
-                       return 0;
+       case HID_GLOBAL_ITEM_TAG_LOGICAL_MAXIMUM:
+               if (parser->global.logical_minimum < 0)
+                       parser->global.logical_maximum = item_sdata(item);
+               else
+                       parser->global.logical_maximum = item_udata(item);
+               return 0;
 
-               case HID_GLOBAL_ITEM_TAG_PHYSICAL_MAXIMUM:
-                       if (parser->global.physical_minimum < 0)
-                               parser->global.physical_maximum = item_sdata(item);
-                       else
-                               parser->global.physical_maximum = item_udata(item);
-                       return 0;
+       case HID_GLOBAL_ITEM_TAG_PHYSICAL_MINIMUM:
+               parser->global.physical_minimum = item_sdata(item);
+               return 0;
 
-               case HID_GLOBAL_ITEM_TAG_UNIT_EXPONENT:
-                       parser->global.unit_exponent = item_sdata(item);
-                       return 0;
+       case HID_GLOBAL_ITEM_TAG_PHYSICAL_MAXIMUM:
+               if (parser->global.physical_minimum < 0)
+                       parser->global.physical_maximum = item_sdata(item);
+               else
+                       parser->global.physical_maximum = item_udata(item);
+               return 0;
 
-               case HID_GLOBAL_ITEM_TAG_UNIT:
-                       parser->global.unit = item_udata(item);
-                       return 0;
+       case HID_GLOBAL_ITEM_TAG_UNIT_EXPONENT:
+               parser->global.unit_exponent = item_sdata(item);
+               return 0;
 
-               case HID_GLOBAL_ITEM_TAG_REPORT_SIZE:
-                       if ((parser->global.report_size = item_udata(item)) > 32) {
-                               dbg_hid("invalid report_size %d\n", parser->global.report_size);
-                               return -1;
-                       }
-                       return 0;
+       case HID_GLOBAL_ITEM_TAG_UNIT:
+               parser->global.unit = item_udata(item);
+               return 0;
 
-               case HID_GLOBAL_ITEM_TAG_REPORT_COUNT:
-                       if ((parser->global.report_count = item_udata(item)) > HID_MAX_USAGES) {
-                               dbg_hid("invalid report_count %d\n", parser->global.report_count);
-                               return -1;
-                       }
-                       return 0;
+       case HID_GLOBAL_ITEM_TAG_REPORT_SIZE:
+               parser->global.report_size = item_udata(item);
+               if (parser->global.report_size > 32) {
+                       dbg_hid("invalid report_size %d\n",
+                                       parser->global.report_size);
+                       return -1;
+               }
+               return 0;
 
-               case HID_GLOBAL_ITEM_TAG_REPORT_ID:
-                       if ((parser->global.report_id = item_udata(item)) == 0) {
-                               dbg_hid("report_id 0 is invalid\n");
-                               return -1;
-                       }
-                       return 0;
+       case HID_GLOBAL_ITEM_TAG_REPORT_COUNT:
+               parser->global.report_count = item_udata(item);
+               if (parser->global.report_count > HID_MAX_USAGES) {
+                       dbg_hid("invalid report_count %d\n",
+                                       parser->global.report_count);
+                       return -1;
+               }
+               return 0;
 
-               default:
-                       dbg_hid("unknown global tag 0x%x\n", item->tag);
+       case HID_GLOBAL_ITEM_TAG_REPORT_ID:
+               parser->global.report_id = item_udata(item);
+               if (parser->global.report_id == 0) {
+                       dbg_hid("report_id 0 is invalid\n");
                        return -1;
+               }
+               return 0;
+
+       default:
+               dbg_hid("unknown global tag 0x%x\n", item->tag);
+               return -1;
        }
 }
 
@@ -393,77 +399,76 @@ static int hid_parser_local(struct hid_parser *parser, struct hid_item *item)
        data = item_udata(item);
 
        switch (item->tag) {
-
-               case HID_LOCAL_ITEM_TAG_DELIMITER:
-
-                       if (data) {
-                               /*
-                                * We treat items before the first delimiter
-                                * as global to all usage sets (branch 0).
-                                * In the moment we process only these global
-                                * items and the first delimiter set.
-                                */
-                               if (parser->local.delimiter_depth != 0) {
-                                       dbg_hid("nested delimiters\n");
-                                       return -1;
-                               }
-                               parser->local.delimiter_depth++;
-                               parser->local.delimiter_branch++;
-                       } else {
-                               if (parser->local.delimiter_depth < 1) {
-                                       dbg_hid("bogus close delimiter\n");
-                                       return -1;
-                               }
-                               parser->local.delimiter_depth--;
+       case HID_LOCAL_ITEM_TAG_DELIMITER:
+
+               if (data) {
+                       /*
+                        * We treat items before the first delimiter
+                        * as global to all usage sets (branch 0).
+                        * In the moment we process only these global
+                        * items and the first delimiter set.
+                        */
+                       if (parser->local.delimiter_depth != 0) {
+                               dbg_hid("nested delimiters\n");
+                               return -1;
                        }
-                       return 1;
-
-               case HID_LOCAL_ITEM_TAG_USAGE:
-
-                       if (parser->local.delimiter_branch > 1) {
-                               dbg_hid("alternative usage ignored\n");
-                               return 0;
+                       parser->local.delimiter_depth++;
+                       parser->local.delimiter_branch++;
+               } else {
+                       if (parser->local.delimiter_depth < 1) {
+                               dbg_hid("bogus close delimiter\n");
+                               return -1;
                        }
+                       parser->local.delimiter_depth--;
+               }
+               return 1;
 
-                       if (item->size <= 2)
-                               data = (parser->global.usage_page << 16) + data;
+       case HID_LOCAL_ITEM_TAG_USAGE:
 
-                       return hid_add_usage(parser, data);
+               if (parser->local.delimiter_branch > 1) {
+                       dbg_hid("alternative usage ignored\n");
+                       return 0;
+               }
 
-               case HID_LOCAL_ITEM_TAG_USAGE_MINIMUM:
+               if (item->size <= 2)
+                       data = (parser->global.usage_page << 16) + data;
 
-                       if (parser->local.delimiter_branch > 1) {
-                               dbg_hid("alternative usage ignored\n");
-                               return 0;
-                       }
+               return hid_add_usage(parser, data);
 
-                       if (item->size <= 2)
-                               data = (parser->global.usage_page << 16) + data;
+       case HID_LOCAL_ITEM_TAG_USAGE_MINIMUM:
 
-                       parser->local.usage_minimum = data;
+               if (parser->local.delimiter_branch > 1) {
+                       dbg_hid("alternative usage ignored\n");
                        return 0;
+               }
 
-               case HID_LOCAL_ITEM_TAG_USAGE_MAXIMUM:
+               if (item->size <= 2)
+                       data = (parser->global.usage_page << 16) + data;
 
-                       if (parser->local.delimiter_branch > 1) {
-                               dbg_hid("alternative usage ignored\n");
-                               return 0;
-                       }
+               parser->local.usage_minimum = data;
+               return 0;
 
-                       if (item->size <= 2)
-                               data = (parser->global.usage_page << 16) + data;
+       case HID_LOCAL_ITEM_TAG_USAGE_MAXIMUM:
 
-                       for (n = parser->local.usage_minimum; n <= data; n++)
-                               if (hid_add_usage(parser, n)) {
-                                       dbg_hid("hid_add_usage failed\n");
-                                       return -1;
-                               }
+               if (parser->local.delimiter_branch > 1) {
+                       dbg_hid("alternative usage ignored\n");
                        return 0;
+               }
 
-               default:
+               if (item->size <= 2)
+                       data = (parser->global.usage_page << 16) + data;
 
-                       dbg_hid("unknown local item tag 0x%x\n", item->tag);
-                       return 0;
+               for (n = parser->local.usage_minimum; n <= data; n++)
+                       if (hid_add_usage(parser, n)) {
+                               dbg_hid("hid_add_usage failed\n");
+                               return -1;
+                       }
+               return 0;
+
+       default:
+
+               dbg_hid("unknown local item tag 0x%x\n", item->tag);
+               return 0;
        }
        return 0;
 }
@@ -480,24 +485,24 @@ static int hid_parser_main(struct hid_parser *parser, struct hid_item *item)
        data = item_udata(item);
 
        switch (item->tag) {
-               case HID_MAIN_ITEM_TAG_BEGIN_COLLECTION:
-                       ret = open_collection(parser, data & 0xff);
-                       break;
-               case HID_MAIN_ITEM_TAG_END_COLLECTION:
-                       ret = close_collection(parser);
-                       break;
-               case HID_MAIN_ITEM_TAG_INPUT:
-                       ret = hid_add_field(parser, HID_INPUT_REPORT, data);
-                       break;
-               case HID_MAIN_ITEM_TAG_OUTPUT:
-                       ret = hid_add_field(parser, HID_OUTPUT_REPORT, data);
-                       break;
-               case HID_MAIN_ITEM_TAG_FEATURE:
-                       ret = hid_add_field(parser, HID_FEATURE_REPORT, data);
-                       break;
-               default:
-                       dbg_hid("unknown main item tag 0x%x\n", item->tag);
-                       ret = 0;
+       case HID_MAIN_ITEM_TAG_BEGIN_COLLECTION:
+               ret = open_collection(parser, data & 0xff);
+               break;
+       case HID_MAIN_ITEM_TAG_END_COLLECTION:
+               ret = close_collection(parser);
+               break;
+       case HID_MAIN_ITEM_TAG_INPUT:
+               ret = hid_add_field(parser, HID_INPUT_REPORT, data);
+               break;
+       case HID_MAIN_ITEM_TAG_OUTPUT:
+               ret = hid_add_field(parser, HID_OUTPUT_REPORT, data);
+               break;
+       case HID_MAIN_ITEM_TAG_FEATURE:
+               ret = hid_add_field(parser, HID_FEATURE_REPORT, data);
+               break;
+       default:
+               dbg_hid("unknown main item tag 0x%x\n", item->tag);
+               ret = 0;
        }
 
        memset(&parser->local, 0, sizeof(parser->local));       /* Reset the local parser environment */
@@ -534,9 +539,10 @@ static void hid_free_report(struct hid_report *report)
  * Free a device structure, all reports, and all fields.
  */
 
-void hid_free_device(struct hid_device *device)
+static void hid_device_release(struct device *dev)
 {
-       unsigned i,j;
+       struct hid_device *device = container_of(dev, struct hid_device, dev);
+       unsigned i, j;
 
        for (i = 0; i < HID_REPORT_TYPES; i++) {
                struct hid_report_enum *report_enum = device->report_enum + i;
@@ -552,7 +558,6 @@ void hid_free_device(struct hid_device *device)
        kfree(device->collection);
        kfree(device);
 }
-EXPORT_SYMBOL_GPL(hid_free_device);
 
 /*
  * Fetch a report description item from the data stream. We support long
@@ -593,47 +598,52 @@ static u8 *fetch_item(__u8 *start, __u8 *end, struct hid_item *item)
        item->size = b & 3;
 
        switch (item->size) {
+       case 0:
+               return start;
 
-               case 0:
-                       return start;
-
-               case 1:
-                       if ((end - start) < 1)
-                               return NULL;
-                       item->data.u8 = *start++;
-                       return start;
+       case 1:
+               if ((end - start) < 1)
+                       return NULL;
+               item->data.u8 = *start++;
+               return start;
 
-               case 2:
-                       if ((end - start) < 2)
-                               return NULL;
-                       item->data.u16 = get_unaligned_le16(start);
-                       start = (__u8 *)((__le16 *)start + 1);
-                       return start;
+       case 2:
+               if ((end - start) < 2)
+                       return NULL;
+               item->data.u16 = get_unaligned_le16(start);
+               start = (__u8 *)((__le16 *)start + 1);
+               return start;
 
-               case 3:
-                       item->size++;
-                       if ((end - start) < 4)
-                               return NULL;
-                       item->data.u32 = get_unaligned_le32(start);
-                       start = (__u8 *)((__le32 *)start + 1);
-                       return start;
+       case 3:
+               item->size++;
+               if ((end - start) < 4)
+                       return NULL;
+               item->data.u32 = get_unaligned_le32(start);
+               start = (__u8 *)((__le32 *)start + 1);
+               return start;
        }
 
        return NULL;
 }
 
-/*
+/**
+ * hid_parse_report - parse device report
+ *
+ * @device: hid device
+ * @start: report start
+ * @size: report size
+ *
  * Parse a report description into a hid_device structure. Reports are
  * enumerated, fields are attached to these reports.
+ * 0 returned on success, otherwise nonzero error value.
  */
-
-struct hid_device *hid_parse_report(__u8 *start, unsigned size)
+int hid_parse_report(struct hid_device *device, __u8 *start,
+               unsigned size)
 {
-       struct hid_device *device;
        struct hid_parser *parser;
        struct hid_item item;
        __u8 *end;
-       unsigned i;
+       int ret;
        static int (*dispatch_type[])(struct hid_parser *parser,
                                      struct hid_item *item) = {
                hid_parser_main,
@@ -642,76 +652,57 @@ struct hid_device *hid_parse_report(__u8 *start, unsigned size)
                hid_parser_reserved
        };
 
-       if (!(device = kzalloc(sizeof(struct hid_device), GFP_KERNEL)))
-               return NULL;
+       if (device->driver->report_fixup)
+               device->driver->report_fixup(device, start, size);
 
-       if (!(device->collection = kzalloc(sizeof(struct hid_collection) *
-                                  HID_DEFAULT_NUM_COLLECTIONS, GFP_KERNEL))) {
-               kfree(device);
-               return NULL;
-       }
-       device->collection_size = HID_DEFAULT_NUM_COLLECTIONS;
-
-       for (i = 0; i < HID_REPORT_TYPES; i++)
-               INIT_LIST_HEAD(&device->report_enum[i].report_list);
-
-       if (!(device->rdesc = kmalloc(size, GFP_KERNEL))) {
-               kfree(device->collection);
-               kfree(device);
-               return NULL;
-       }
+       device->rdesc = kmalloc(size, GFP_KERNEL);
+       if (device->rdesc == NULL)
+               return -ENOMEM;
        memcpy(device->rdesc, start, size);
        device->rsize = size;
 
-       if (!(parser = vmalloc(sizeof(struct hid_parser)))) {
-               kfree(device->rdesc);
-               kfree(device->collection);
-               kfree(device);
-               return NULL;
+       parser = vmalloc(sizeof(struct hid_parser));
+       if (!parser) {
+               ret = -ENOMEM;
+               goto err;
        }
+
        memset(parser, 0, sizeof(struct hid_parser));
        parser->device = device;
 
        end = start + size;
+       ret = -EINVAL;
        while ((start = fetch_item(start, end, &item)) != NULL) {
 
                if (item.format != HID_ITEM_FORMAT_SHORT) {
                        dbg_hid("unexpected long global item\n");
-                       hid_free_device(device);
-                       vfree(parser);
-                       return NULL;
+                       goto err;
                }
 
                if (dispatch_type[item.type](parser, &item)) {
                        dbg_hid("item %u %u %u %u parsing failed\n",
                                item.format, (unsigned)item.size, (unsigned)item.type, (unsigned)item.tag);
-                       hid_free_device(device);
-                       vfree(parser);
-                       return NULL;
+                       goto err;
                }
 
                if (start == end) {
                        if (parser->collection_stack_ptr) {
                                dbg_hid("unbalanced collection at end of report description\n");
-                               hid_free_device(device);
-                               vfree(parser);
-                               return NULL;
+                               goto err;
                        }
                        if (parser->local.delimiter_depth) {
                                dbg_hid("unbalanced delimiter at end of report description\n");
-                               hid_free_device(device);
-                               vfree(parser);
-                               return NULL;
+                               goto err;
                        }
                        vfree(parser);
-                       return device;
+                       return 0;
                }
        }
 
        dbg_hid("item fetching failed at offset %d\n", (int)(end - start));
-       hid_free_device(device);
+err:
        vfree(parser);
-       return NULL;
+       return ret;
 }
 EXPORT_SYMBOL_GPL(hid_parse_report);
 
@@ -724,9 +715,9 @@ EXPORT_SYMBOL_GPL(hid_parse_report);
 static s32 snto32(__u32 value, unsigned n)
 {
        switch (n) {
-               case 8:  return ((__s8)value);
-               case 16: return ((__s16)value);
-               case 32: return ((__s32)value);
+       case 8:  return ((__s8)value);
+       case 16: return ((__s16)value);
+       case 32: return ((__s32)value);
        }
        return value & (1 << (n - 1)) ? value | (-1 << n) : value;
 }
@@ -815,9 +806,73 @@ static __inline__ int search(__s32 *array, __s32 value, unsigned n)
        return -1;
 }
 
-static void hid_process_event(struct hid_device *hid, struct hid_field *field, struct hid_usage *usage, __s32 value, int interrupt)
+/**
+ * hid_match_report - check if driver's raw_event should be called
+ *
+ * @hid: hid device
+ * @report_type: type to match against
+ *
+ * compare hid->driver->report_table->report_type to report->type
+ */
+static int hid_match_report(struct hid_device *hid, struct hid_report *report)
+{
+       const struct hid_report_id *id = hid->driver->report_table;
+
+       if (!id) /* NULL means all */
+               return 1;
+
+       for (; id->report_type != HID_TERMINATOR; id++)
+               if (id->report_type == HID_ANY_ID ||
+                               id->report_type == report->type)
+                       return 1;
+       return 0;
+}
+
+/**
+ * hid_match_usage - check if driver's event should be called
+ *
+ * @hid: hid device
+ * @usage: usage to match against
+ *
+ * compare hid->driver->usage_table->usage_{type,code} to
+ * usage->usage_{type,code}
+ */
+static int hid_match_usage(struct hid_device *hid, struct hid_usage *usage)
 {
+       const struct hid_usage_id *id = hid->driver->usage_table;
+
+       if (!id) /* NULL means all */
+               return 1;
+
+       for (; id->usage_type != HID_ANY_ID - 1; id++)
+               if ((id->usage_hid == HID_ANY_ID ||
+                               id->usage_hid == usage->hid) &&
+                               (id->usage_type == HID_ANY_ID ||
+                               id->usage_type == usage->type) &&
+                               (id->usage_code == HID_ANY_ID ||
+                                id->usage_code == usage->code))
+                       return 1;
+       return 0;
+}
+
+static void hid_process_event(struct hid_device *hid, struct hid_field *field,
+               struct hid_usage *usage, __s32 value, int interrupt)
+{
+       struct hid_driver *hdrv = hid->driver;
+       int ret;
+
        hid_dump_input(usage, value);
+
+       if (hdrv && hdrv->event && hid_match_usage(hid, usage)) {
+               ret = hdrv->event(hid, field, usage, value);
+               if (ret != 0) {
+                       if (ret < 0)
+                               dbg_hid("%s's event failed with %d\n",
+                                               hdrv->name, ret);
+                       return;
+               }
+       }
+
        if (hid->claimed & HID_CLAIMED_INPUT)
                hidinput_hid_event(hid, field, usage, value);
        if (hid->claimed & HID_CLAIMED_HIDDEV && interrupt && hid->hiddev_hid_event)
@@ -946,44 +1001,47 @@ int hid_set_field(struct hid_field *field, unsigned offset, __s32 value)
 }
 EXPORT_SYMBOL_GPL(hid_set_field);
 
-int hid_input_report(struct hid_device *hid, int type, u8 *data, int size, int interrupt)
+static struct hid_report *hid_get_report(struct hid_report_enum *report_enum,
+               const u8 *data)
 {
-       struct hid_report_enum *report_enum = hid->report_enum + type;
        struct hid_report *report;
-       int n, rsize, i;
+       unsigned int n = 0;     /* Normally report number is 0 */
 
-       if (!hid)
-               return -ENODEV;
+       /* Device uses numbered reports, data[0] is report number */
+       if (report_enum->numbered)
+               n = *data;
 
-       if (!size) {
-               dbg_hid("empty report\n");
-               return -1;
-       }
+       report = report_enum->report_id_hash[n];
+       if (report == NULL)
+               dbg_hid("undefined report_id %u received\n", n);
 
-       dbg_hid("report (size %u) (%snumbered)\n", size, report_enum->numbered ? "" : "un");
+       return report;
+}
 
-       n = 0;                          /* Normally report number is 0 */
-       if (report_enum->numbered) {    /* Device uses numbered reports, data[0] is report number */
-               n = *data++;
-               size--;
-       }
+void hid_report_raw_event(struct hid_device *hid, int type, u8 *data, int size,
+               int interrupt)
+{
+       struct hid_report_enum *report_enum = hid->report_enum + type;
+       struct hid_report *report;
+       unsigned int a;
+       int rsize, csize = size;
+       u8 *cdata = data;
 
-       /* dump the report */
-       dbg_hid("report %d (size %u) = ", n, size);
-       for (i = 0; i < size; i++)
-               dbg_hid_line(" %02x", data[i]);
-       dbg_hid_line("\n");
+       report = hid_get_report(report_enum, data);
+       if (!report)
+               return;
 
-       if (!(report = report_enum->report_id_hash[n])) {
-               dbg_hid("undefined report_id %d received\n", n);
-               return -1;
+       if (report_enum->numbered) {
+               cdata++;
+               csize--;
        }
 
        rsize = ((report->size - 1) >> 3) + 1;
 
-       if (size < rsize) {
-               dbg_hid("report %d is too short, (%d < %d)\n", report->id, size, rsize);
-               memset(data + size, 0, rsize - size);
+       if (csize < rsize) {
+               dbg_hid("report %d is too short, (%d < %d)\n", report->id,
+                               csize, rsize);
+               memset(cdata + csize, 0, rsize - csize);
        }
 
        if ((hid->claimed & HID_CLAIMED_HIDDEV) && hid->hiddev_report_event)
@@ -996,24 +1054,661 @@ int hid_input_report(struct hid_device *hid, int type, u8 *data, int size, int i
                        hidraw_report_event(hid, data, size);
        }
 
-       for (n = 0; n < report->maxfield; n++)
-               hid_input_field(hid, report->field[n], data, interrupt);
+       for (a = 0; a < report->maxfield; a++)
+               hid_input_field(hid, report->field[a], cdata, interrupt);
 
        if (hid->claimed & HID_CLAIMED_INPUT)
                hidinput_report_event(hid, report);
+}
+EXPORT_SYMBOL_GPL(hid_report_raw_event);
+
+/**
+ * hid_input_report - report data from lower layer (usb, bt...)
+ *
+ * @hid: hid device
+ * @type: HID report type (HID_*_REPORT)
+ * @data: report contents
+ * @size: size of data parameter
+ * @interrupt: called from atomic?
+ *
+ * This is data entry for lower layers.
+ */
+int hid_input_report(struct hid_device *hid, int type, u8 *data, int size, int interrupt)
+{
+       struct hid_report_enum *report_enum = hid->report_enum + type;
+       struct hid_driver *hdrv = hid->driver;
+       struct hid_report *report;
+       unsigned int i;
+       int ret;
+
+       if (!hid || !hid->driver)
+               return -ENODEV;
+
+       if (!size) {
+               dbg_hid("empty report\n");
+               return -1;
+       }
+
+       dbg_hid("report (size %u) (%snumbered)\n", size, report_enum->numbered ? "" : "un");
+
+       report = hid_get_report(report_enum, data);
+       if (!report)
+               return -1;
+
+       /* dump the report */
+       dbg_hid("report %d (size %u) = ", report->id, size);
+       for (i = 0; i < size; i++)
+               dbg_hid_line(" %02x", data[i]);
+       dbg_hid_line("\n");
+
+       if (hdrv && hdrv->raw_event && hid_match_report(hid, report)) {
+               ret = hdrv->raw_event(hid, report, data, size);
+               if (ret != 0)
+                       return ret < 0 ? ret : 0;
+       }
+
+       hid_report_raw_event(hid, type, data, size, interrupt);
 
        return 0;
 }
 EXPORT_SYMBOL_GPL(hid_input_report);
 
+static bool hid_match_one_id(struct hid_device *hdev,
+               const struct hid_device_id *id)
+{
+       return id->bus == hdev->bus &&
+               (id->vendor == HID_ANY_ID || id->vendor == hdev->vendor) &&
+               (id->product == HID_ANY_ID || id->product == hdev->product);
+}
+
+static const struct hid_device_id *hid_match_id(struct hid_device *hdev,
+               const struct hid_device_id *id)
+{
+       for (; id->bus; id++)
+               if (hid_match_one_id(hdev, id))
+                       return id;
+
+       return NULL;
+}
+
+static const struct hid_device_id hid_hiddev_list[] = {
+       { HID_USB_DEVICE(USB_VENDOR_ID_MGE, USB_DEVICE_ID_MGE_UPS) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_MGE, USB_DEVICE_ID_MGE_UPS1) },
+       { }
+};
+
+static bool hid_hiddev(struct hid_device *hdev)
+{
+       return !!hid_match_id(hdev, hid_hiddev_list);
+}
+
+int hid_connect(struct hid_device *hdev, unsigned int connect_mask)
+{
+       static const char *types[] = { "Device", "Pointer", "Mouse", "Device",
+               "Joystick", "Gamepad", "Keyboard", "Keypad",
+               "Multi-Axis Controller"
+       };
+       const char *type, *bus;
+       char buf[64];
+       unsigned int i;
+       int len;
+
+       if (hdev->bus != BUS_USB)
+               connect_mask &= ~HID_CONNECT_HIDDEV;
+       if (hid_hiddev(hdev))
+               connect_mask |= HID_CONNECT_HIDDEV_FORCE;
+
+       if ((connect_mask & HID_CONNECT_HIDINPUT) && !hidinput_connect(hdev,
+                               connect_mask & HID_CONNECT_HIDINPUT_FORCE))
+               hdev->claimed |= HID_CLAIMED_INPUT;
+       if ((connect_mask & HID_CONNECT_HIDDEV) && hdev->hiddev_connect &&
+                       !hdev->hiddev_connect(hdev,
+                               connect_mask & HID_CONNECT_HIDDEV_FORCE))
+               hdev->claimed |= HID_CLAIMED_HIDDEV;
+       if ((connect_mask & HID_CONNECT_HIDRAW) && !hidraw_connect(hdev))
+               hdev->claimed |= HID_CLAIMED_HIDRAW;
+
+       if (!hdev->claimed) {
+               dev_err(&hdev->dev, "claimed by neither input, hiddev nor "
+                               "hidraw\n");
+               return -ENODEV;
+       }
+
+       if ((hdev->claimed & HID_CLAIMED_INPUT) &&
+                       (connect_mask & HID_CONNECT_FF) && hdev->ff_init)
+               hdev->ff_init(hdev);
+
+       len = 0;
+       if (hdev->claimed & HID_CLAIMED_INPUT)
+               len += sprintf(buf + len, "input");
+       if (hdev->claimed & HID_CLAIMED_HIDDEV)
+               len += sprintf(buf + len, "%shiddev%d", len ? "," : "",
+                               hdev->minor);
+       if (hdev->claimed & HID_CLAIMED_HIDRAW)
+               len += sprintf(buf + len, "%shidraw%d", len ? "," : "",
+                               ((struct hidraw *)hdev->hidraw)->minor);
+
+       type = "Device";
+       for (i = 0; i < hdev->maxcollection; i++) {
+               struct hid_collection *col = &hdev->collection[i];
+               if (col->type == HID_COLLECTION_APPLICATION &&
+                  (col->usage & HID_USAGE_PAGE) == HID_UP_GENDESK &&
+                  (col->usage & 0xffff) < ARRAY_SIZE(types)) {
+                       type = types[col->usage & 0xffff];
+                       break;
+               }
+       }
+
+       switch (hdev->bus) {
+       case BUS_USB:
+               bus = "USB";
+               break;
+       case BUS_BLUETOOTH:
+               bus = "BLUETOOTH";
+               break;
+       default:
+               bus = "<UNKNOWN>";
+       }
+
+       dev_info(&hdev->dev, "%s: %s HID v%x.%02x %s [%s] on %s\n",
+                       buf, bus, hdev->version >> 8, hdev->version & 0xff,
+                       type, hdev->name, hdev->phys);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(hid_connect);
+
+static const struct hid_device_id hid_blacklist[] = {
+       { HID_USB_DEVICE(USB_VENDOR_ID_A4TECH, USB_DEVICE_ID_A4TECH_WCP32PU) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_A4TECH, USB_DEVICE_ID_A4TECH_X5_005D) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ATV_IRCONTROL) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL4) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MIGHTYMOUSE) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_FOUNTAIN_ANSI) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_FOUNTAIN_ISO) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER_ANSI) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER_ISO) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER_JIS) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER3_ANSI) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER3_ISO) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER3_JIS) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_ANSI) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_ISO) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_JIS) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_ANSI) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_ISO) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_JIS) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_HF_ANSI) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_HF_ISO) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_HF_JIS) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_ANSI) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_ISO) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_JIS) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING_ANSI) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING_ISO) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING_JIS) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING2_ANSI) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING2_ISO) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING2_JIS) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_FOUNTAIN_TP_ONLY) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER1_TP_ONLY) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_AVERMEDIA, USB_DEVICE_ID_AVER_FM_MR800) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_BELKIN, USB_DEVICE_ID_FLIP_KVM) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_BRIGHT, USB_DEVICE_ID_BRIGHT_ABNT2) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_CHERRY, USB_DEVICE_ID_CHERRY_CYMOTION) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_CHICONY, USB_DEVICE_ID_CHICONY_TACTICAL_PAD) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_BARCODE_1) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_BARCODE_2) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_MOUSE) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_DELL, USB_DEVICE_ID_DELL_W7658) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_DELL, USB_DEVICE_ID_DELL_SK8115) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_EZKEY, USB_DEVICE_ID_BTC_8193) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_GYRATION, USB_DEVICE_ID_GYRATION_REMOTE) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_LABTEC, USB_DEVICE_ID_LABTEC_WIRELESS_KEYBOARD) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_MX3000_RECEIVER) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_S510_RECEIVER) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_S510_RECEIVER_2) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RECEIVER) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_DINOVO_DESKTOP) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_DINOVO_EDGE) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_DINOVO_MINI) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_KBD) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_ELITE_KBD) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_CORDLESS_DESKTOP_LX500) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_LX3) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_V150) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_EXTREME_3D) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WHEEL) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD2_2) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WINGMAN_F3D) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_FORCE3D_PRO) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_MOMO_WHEEL) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_MOMO_WHEEL2) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD2) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_SIDEWINDER_GV) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_NE4K) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_LK6K) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_PRESENTER_8K_USB) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_WIRELESS_OPTICAL_DESKTOP_3_0) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_MONTEREY, USB_DEVICE_ID_GENIUS_KB29E) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_PETALYNX, USB_DEVICE_ID_PETALYNX_MAXTER_REMOTE) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_SAMSUNG, USB_DEVICE_ID_SAMSUNG_IR_REMOTE) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS3_CONTROLLER) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_SUNPLUS, USB_DEVICE_ID_SUNPLUS_WDESKTOP) },
+
+       { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, 0x030c) },
+       { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_PRESENTER_8K_BT) },
+       { }
+};
+
+static int hid_bus_match(struct device *dev, struct device_driver *drv)
+{
+       struct hid_driver *hdrv = container_of(drv, struct hid_driver, driver);
+       struct hid_device *hdev = container_of(dev, struct hid_device, dev);
+
+       if (!hid_match_id(hdev, hdrv->id_table))
+               return 0;
+
+       /* generic wants all non-blacklisted */
+       if (!strncmp(hdrv->name, "generic-", 8))
+               return !hid_match_id(hdev, hid_blacklist);
+
+       return 1;
+}
+
+static int hid_device_probe(struct device *dev)
+{
+       struct hid_driver *hdrv = container_of(dev->driver,
+                       struct hid_driver, driver);
+       struct hid_device *hdev = container_of(dev, struct hid_device, dev);
+       const struct hid_device_id *id;
+       int ret = 0;
+
+       if (!hdev->driver) {
+               id = hid_match_id(hdev, hdrv->id_table);
+               if (id == NULL)
+                       return -ENODEV;
+
+               hdev->driver = hdrv;
+               if (hdrv->probe) {
+                       ret = hdrv->probe(hdev, id);
+               } else { /* default probe */
+                       ret = hid_parse(hdev);
+                       if (!ret)
+                               ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
+               }
+               if (ret)
+                       hdev->driver = NULL;
+       }
+       return ret;
+}
+
+static int hid_device_remove(struct device *dev)
+{
+       struct hid_device *hdev = container_of(dev, struct hid_device, dev);
+       struct hid_driver *hdrv = hdev->driver;
+
+       if (hdrv) {
+               if (hdrv->remove)
+                       hdrv->remove(hdev);
+               else /* default remove */
+                       hid_hw_stop(hdev);
+               hdev->driver = NULL;
+       }
+
+       return 0;
+}
+
+static int hid_uevent(struct device *dev, struct kobj_uevent_env *env)
+{
+       struct hid_device *hdev = container_of(dev, struct hid_device, dev);
+
+       if (add_uevent_var(env, "HID_ID=%04X:%08X:%08X",
+                       hdev->bus, hdev->vendor, hdev->product))
+               return -ENOMEM;
+
+       if (add_uevent_var(env, "HID_NAME=%s", hdev->name))
+               return -ENOMEM;
+
+       if (add_uevent_var(env, "HID_PHYS=%s", hdev->phys))
+               return -ENOMEM;
+
+       if (add_uevent_var(env, "HID_UNIQ=%s", hdev->uniq))
+               return -ENOMEM;
+
+       if (add_uevent_var(env, "MODALIAS=hid:b%04Xv%08Xp%08X",
+                       hdev->bus, hdev->vendor, hdev->product))
+               return -ENOMEM;
+
+       return 0;
+}
+
+static struct bus_type hid_bus_type = {
+       .name           = "hid",
+       .match          = hid_bus_match,
+       .probe          = hid_device_probe,
+       .remove         = hid_device_remove,
+       .uevent         = hid_uevent,
+};
+
+static const struct hid_device_id hid_ignore_list[] = {
+       { HID_USB_DEVICE(USB_VENDOR_ID_ACECAD, USB_DEVICE_ID_ACECAD_FLAIR) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_ACECAD, USB_DEVICE_ID_ACECAD_302) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_ADS_TECH, USB_DEVICE_ID_ADS_TECH_RADIO_SI470X) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_01) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_10) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_20) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_21) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_22) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_23) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_24) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_AIRCABLE, USB_DEVICE_ID_AIRCABLE1) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_ALCOR, USB_DEVICE_ID_ALCOR_USBRS232) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_ASUS, USB_DEVICE_ID_ASUS_LCM)},
+       { HID_USB_DEVICE(USB_VENDOR_ID_BERKSHIRE, USB_DEVICE_ID_BERKSHIRE_PCWD) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_CIDC, 0x0103) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_CYGNAL, USB_DEVICE_ID_CYGNAL_RADIO_SI470X) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_CMEDIA, USB_DEVICE_ID_CM109) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_HIDCOM) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_ULTRAMOUSE) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_DELORME, USB_DEVICE_ID_DELORME_EARTHMATE) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_DELORME, USB_DEVICE_ID_DELORME_EM_LT20) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_ESSENTIAL_REALITY, USB_DEVICE_ID_ESSENTIAL_REALITY_P5) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_GAMERON, USB_DEVICE_ID_GAMERON_DUAL_PSX_ADAPTOR) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_GENERAL_TOUCH, 0x0001) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_GENERAL_TOUCH, 0x0002) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_GENERAL_TOUCH, 0x0003) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_GENERAL_TOUCH, 0x0004) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_GLAB, USB_DEVICE_ID_4_PHIDGETSERVO_30) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_GLAB, USB_DEVICE_ID_1_PHIDGETSERVO_30) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_GLAB, USB_DEVICE_ID_0_0_4_IF_KIT) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_GLAB, USB_DEVICE_ID_0_16_16_IF_KIT) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_GLAB, USB_DEVICE_ID_8_8_8_IF_KIT) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_GLAB, USB_DEVICE_ID_0_8_7_IF_KIT) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_GLAB, USB_DEVICE_ID_0_8_8_IF_KIT) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_GLAB, USB_DEVICE_ID_PHIDGET_MOTORCONTROL) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_GOTOP, USB_DEVICE_ID_SUPER_Q2) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_GOTOP, USB_DEVICE_ID_GOGOPEN) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_GOTOP, USB_DEVICE_ID_PENPOWER) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_GREENASIA, 0x0003) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_GRETAGMACBETH, USB_DEVICE_ID_GRETAGMACBETH_HUEY) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_GRIFFIN, USB_DEVICE_ID_POWERMATE) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_GRIFFIN, USB_DEVICE_ID_SOUNDKNOB) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_90) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_100) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_101) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_103) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_104) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_105) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_106) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_107) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_108) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_200) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_201) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_202) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_203) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_204) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_205) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_206) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_207) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_300) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_301) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_302) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_303) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_304) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_305) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_306) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_307) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_308) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_309) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_400) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_401) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_402) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_403) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_404) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_405) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_500) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_501) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_502) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_503) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_504) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_1000) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_1001) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_1002) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_1003) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_1004) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_1005) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_1006) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_1007) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_IMATION, USB_DEVICE_ID_DISC_STAKKA) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_KBGEAR, USB_DEVICE_ID_KBGEAR_JAMSTUDIO) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_GPEN_560) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_CASSY) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_POCKETCASSY) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_MOBILECASSY) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_JWM) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_DMMP) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_UMIP) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_XRAY1) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_XRAY2) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_VIDEOCOM) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_COM3LAB) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_TELEPORT) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_NETWORKANALYSER) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_POWERCONTROL) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_MACHINETEST) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_MCC, USB_DEVICE_ID_MCC_PMD1024LS) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_MCC, USB_DEVICE_ID_MCC_PMD1208LS) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_MICROCHIP, USB_DEVICE_ID_PICKIT1) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_MICROCHIP, USB_DEVICE_ID_PICKIT2) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_NATIONAL_SEMICONDUCTOR, USB_DEVICE_ID_N_S_HARMONY) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 20) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 30) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 100) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 108) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 118) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 200) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 300) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 400) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 500) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_PANJIT, 0x0001) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_PANJIT, 0x0002) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_PANJIT, 0x0003) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_PANJIT, 0x0004) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_SOUNDGRAPH, USB_DEVICE_ID_SOUNDGRAPH_IMON_LCD) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_TENX, USB_DEVICE_ID_TENX_IBUDDY1) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_TENX, USB_DEVICE_ID_TENX_IBUDDY2) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb300) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb304) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb651) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb654) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_LABPRO) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_GOTEMP) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_SKIP) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_CYCLOPS) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_LCSPEC) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_WACOM, HID_ANY_ID) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_4_PHIDGETSERVO_20) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_1_PHIDGETSERVO_20) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_8_8_4_IF_KIT) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_YEALINK, USB_DEVICE_ID_YEALINK_P1K_P4K_B2K) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_ZEROPLUS, 0x0005) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_ZEROPLUS, 0x0030) },
+       { }
+};
+
+static bool hid_ignore(struct hid_device *hdev)
+{
+       switch (hdev->vendor) {
+       case USB_VENDOR_ID_CODEMERCS:
+               /* ignore all Code Mercenaries IOWarrior devices */
+               if (hdev->product >= USB_DEVICE_ID_CODEMERCS_IOW_FIRST &&
+                               hdev->product <= USB_DEVICE_ID_CODEMERCS_IOW_LAST)
+                       return true;
+               break;
+       case USB_VENDOR_ID_LOGITECH:
+               if (hdev->product >= USB_DEVICE_ID_LOGITECH_HARMONY_FIRST &&
+                               hdev->product <= USB_DEVICE_ID_LOGITECH_HARMONY_LAST)
+                       return true;
+               break;
+       }
+
+       return !!hid_match_id(hdev, hid_ignore_list);
+}
+
+int hid_add_device(struct hid_device *hdev)
+{
+       static atomic_t id = ATOMIC_INIT(0);
+       int ret;
+
+       if (WARN_ON(hdev->status & HID_STAT_ADDED))
+               return -EBUSY;
+
+       /* we need to kill them here, otherwise they will stay allocated to
+        * wait for coming driver */
+       if (hid_ignore(hdev))
+               return -ENODEV;
+
+       /* XXX hack, any other cleaner solution < 20 bus_id bytes? */
+       sprintf(hdev->dev.bus_id, "%04X:%04X:%04X.%04X", hdev->bus,
+                       hdev->vendor, hdev->product, atomic_inc_return(&id));
+
+       ret = device_add(&hdev->dev);
+       if (!ret)
+               hdev->status |= HID_STAT_ADDED;
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(hid_add_device);
+
+/**
+ * hid_allocate_device - allocate new hid device descriptor
+ *
+ * Allocate and initialize hid device, so that hid_destroy_device might be
+ * used to free it.
+ *
+ * New hid_device pointer is returned on success, otherwise ERR_PTR encoded
+ * error value.
+ */
+struct hid_device *hid_allocate_device(void)
+{
+       struct hid_device *hdev;
+       unsigned int i;
+       int ret = -ENOMEM;
+
+       hdev = kzalloc(sizeof(*hdev), GFP_KERNEL);
+       if (hdev == NULL)
+               return ERR_PTR(ret);
+
+       device_initialize(&hdev->dev);
+       hdev->dev.release = hid_device_release;
+       hdev->dev.bus = &hid_bus_type;
+
+       hdev->collection = kcalloc(HID_DEFAULT_NUM_COLLECTIONS,
+                       sizeof(struct hid_collection), GFP_KERNEL);
+       if (hdev->collection == NULL)
+               goto err;
+       hdev->collection_size = HID_DEFAULT_NUM_COLLECTIONS;
+
+       for (i = 0; i < HID_REPORT_TYPES; i++)
+               INIT_LIST_HEAD(&hdev->report_enum[i].report_list);
+
+       return hdev;
+err:
+       put_device(&hdev->dev);
+       return ERR_PTR(ret);
+}
+EXPORT_SYMBOL_GPL(hid_allocate_device);
+
+static void hid_remove_device(struct hid_device *hdev)
+{
+       if (hdev->status & HID_STAT_ADDED) {
+               device_del(&hdev->dev);
+               hdev->status &= ~HID_STAT_ADDED;
+       }
+}
+
+/**
+ * hid_destroy_device - free previously allocated device
+ *
+ * @hdev: hid device
+ *
+ * If you allocate hid_device through hid_allocate_device, you should ever
+ * free by this function.
+ */
+void hid_destroy_device(struct hid_device *hdev)
+{
+       hid_remove_device(hdev);
+       put_device(&hdev->dev);
+}
+EXPORT_SYMBOL_GPL(hid_destroy_device);
+
+int __hid_register_driver(struct hid_driver *hdrv, struct module *owner,
+               const char *mod_name)
+{
+       hdrv->driver.name = hdrv->name;
+       hdrv->driver.bus = &hid_bus_type;
+       hdrv->driver.owner = owner;
+       hdrv->driver.mod_name = mod_name;
+
+       return driver_register(&hdrv->driver);
+}
+EXPORT_SYMBOL_GPL(__hid_register_driver);
+
+void hid_unregister_driver(struct hid_driver *hdrv)
+{
+       driver_unregister(&hdrv->driver);
+}
+EXPORT_SYMBOL_GPL(hid_unregister_driver);
+
+#ifdef CONFIG_HID_COMPAT
+static void hid_compat_load(struct work_struct *ws)
+{
+       request_module("hid-dummy");
+}
+static DECLARE_WORK(hid_compat_work, hid_compat_load);
+static struct workqueue_struct *hid_compat_wq;
+#endif
+
 static int __init hid_init(void)
 {
-       return hidraw_init();
+       int ret;
+
+       ret = bus_register(&hid_bus_type);
+       if (ret) {
+               printk(KERN_ERR "HID: can't register hid bus\n");
+               goto err;
+       }
+
+       ret = hidraw_init();
+       if (ret)
+               goto err_bus;
+
+#ifdef CONFIG_HID_COMPAT
+       hid_compat_wq = create_workqueue("hid_compat");
+       if (!hid_compat_wq) {
+               hidraw_exit();
+               goto err;
+       }
+       queue_work(hid_compat_wq, &hid_compat_work);
+#endif
+
+       return 0;
+err_bus:
+       bus_unregister(&hid_bus_type);
+err:
+       return ret;
 }
 
 static void __exit hid_exit(void)
 {
+#ifdef CONFIG_HID_COMPAT
+       destroy_workqueue(hid_compat_wq);
+#endif
        hidraw_exit();
+       bus_unregister(&hid_bus_type);
 }
 
 module_init(hid_init);
diff --git a/drivers/hid/hid-cypress.c b/drivers/hid/hid-cypress.c
new file mode 100644 (file)
index 0000000..5d69d27
--- /dev/null
@@ -0,0 +1,158 @@
+/*
+ *  HID driver for some cypress "special" devices
+ *
+ *  Copyright (c) 1999 Andreas Gal
+ *  Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
+ *  Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
+ *  Copyright (c) 2006-2007 Jiri Kosina
+ *  Copyright (c) 2007 Paul Walmsley
+ *  Copyright (c) 2008 Jiri Slaby
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ */
+
+#include <linux/device.h>
+#include <linux/hid.h>
+#include <linux/input.h>
+#include <linux/module.h>
+
+#include "hid-ids.h"
+
+#define CP_RDESC_SWAPPED_MIN_MAX       0x01
+#define CP_2WHEEL_MOUSE_HACK           0x02
+#define CP_2WHEEL_MOUSE_HACK_ON                0x04
+
+/*
+ * Some USB barcode readers from cypress have usage min and usage max in
+ * the wrong order
+ */
+static void cp_report_fixup(struct hid_device *hdev, __u8 *rdesc,
+               unsigned int rsize)
+{
+       unsigned long quirks = (unsigned long)hid_get_drvdata(hdev);
+       unsigned int i;
+
+       if (!(quirks & CP_RDESC_SWAPPED_MIN_MAX))
+               return;
+
+       for (i = 0; i < rsize - 4; i++)
+               if (rdesc[i] == 0x29 && rdesc[i + 2] == 0x19) {
+                       __u8 tmp;
+
+                       rdesc[i] = 0x19;
+                       rdesc[i + 2] = 0x29;
+                       tmp = rdesc[i + 3];
+                       rdesc[i + 3] = rdesc[i + 1];
+                       rdesc[i + 1] = tmp;
+               }
+}
+
+static int cp_input_mapped(struct hid_device *hdev, struct hid_input *hi,
+               struct hid_field *field, struct hid_usage *usage,
+               unsigned long **bit, int *max)
+{
+       unsigned long quirks = (unsigned long)hid_get_drvdata(hdev);
+
+       if (!(quirks & CP_2WHEEL_MOUSE_HACK))
+               return 0;
+
+       if (usage->type == EV_REL && usage->code == REL_WHEEL)
+               set_bit(REL_HWHEEL, *bit);
+       if (usage->hid == 0x00090005)
+               return -1;
+
+       return 0;
+}
+
+static int cp_event(struct hid_device *hdev, struct hid_field *field,
+               struct hid_usage *usage, __s32 value)
+{
+       unsigned long quirks = (unsigned long)hid_get_drvdata(hdev);
+
+       if (!(hdev->claimed & HID_CLAIMED_INPUT) || !field->hidinput ||
+                       !usage->type || !(quirks & CP_2WHEEL_MOUSE_HACK))
+               return 0;
+
+       if (usage->hid == 0x00090005) {
+               if (value)
+                       quirks |=  CP_2WHEEL_MOUSE_HACK_ON;
+               else
+                       quirks &= ~CP_2WHEEL_MOUSE_HACK_ON;
+               hid_set_drvdata(hdev, (void *)quirks);
+               return 1;
+       }
+
+       if (usage->code == REL_WHEEL && (quirks & CP_2WHEEL_MOUSE_HACK_ON)) {
+               struct input_dev *input = field->hidinput->input;
+
+               input_event(input, usage->type, REL_HWHEEL, value);
+               return 1;
+       }
+
+       return 0;
+}
+
+static int cp_probe(struct hid_device *hdev, const struct hid_device_id *id)
+{
+       unsigned long quirks = id->driver_data;
+       int ret;
+
+       hid_set_drvdata(hdev, (void *)quirks);
+
+       ret = hid_parse(hdev);
+       if (ret) {
+               dev_err(&hdev->dev, "parse failed\n");
+               goto err_free;
+       }
+
+       ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
+       if (ret) {
+               dev_err(&hdev->dev, "hw start failed\n");
+               goto err_free;
+       }
+
+       return 0;
+err_free:
+       return ret;
+}
+
+static const struct hid_device_id cp_devices[] = {
+       { HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_BARCODE_1),
+               .driver_data = CP_RDESC_SWAPPED_MIN_MAX },
+       { HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_BARCODE_2),
+               .driver_data = CP_RDESC_SWAPPED_MIN_MAX },
+       { HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_MOUSE),
+               .driver_data = CP_2WHEEL_MOUSE_HACK },
+       { }
+};
+MODULE_DEVICE_TABLE(hid, cp_devices);
+
+static struct hid_driver cp_driver = {
+       .name = "cypress",
+       .id_table = cp_devices,
+       .report_fixup = cp_report_fixup,
+       .input_mapped = cp_input_mapped,
+       .event = cp_event,
+       .probe = cp_probe,
+};
+
+static int cp_init(void)
+{
+       return hid_register_driver(&cp_driver);
+}
+
+static void cp_exit(void)
+{
+       hid_unregister_driver(&cp_driver);
+}
+
+module_init(cp_init);
+module_exit(cp_exit);
+MODULE_LICENSE("GPL");
+
+HID_COMPAT_LOAD_DRIVER(cypress);
diff --git a/drivers/hid/hid-dell.c b/drivers/hid/hid-dell.c
new file mode 100644 (file)
index 0000000..1a0d0df
--- /dev/null
@@ -0,0 +1,75 @@
+/*
+ *  HID driver for some dell "special" devices
+ *
+ *  Copyright (c) 1999 Andreas Gal
+ *  Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
+ *  Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
+ *  Copyright (c) 2006-2007 Jiri Kosina
+ *  Copyright (c) 2007 Paul Walmsley
+ *  Copyright (c) 2008 Jiri Slaby
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ */
+
+#include <linux/device.h>
+#include <linux/hid.h>
+#include <linux/module.h>
+
+#include "hid-ids.h"
+
+static int dell_probe(struct hid_device *hdev, const struct hid_device_id *id)
+{
+       int ret;
+
+       ret = hid_parse(hdev);
+       if (ret) {
+               dev_err(&hdev->dev, "parse failed\n");
+               goto err_free;
+       }
+
+       ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
+       if (ret) {
+               dev_err(&hdev->dev, "hw start failed\n");
+               goto err_free;
+       }
+
+       usbhid_set_leds(hdev);
+
+       return 0;
+err_free:
+       return ret;
+}
+
+static const struct hid_device_id dell_devices[] = {
+       { HID_USB_DEVICE(USB_VENDOR_ID_DELL, USB_DEVICE_ID_DELL_W7658) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_DELL, USB_DEVICE_ID_DELL_SK8115) },
+       { }
+};
+MODULE_DEVICE_TABLE(hid, dell_devices);
+
+static struct hid_driver dell_driver = {
+       .name = "dell",
+       .id_table = dell_devices,
+       .probe = dell_probe,
+};
+
+static int dell_init(void)
+{
+       return hid_register_driver(&dell_driver);
+}
+
+static void dell_exit(void)
+{
+       hid_unregister_driver(&dell_driver);
+}
+
+module_init(dell_init);
+module_exit(dell_exit);
+MODULE_LICENSE("GPL");
+
+HID_COMPAT_LOAD_DRIVER(dell);
diff --git a/drivers/hid/hid-dummy.c b/drivers/hid/hid-dummy.c
new file mode 100644 (file)
index 0000000..e148f86
--- /dev/null
@@ -0,0 +1,72 @@
+#include <linux/autoconf.h>
+#include <linux/module.h>
+#include <linux/hid.h>
+
+static int __init hid_dummy_init(void)
+{
+#ifdef CONFIG_HID_A4TECH_MODULE
+       HID_COMPAT_CALL_DRIVER(a4tech);
+#endif
+#ifdef CONFIG_HID_APPLE_MODULE
+       HID_COMPAT_CALL_DRIVER(apple);
+#endif
+#ifdef CONFIG_HID_BELKIN_MODULE
+       HID_COMPAT_CALL_DRIVER(belkin);
+#endif
+#ifdef CONFIG_HID_BRIGHT_MODULE
+       HID_COMPAT_CALL_DRIVER(bright);
+#endif
+#ifdef CONFIG_HID_CHERRY_MODULE
+       HID_COMPAT_CALL_DRIVER(cherry);
+#endif
+#ifdef CONFIG_HID_CHICONY_MODULE
+       HID_COMPAT_CALL_DRIVER(chicony);
+#endif
+#ifdef CONFIG_HID_CYPRESS_MODULE
+       HID_COMPAT_CALL_DRIVER(cypress);
+#endif
+#ifdef CONFIG_HID_DELL_MODULE
+       HID_COMPAT_CALL_DRIVER(dell);
+#endif
+#ifdef CONFIG_HID_EZKEY_MODULE
+       HID_COMPAT_CALL_DRIVER(ezkey);
+#endif
+#ifdef CONFIG_HID_GYRATION_MODULE
+       HID_COMPAT_CALL_DRIVER(gyration);
+#endif
+#ifdef CONFIG_HID_LOGITECH_MODULE
+       HID_COMPAT_CALL_DRIVER(logitech);
+#endif
+#ifdef CONFIG_HID_MICROSOFT_MODULE
+       HID_COMPAT_CALL_DRIVER(microsoft);
+#endif
+#ifdef CONFIG_HID_MONTEREY_MODULE
+       HID_COMPAT_CALL_DRIVER(monterey);
+#endif
+#ifdef CONFIG_HID_PANTHERLORD_MODULE
+       HID_COMPAT_CALL_DRIVER(pantherlord);
+#endif
+#ifdef CONFIG_HID_PETALYNX_MODULE
+       HID_COMPAT_CALL_DRIVER(petalynx);
+#endif
+#ifdef CONFIG_HID_SAMSUNG_MODULE
+       HID_COMPAT_CALL_DRIVER(samsung);
+#endif
+#ifdef CONFIG_HID_SONY_MODULE
+       HID_COMPAT_CALL_DRIVER(sony);
+#endif
+#ifdef CONFIG_HID_SUNPLUS_MODULE
+       HID_COMPAT_CALL_DRIVER(sunplus);
+#endif
+#ifdef CONFIG_THRUSTMASTER_FF_MODULE
+       HID_COMPAT_CALL_DRIVER(thrustmaster);
+#endif
+#ifdef CONFIG_ZEROPLUS_FF_MODULE
+       HID_COMPAT_CALL_DRIVER(zeroplus);
+#endif
+
+       return -EIO;
+}
+module_init(hid_dummy_init);
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/hid/hid-ezkey.c b/drivers/hid/hid-ezkey.c
new file mode 100644 (file)
index 0000000..deb42f9
--- /dev/null
@@ -0,0 +1,95 @@
+/*
+ *  HID driver for some ezkey "special" devices
+ *
+ *  Copyright (c) 1999 Andreas Gal
+ *  Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
+ *  Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
+ *  Copyright (c) 2006-2007 Jiri Kosina
+ *  Copyright (c) 2007 Paul Walmsley
+ *  Copyright (c) 2008 Jiri Slaby
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ */
+
+#include <linux/device.h>
+#include <linux/input.h>
+#include <linux/hid.h>
+#include <linux/module.h>
+
+#include "hid-ids.h"
+
+#define ez_map_rel(c)  hid_map_usage(hi, usage, bit, max, EV_REL, (c))
+#define ez_map_key(c)  hid_map_usage(hi, usage, bit, max, EV_KEY, (c))
+
+static int ez_input_mapping(struct hid_device *hdev, struct hid_input *hi,
+               struct hid_field *field, struct hid_usage *usage,
+               unsigned long **bit, int *max)
+{
+       if ((usage->hid & HID_USAGE_PAGE) != HID_UP_CONSUMER)
+               return 0;
+
+       switch (usage->hid & HID_USAGE) {
+       case 0x230: ez_map_key(BTN_MOUSE);      break;
+       case 0x231: ez_map_rel(REL_WHEEL);      break;
+       /*
+        * this keyboard has a scrollwheel implemented in
+        * totally broken way. We map this usage temporarily
+        * to HWHEEL and handle it in the event quirk handler
+        */
+       case 0x232: ez_map_rel(REL_HWHEEL);     break;
+       default:
+               return 0;
+       }
+       return 1;
+}
+
+static int ez_event(struct hid_device *hdev, struct hid_field *field,
+               struct hid_usage *usage, __s32 value)
+{
+       if (!(hdev->claimed & HID_CLAIMED_INPUT) || !field->hidinput ||
+                       !usage->type)
+               return 0;
+
+       /* handle the temporary quirky mapping to HWHEEL */
+       if (usage->type == EV_REL && usage->code == REL_HWHEEL) {
+               struct input_dev *input = field->hidinput->input;
+               input_event(input, usage->type, REL_WHEEL, -value);
+               return 1;
+       }
+
+       return 0;
+}
+
+static const struct hid_device_id ez_devices[] = {
+       { HID_USB_DEVICE(USB_VENDOR_ID_EZKEY, USB_DEVICE_ID_BTC_8193) },
+       { }
+};
+MODULE_DEVICE_TABLE(hid, ez_devices);
+
+static struct hid_driver ez_driver = {
+       .name = "ezkey",
+       .id_table = ez_devices,
+       .input_mapping = ez_input_mapping,
+       .event = ez_event,
+};
+
+static int ez_init(void)
+{
+       return hid_register_driver(&ez_driver);
+}
+
+static void ez_exit(void)
+{
+       hid_unregister_driver(&ez_driver);
+}
+
+module_init(ez_init);
+module_exit(ez_exit);
+MODULE_LICENSE("GPL");
+
+HID_COMPAT_LOAD_DRIVER(ezkey);
diff --git a/drivers/hid/hid-gyration.c b/drivers/hid/hid-gyration.c
new file mode 100644 (file)
index 0000000..ac5120f
--- /dev/null
@@ -0,0 +1,96 @@
+/*
+ *  HID driver for some gyration "special" devices
+ *
+ *  Copyright (c) 1999 Andreas Gal
+ *  Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
+ *  Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
+ *  Copyright (c) 2006-2007 Jiri Kosina
+ *  Copyright (c) 2007 Paul Walmsley
+ *  Copyright (c) 2008 Jiri Slaby
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ */
+
+#include <linux/device.h>
+#include <linux/input.h>
+#include <linux/hid.h>
+#include <linux/module.h>
+
+#include "hid-ids.h"
+
+#define gy_map_key_clear(c)    hid_map_usage_clear(hi, usage, bit, max, \
+                                       EV_KEY, (c))
+static int gyration_input_mapping(struct hid_device *hdev, struct hid_input *hi,
+               struct hid_field *field, struct hid_usage *usage,
+               unsigned long **bit, int *max)
+{
+       if ((usage->hid & HID_USAGE_PAGE) != HID_UP_LOGIVENDOR)
+               return 0;
+
+       set_bit(EV_REP, hi->input->evbit);
+       switch (usage->hid & HID_USAGE) {
+       /* Reported on Gyration MCE Remote */
+       case 0x00d: gy_map_key_clear(KEY_HOME);         break;
+       case 0x024: gy_map_key_clear(KEY_DVD);          break;
+       case 0x025: gy_map_key_clear(KEY_PVR);          break;
+       case 0x046: gy_map_key_clear(KEY_MEDIA);        break;
+       case 0x047: gy_map_key_clear(KEY_MP3);          break;
+       case 0x049: gy_map_key_clear(KEY_CAMERA);       break;
+       case 0x04a: gy_map_key_clear(KEY_VIDEO);        break;
+
+       default:
+               return 0;
+       }
+       return 1;
+}
+
+static int gyration_event(struct hid_device *hdev, struct hid_field *field,
+               struct hid_usage *usage, __s32 value)
+{
+       struct input_dev *input = field->hidinput->input;
+
+       if ((usage->hid & HID_USAGE_PAGE) == HID_UP_GENDESK &&
+                       (usage->hid & 0xff) == 0x82) {
+               input_event(input, usage->type, usage->code, 1);
+               input_sync(input);
+               input_event(input, usage->type, usage->code, 0);
+               input_sync(input);
+               return 1;
+       }
+
+       return 0;
+}
+
+static const struct hid_device_id gyration_devices[] = {
+       { HID_USB_DEVICE(USB_VENDOR_ID_GYRATION, USB_DEVICE_ID_GYRATION_REMOTE) },
+       { }
+};
+MODULE_DEVICE_TABLE(hid, gyration_devices);
+
+static struct hid_driver gyration_driver = {
+       .name = "gyration",
+       .id_table = gyration_devices,
+       .input_mapping = gyration_input_mapping,
+       .event = gyration_event,
+};
+
+static int gyration_init(void)
+{
+       return hid_register_driver(&gyration_driver);
+}
+
+static void gyration_exit(void)
+{
+       hid_unregister_driver(&gyration_driver);
+}
+
+module_init(gyration_init);
+module_exit(gyration_exit);
+MODULE_LICENSE("GPL");
+
+HID_COMPAT_LOAD_DRIVER(gyration);
diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
new file mode 100644 (file)
index 0000000..aad9ed1
--- /dev/null
@@ -0,0 +1,404 @@
+/*
+ *  USB HID quirks support for Linux
+ *
+ *  Copyright (c) 1999 Andreas Gal
+ *  Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
+ *  Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
+ *  Copyright (c) 2006-2007 Jiri Kosina
+ *  Copyright (c) 2007 Paul Walmsley
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ */
+
+#ifndef HID_IDS_H_FILE
+#define HID_IDS_H_FILE
+
+#define USB_VENDOR_ID_A4TECH           0x09da
+#define USB_DEVICE_ID_A4TECH_WCP32PU   0x0006
+#define USB_DEVICE_ID_A4TECH_X5_005D   0x000a
+
+#define USB_VENDOR_ID_AASHIMA          0x06d6
+#define USB_DEVICE_ID_AASHIMA_GAMEPAD  0x0025
+#define USB_DEVICE_ID_AASHIMA_PREDATOR 0x0026
+
+#define USB_VENDOR_ID_ACECAD           0x0460
+#define USB_DEVICE_ID_ACECAD_FLAIR     0x0004
+#define USB_DEVICE_ID_ACECAD_302       0x0008
+
+#define USB_VENDOR_ID_ADS_TECH                 0x06e1
+#define USB_DEVICE_ID_ADS_TECH_RADIO_SI470X    0xa155
+
+#define USB_VENDOR_ID_AFATECH          0x15a4
+#define USB_DEVICE_ID_AFATECH_AF9016   0x9016
+
+#define USB_VENDOR_ID_AIPTEK           0x08ca
+#define USB_DEVICE_ID_AIPTEK_01                0x0001
+#define USB_DEVICE_ID_AIPTEK_10                0x0010
+#define USB_DEVICE_ID_AIPTEK_20                0x0020
+#define USB_DEVICE_ID_AIPTEK_21                0x0021
+#define USB_DEVICE_ID_AIPTEK_22                0x0022
+#define USB_DEVICE_ID_AIPTEK_23                0x0023
+#define USB_DEVICE_ID_AIPTEK_24                0x0024
+
+#define USB_VENDOR_ID_AIRCABLE         0x16CA
+#define USB_DEVICE_ID_AIRCABLE1                0x1502
+
+#define USB_VENDOR_ID_ALCOR            0x058f
+#define USB_DEVICE_ID_ALCOR_USBRS232   0x9720
+
+#define USB_VENDOR_ID_ALPS             0x0433
+#define USB_DEVICE_ID_IBM_GAMEPAD      0x1101
+
+#define USB_VENDOR_ID_APPLE            0x05ac
+#define USB_DEVICE_ID_APPLE_MIGHTYMOUSE        0x0304
+#define USB_DEVICE_ID_APPLE_FOUNTAIN_ANSI      0x020e
+#define USB_DEVICE_ID_APPLE_FOUNTAIN_ISO       0x020f
+#define USB_DEVICE_ID_APPLE_GEYSER_ANSI        0x0214
+#define USB_DEVICE_ID_APPLE_GEYSER_ISO 0x0215
+#define USB_DEVICE_ID_APPLE_GEYSER_JIS 0x0216
+#define USB_DEVICE_ID_APPLE_GEYSER3_ANSI       0x0217
+#define USB_DEVICE_ID_APPLE_GEYSER3_ISO        0x0218
+#define USB_DEVICE_ID_APPLE_GEYSER3_JIS        0x0219
+#define USB_DEVICE_ID_APPLE_GEYSER4_ANSI       0x021a
+#define USB_DEVICE_ID_APPLE_GEYSER4_ISO        0x021b
+#define USB_DEVICE_ID_APPLE_GEYSER4_JIS        0x021c
+#define USB_DEVICE_ID_APPLE_ALU_ANSI   0x0220
+#define USB_DEVICE_ID_APPLE_ALU_ISO    0x0221
+#define USB_DEVICE_ID_APPLE_ALU_JIS    0x0222
+#define USB_DEVICE_ID_APPLE_WELLSPRING_ANSI    0x0223
+#define USB_DEVICE_ID_APPLE_WELLSPRING_ISO     0x0224
+#define USB_DEVICE_ID_APPLE_WELLSPRING_JIS     0x0225
+#define USB_DEVICE_ID_APPLE_GEYSER4_HF_ANSI    0x0229
+#define USB_DEVICE_ID_APPLE_GEYSER4_HF_ISO     0x022a
+#define USB_DEVICE_ID_APPLE_GEYSER4_HF_JIS     0x022b
+#define USB_DEVICE_ID_APPLE_ALU_WIRELESS_ANSI  0x022c
+#define USB_DEVICE_ID_APPLE_ALU_WIRELESS_ISO   0x022d
+#define USB_DEVICE_ID_APPLE_ALU_WIRELESS_JIS   0x022e
+#define USB_DEVICE_ID_APPLE_WELLSPRING2_ANSI   0x0230
+#define USB_DEVICE_ID_APPLE_WELLSPRING2_ISO    0x0231
+#define USB_DEVICE_ID_APPLE_WELLSPRING2_JIS    0x0232
+#define USB_DEVICE_ID_APPLE_FOUNTAIN_TP_ONLY   0x030a
+#define USB_DEVICE_ID_APPLE_GEYSER1_TP_ONLY    0x030b
+#define USB_DEVICE_ID_APPLE_ATV_IRCONTROL      0x8241
+#define USB_DEVICE_ID_APPLE_IRCONTROL4 0x8242
+
+#define USB_VENDOR_ID_ASUS             0x0b05
+#define USB_DEVICE_ID_ASUS_LCM         0x1726
+
+#define USB_VENDOR_ID_ATEN             0x0557
+#define USB_DEVICE_ID_ATEN_UC100KM     0x2004
+#define USB_DEVICE_ID_ATEN_CS124U      0x2202
+#define USB_DEVICE_ID_ATEN_2PORTKVM    0x2204
+#define USB_DEVICE_ID_ATEN_4PORTKVM    0x2205
+#define USB_DEVICE_ID_ATEN_4PORTKVMC   0x2208
+
+#define USB_VENDOR_ID_AVERMEDIA                0x07ca
+#define USB_DEVICE_ID_AVER_FM_MR800    0xb800
+
+#define USB_VENDOR_ID_BELKIN           0x050d
+#define USB_DEVICE_ID_FLIP_KVM         0x3201
+
+#define USB_VENDOR_ID_BRIGHT           0x1241
+#define USB_DEVICE_ID_BRIGHT_ABNT2     0x1503
+
+#define USB_VENDOR_ID_BERKSHIRE                0x0c98
+#define USB_DEVICE_ID_BERKSHIRE_PCWD   0x1140
+
+#define USB_VENDOR_ID_CHERRY           0x046a
+#define USB_DEVICE_ID_CHERRY_CYMOTION  0x0023
+
+#define USB_VENDOR_ID_CHIC             0x05fe
+#define USB_DEVICE_ID_CHIC_GAMEPAD     0x0014
+
+#define USB_VENDOR_ID_CHICONY          0x04f2
+#define USB_DEVICE_ID_CHICONY_TACTICAL_PAD     0x0418
+
+#define USB_VENDOR_ID_CIDC             0x1677
+
+#define USB_VENDOR_ID_CMEDIA           0x0d8c
+#define USB_DEVICE_ID_CM109            0x000e
+
+#define USB_VENDOR_ID_CODEMERCS                0x07c0
+#define USB_DEVICE_ID_CODEMERCS_IOW_FIRST      0x1500
+#define USB_DEVICE_ID_CODEMERCS_IOW_LAST       0x15ff
+
+#define USB_VENDOR_ID_CYGNAL           0x10c4
+#define USB_DEVICE_ID_CYGNAL_RADIO_SI470X      0x818a
+
+#define USB_VENDOR_ID_CYPRESS          0x04b4
+#define USB_DEVICE_ID_CYPRESS_MOUSE    0x0001
+#define USB_DEVICE_ID_CYPRESS_HIDCOM   0x5500
+#define USB_DEVICE_ID_CYPRESS_ULTRAMOUSE       0x7417
+#define USB_DEVICE_ID_CYPRESS_BARCODE_1        0xde61
+#define USB_DEVICE_ID_CYPRESS_BARCODE_2        0xde64
+
+#define USB_VENDOR_ID_DELL             0x413c
+#define USB_DEVICE_ID_DELL_W7658       0x2005
+#define USB_DEVICE_ID_DELL_SK8115      0x2105
+
+#define USB_VENDOR_ID_DELORME          0x1163
+#define USB_DEVICE_ID_DELORME_EARTHMATE 0x0100
+#define USB_DEVICE_ID_DELORME_EM_LT20  0x0200
+
+#define USB_VENDOR_ID_DMI              0x0c0b
+#define USB_DEVICE_ID_DMI_ENC          0x5fab
+
+#define USB_VENDOR_ID_ELO              0x04E7
+#define USB_DEVICE_ID_ELO_TS2700       0x0020
+
+#define USB_VENDOR_ID_ESSENTIAL_REALITY        0x0d7f
+#define USB_DEVICE_ID_ESSENTIAL_REALITY_P5 0x0100
+
+#define USB_VENDOR_ID_EZKEY            0x0518
+#define USB_DEVICE_ID_BTC_8193         0x0002
+
+#define USB_VENDOR_ID_GAMERON          0x0810
+#define USB_DEVICE_ID_GAMERON_DUAL_PSX_ADAPTOR 0x0001
+
+#define USB_VENDOR_ID_GENERAL_TOUCH    0x0dfc
+
+#define USB_VENDOR_ID_GLAB             0x06c2
+#define USB_DEVICE_ID_4_PHIDGETSERVO_30        0x0038
+#define USB_DEVICE_ID_1_PHIDGETSERVO_30        0x0039
+#define USB_DEVICE_ID_0_0_4_IF_KIT     0x0040
+#define USB_DEVICE_ID_0_16_16_IF_KIT   0x0044
+#define USB_DEVICE_ID_8_8_8_IF_KIT     0x0045
+#define USB_DEVICE_ID_0_8_7_IF_KIT     0x0051
+#define USB_DEVICE_ID_0_8_8_IF_KIT     0x0053
+#define USB_DEVICE_ID_PHIDGET_MOTORCONTROL     0x0058
+
+#define USB_VENDOR_ID_GOTOP            0x08f2
+#define USB_DEVICE_ID_SUPER_Q2         0x007f
+#define USB_DEVICE_ID_GOGOPEN          0x00ce
+#define USB_DEVICE_ID_PENPOWER         0x00f4
+
+#define USB_VENDOR_ID_GREENASIA                0x0e8f
+
+#define USB_VENDOR_ID_GRETAGMACBETH    0x0971
+#define USB_DEVICE_ID_GRETAGMACBETH_HUEY       0x2005
+
+#define USB_VENDOR_ID_GRIFFIN          0x077d
+#define USB_DEVICE_ID_POWERMATE                0x0410
+#define USB_DEVICE_ID_SOUNDKNOB                0x04AA
+
+#define USB_VENDOR_ID_GTCO             0x078c
+#define USB_DEVICE_ID_GTCO_90          0x0090
+#define USB_DEVICE_ID_GTCO_100         0x0100
+#define USB_DEVICE_ID_GTCO_101         0x0101
+#define USB_DEVICE_ID_GTCO_103         0x0103
+#define USB_DEVICE_ID_GTCO_104         0x0104
+#define USB_DEVICE_ID_GTCO_105         0x0105
+#define USB_DEVICE_ID_GTCO_106         0x0106
+#define USB_DEVICE_ID_GTCO_107         0x0107
+#define USB_DEVICE_ID_GTCO_108         0x0108
+#define USB_DEVICE_ID_GTCO_200         0x0200
+#define USB_DEVICE_ID_GTCO_201         0x0201
+#define USB_DEVICE_ID_GTCO_202         0x0202
+#define USB_DEVICE_ID_GTCO_203         0x0203
+#define USB_DEVICE_ID_GTCO_204         0x0204
+#define USB_DEVICE_ID_GTCO_205         0x0205
+#define USB_DEVICE_ID_GTCO_206         0x0206
+#define USB_DEVICE_ID_GTCO_207         0x0207
+#define USB_DEVICE_ID_GTCO_300         0x0300
+#define USB_DEVICE_ID_GTCO_301         0x0301
+#define USB_DEVICE_ID_GTCO_302         0x0302
+#define USB_DEVICE_ID_GTCO_303         0x0303
+#define USB_DEVICE_ID_GTCO_304         0x0304
+#define USB_DEVICE_ID_GTCO_305         0x0305
+#define USB_DEVICE_ID_GTCO_306         0x0306
+#define USB_DEVICE_ID_GTCO_307         0x0307
+#define USB_DEVICE_ID_GTCO_308         0x0308
+#define USB_DEVICE_ID_GTCO_309         0x0309
+#define USB_DEVICE_ID_GTCO_400         0x0400
+#define USB_DEVICE_ID_GTCO_401         0x0401
+#define USB_DEVICE_ID_GTCO_402         0x0402
+#define USB_DEVICE_ID_GTCO_403         0x0403
+#define USB_DEVICE_ID_GTCO_404         0x0404
+#define USB_DEVICE_ID_GTCO_405         0x0405
+#define USB_DEVICE_ID_GTCO_500         0x0500
+#define USB_DEVICE_ID_GTCO_501         0x0501
+#define USB_DEVICE_ID_GTCO_502         0x0502
+#define USB_DEVICE_ID_GTCO_503         0x0503
+#define USB_DEVICE_ID_GTCO_504         0x0504
+#define USB_DEVICE_ID_GTCO_1000                0x1000
+#define USB_DEVICE_ID_GTCO_1001                0x1001
+#define USB_DEVICE_ID_GTCO_1002                0x1002
+#define USB_DEVICE_ID_GTCO_1003                0x1003
+#define USB_DEVICE_ID_GTCO_1004                0x1004
+#define USB_DEVICE_ID_GTCO_1005                0x1005
+#define USB_DEVICE_ID_GTCO_1006                0x1006
+#define USB_DEVICE_ID_GTCO_1007                0x1007
+
+#define USB_VENDOR_ID_GYRATION         0x0c16
+#define USB_DEVICE_ID_GYRATION_REMOTE  0x0002
+
+#define USB_VENDOR_ID_HAPP             0x078b
+#define USB_DEVICE_ID_UGCI_DRIVING     0x0010
+#define USB_DEVICE_ID_UGCI_FLYING      0x0020
+#define USB_DEVICE_ID_UGCI_FIGHTING    0x0030
+
+#define USB_VENDOR_ID_IMATION          0x0718
+#define USB_DEVICE_ID_DISC_STAKKA      0xd000
+
+#define USB_VENDOR_ID_KBGEAR           0x084e
+#define USB_DEVICE_ID_KBGEAR_JAMSTUDIO 0x1001
+
+#define USB_VENDOR_ID_LABTEC           0x1020
+#define USB_DEVICE_ID_LABTEC_WIRELESS_KEYBOARD 0x0006
+
+#define USB_VENDOR_ID_LD               0x0f11
+#define USB_DEVICE_ID_LD_CASSY         0x1000
+#define USB_DEVICE_ID_LD_POCKETCASSY   0x1010
+#define USB_DEVICE_ID_LD_MOBILECASSY   0x1020
+#define USB_DEVICE_ID_LD_JWM           0x1080
+#define USB_DEVICE_ID_LD_DMMP          0x1081
+#define USB_DEVICE_ID_LD_UMIP          0x1090
+#define USB_DEVICE_ID_LD_XRAY1         0x1100
+#define USB_DEVICE_ID_LD_XRAY2         0x1101
+#define USB_DEVICE_ID_LD_VIDEOCOM      0x1200
+#define USB_DEVICE_ID_LD_COM3LAB       0x2000
+#define USB_DEVICE_ID_LD_TELEPORT      0x2010
+#define USB_DEVICE_ID_LD_NETWORKANALYSER 0x2020
+#define USB_DEVICE_ID_LD_POWERCONTROL  0x2030
+#define USB_DEVICE_ID_LD_MACHINETEST   0x2040
+
+#define USB_VENDOR_ID_LOGITECH         0x046d
+#define USB_DEVICE_ID_LOGITECH_LX3     0xc044
+#define USB_DEVICE_ID_LOGITECH_V150    0xc047
+#define USB_DEVICE_ID_LOGITECH_RECEIVER        0xc101
+#define USB_DEVICE_ID_LOGITECH_HARMONY_FIRST  0xc110
+#define USB_DEVICE_ID_LOGITECH_HARMONY_LAST 0xc14f
+#define USB_DEVICE_ID_LOGITECH_RUMBLEPAD       0xc211
+#define USB_DEVICE_ID_LOGITECH_EXTREME_3D      0xc215
+#define USB_DEVICE_ID_LOGITECH_RUMBLEPAD2      0xc218
+#define USB_DEVICE_ID_LOGITECH_RUMBLEPAD2_2    0xc219
+#define USB_DEVICE_ID_LOGITECH_WINGMAN_F3D     0xc283
+#define USB_DEVICE_ID_LOGITECH_FORCE3D_PRO     0xc286
+#define USB_DEVICE_ID_LOGITECH_WHEEL   0xc294
+#define USB_DEVICE_ID_LOGITECH_MOMO_WHEEL      0xc295
+#define USB_DEVICE_ID_LOGITECH_ELITE_KBD       0xc30a
+#define USB_DEVICE_ID_LOGITECH_KBD     0xc311
+#define USB_DEVICE_ID_S510_RECEIVER    0xc50c
+#define USB_DEVICE_ID_S510_RECEIVER_2  0xc517
+#define USB_DEVICE_ID_LOGITECH_CORDLESS_DESKTOP_LX500  0xc512
+#define USB_DEVICE_ID_MX3000_RECEIVER  0xc513
+#define USB_DEVICE_ID_DINOVO_DESKTOP   0xc704
+#define USB_DEVICE_ID_DINOVO_EDGE      0xc714
+#define USB_DEVICE_ID_DINOVO_MINI      0xc71f
+#define USB_DEVICE_ID_LOGITECH_MOMO_WHEEL2     0xca03
+
+#define USB_VENDOR_ID_MCC              0x09db
+#define USB_DEVICE_ID_MCC_PMD1024LS    0x0076
+#define USB_DEVICE_ID_MCC_PMD1208LS    0x007a
+
+#define USB_VENDOR_ID_MGE              0x0463
+#define USB_DEVICE_ID_MGE_UPS          0xffff
+#define USB_DEVICE_ID_MGE_UPS1         0x0001
+
+#define USB_VENDOR_ID_MICROCHIP                0x04d8
+#define USB_DEVICE_ID_PICKIT1          0x0032
+#define USB_DEVICE_ID_PICKIT2          0x0033
+
+#define USB_VENDOR_ID_MICROSOFT                0x045e
+#define USB_DEVICE_ID_SIDEWINDER_GV    0x003b
+#define USB_DEVICE_ID_WIRELESS_OPTICAL_DESKTOP_3_0 0x009d
+#define USB_DEVICE_ID_MS_NE4K          0x00db
+#define USB_DEVICE_ID_MS_LK6K          0x00f9
+#define USB_DEVICE_ID_MS_PRESENTER_8K_BT       0x0701
+#define USB_DEVICE_ID_MS_PRESENTER_8K_USB      0x0713
+
+
+#define USB_VENDOR_ID_MONTEREY         0x0566
+#define USB_DEVICE_ID_GENIUS_KB29E     0x3004
+
+#define USB_VENDOR_ID_NCR              0x0404
+#define USB_DEVICE_ID_NCR_FIRST                0x0300
+#define USB_DEVICE_ID_NCR_LAST         0x03ff
+
+#define USB_VENDOR_ID_NATIONAL_SEMICONDUCTOR 0x0400
+#define USB_DEVICE_ID_N_S_HARMONY       0xc359
+
+#define USB_VENDOR_ID_NATSU             0x08b7
+#define USB_DEVICE_ID_NATSU_GAMEPAD     0x0001
+
+#define USB_VENDOR_ID_NEC              0x073e
+#define USB_DEVICE_ID_NEC_USB_GAME_PAD 0x0301
+
+#define USB_VENDOR_ID_ONTRAK           0x0a07
+#define USB_DEVICE_ID_ONTRAK_ADU100    0x0064
+
+#define USB_VENDOR_ID_PANJIT           0x134c
+
+#define USB_VENDOR_ID_PANTHERLORD      0x0810
+#define USB_DEVICE_ID_PANTHERLORD_TWIN_USB_JOYSTICK    0x0001
+
+#define USB_VENDOR_ID_PETALYNX         0x18b1
+#define USB_DEVICE_ID_PETALYNX_MAXTER_REMOTE   0x0037
+
+#define USB_VENDOR_ID_PLAYDOTCOM       0x0b43
+#define USB_DEVICE_ID_PLAYDOTCOM_EMS_USBII     0x0003
+
+#define USB_VENDOR_ID_SAITEK           0x06a3
+#define USB_DEVICE_ID_SAITEK_RUMBLEPAD 0xff17
+
+#define USB_VENDOR_ID_SAMSUNG          0x0419
+#define USB_DEVICE_ID_SAMSUNG_IR_REMOTE        0x0001
+
+#define USB_VENDOR_ID_SONY                     0x054c
+#define USB_DEVICE_ID_SONY_PS3_CONTROLLER      0x0268
+
+#define USB_VENDOR_ID_SOUNDGRAPH       0x15c2
+#define USB_DEVICE_ID_SOUNDGRAPH_IMON_LCD      0x0038
+
+#define USB_VENDOR_ID_SUN              0x0430
+#define USB_DEVICE_ID_RARITAN_KVM_DONGLE       0xcdab
+
+#define USB_VENDOR_ID_SUNPLUS          0x04fc
+#define USB_DEVICE_ID_SUNPLUS_WDESKTOP 0x05d8
+
+#define USB_VENDOR_ID_TENX             0x1130
+#define USB_DEVICE_ID_TENX_IBUDDY1     0x0001
+#define USB_DEVICE_ID_TENX_IBUDDY2     0x0002
+
+#define USB_VENDOR_ID_THRUSTMASTER     0x044f
+
+#define USB_VENDOR_ID_TOPMAX           0x0663
+#define USB_DEVICE_ID_TOPMAX_COBRAPAD  0x0103
+
+#define USB_VENDOR_ID_TURBOX           0x062a
+#define USB_DEVICE_ID_TURBOX_KEYBOARD  0x0201
+
+#define USB_VENDOR_ID_VERNIER          0x08f7
+#define USB_DEVICE_ID_VERNIER_LABPRO   0x0001
+#define USB_DEVICE_ID_VERNIER_GOTEMP   0x0002
+#define USB_DEVICE_ID_VERNIER_SKIP     0x0003
+#define USB_DEVICE_ID_VERNIER_CYCLOPS  0x0004
+#define USB_DEVICE_ID_VERNIER_LCSPEC   0x0006
+
+#define USB_VENDOR_ID_WACOM            0x056a
+
+#define USB_VENDOR_ID_WISEGROUP                0x0925
+#define USB_DEVICE_ID_1_PHIDGETSERVO_20        0x8101
+#define USB_DEVICE_ID_4_PHIDGETSERVO_20        0x8104
+#define USB_DEVICE_ID_8_8_4_IF_KIT     0x8201
+#define USB_DEVICE_ID_QUAD_USB_JOYPAD  0x8800
+#define USB_DEVICE_ID_DUAL_USB_JOYPAD  0x8866
+
+#define USB_VENDOR_ID_WISEGROUP_LTD    0x6666
+#define USB_VENDOR_ID_WISEGROUP_LTD2   0x6677
+#define USB_DEVICE_ID_SMARTJOY_DUAL_PLUS 0x8802
+
+#define USB_VENDOR_ID_YEALINK          0x6993
+#define USB_DEVICE_ID_YEALINK_P1K_P4K_B2K      0xb001
+
+#define USB_VENDOR_ID_ZEROPLUS         0x0c12
+
+#define USB_VENDOR_ID_KYE              0x0458
+#define USB_DEVICE_ID_KYE_GPEN_560     0x5003
+
+#endif
diff --git a/drivers/hid/hid-input-quirks.c b/drivers/hid/hid-input-quirks.c
deleted file mode 100644 (file)
index 16feea0..0000000
+++ /dev/null
@@ -1,484 +0,0 @@
-/*
- *  HID-input usage mapping quirks
- *
- *  This is used to handle HID-input mappings for devices violating
- *  HUT 1.12 specification.
- *
- * Copyright (c) 2007-2008 Jiri Kosina
- */
-
-/*
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the Free
- * Software Foundation; either version 2 of the License
- */
-
-#include <linux/input.h>
-#include <linux/hid.h>
-
-#define map_abs(c)      do { usage->code = c; usage->type = EV_ABS; *bit = input->absbit; *max = ABS_MAX; } while (0)
-#define map_rel(c)      do { usage->code = c; usage->type = EV_REL; *bit = input->relbit; *max = REL_MAX; } while (0)
-#define map_key(c)      do { usage->code = c; usage->type = EV_KEY; *bit = input->keybit; *max = KEY_MAX; } while (0)
-#define map_led(c)      do { usage->code = c; usage->type = EV_LED; *bit = input->ledbit; *max = LED_MAX; } while (0)
-
-#define map_abs_clear(c)        do { map_abs(c); clear_bit(c, *bit); } while (0)
-#define map_key_clear(c)        do { map_key(c); clear_bit(c, *bit); } while (0)
-
-static int quirk_belkin_wkbd(struct hid_usage *usage, struct input_dev *input,
-                             unsigned long **bit, int *max)
-{
-       if ((usage->hid & HID_USAGE_PAGE) != HID_UP_CONSUMER)
-               return 0;
-
-       switch (usage->hid & HID_USAGE) {
-               case 0x03a: map_key_clear(KEY_SOUND);           break;
-               case 0x03b: map_key_clear(KEY_CAMERA);          break;
-               case 0x03c: map_key_clear(KEY_DOCUMENTS);       break;
-               default:
-                       return 0;
-       }
-       return 1;
-}
-
-static int quirk_cherry_cymotion(struct hid_usage *usage, struct input_dev *input,
-                             unsigned long **bit, int *max)
-{
-       if ((usage->hid & HID_USAGE_PAGE) != HID_UP_CONSUMER)
-               return 0;
-
-       switch (usage->hid & HID_USAGE) {
-               case 0x301: map_key_clear(KEY_PROG1);           break;
-               case 0x302: map_key_clear(KEY_PROG2);           break;
-               case 0x303: map_key_clear(KEY_PROG3);           break;
-               default:
-                       return 0;
-       }
-       return 1;
-}
-
-static int quirk_logitech_ultrax_remote(struct hid_usage *usage, struct input_dev *input,
-                             unsigned long **bit, int *max)
-{
-       if ((usage->hid & HID_USAGE_PAGE) != HID_UP_LOGIVENDOR)
-               return 0;
-
-       set_bit(EV_REP, input->evbit);
-       switch(usage->hid & HID_USAGE) {
-               /* Reported on Logitech Ultra X Media Remote */
-               case 0x004: map_key_clear(KEY_AGAIN);           break;
-               case 0x00d: map_key_clear(KEY_HOME);            break;
-               case 0x024: map_key_clear(KEY_SHUFFLE);         break;
-               case 0x025: map_key_clear(KEY_TV);              break;
-               case 0x026: map_key_clear(KEY_MENU);            break;
-               case 0x031: map_key_clear(KEY_AUDIO);           break;
-               case 0x032: map_key_clear(KEY_TEXT);            break;
-               case 0x033: map_key_clear(KEY_LAST);            break;
-               case 0x047: map_key_clear(KEY_MP3);             break;
-               case 0x048: map_key_clear(KEY_DVD);             break;
-               case 0x049: map_key_clear(KEY_MEDIA);           break;
-               case 0x04a: map_key_clear(KEY_VIDEO);           break;
-               case 0x04b: map_key_clear(KEY_ANGLE);           break;
-               case 0x04c: map_key_clear(KEY_LANGUAGE);        break;
-               case 0x04d: map_key_clear(KEY_SUBTITLE);        break;
-               case 0x051: map_key_clear(KEY_RED);             break;
-               case 0x052: map_key_clear(KEY_CLOSE);           break;
-
-               default:
-                       return 0;
-       }
-       return 1;
-}
-
-static int quirk_gyration_remote(struct hid_usage *usage, struct input_dev *input,
-                             unsigned long **bit, int *max)
-{
-       if ((usage->hid & HID_USAGE_PAGE) != HID_UP_LOGIVENDOR)
-               return 0;
-
-       set_bit(EV_REP, input->evbit);
-       switch(usage->hid & HID_USAGE) {
-               /* Reported on Gyration MCE Remote */
-               case 0x00d: map_key_clear(KEY_HOME);            break;
-               case 0x024: map_key_clear(KEY_DVD);             break;
-               case 0x025: map_key_clear(KEY_PVR);             break;
-               case 0x046: map_key_clear(KEY_MEDIA);           break;
-               case 0x047: map_key_clear(KEY_MP3);             break;
-               case 0x049: map_key_clear(KEY_CAMERA);          break;
-               case 0x04a: map_key_clear(KEY_VIDEO);           break;
-
-               default:
-                       return 0;
-       }
-       return 1;
-}
-
-static int quirk_chicony_tactical_pad(struct hid_usage *usage, struct input_dev *input,
-                             unsigned long **bit, int *max)
-{
-       if ((usage->hid & HID_USAGE_PAGE) != HID_UP_MSVENDOR)
-               return 0;
-
-       set_bit(EV_REP, input->evbit);
-       switch (usage->hid & HID_USAGE) {
-               case 0xff01: map_key_clear(BTN_1);              break;
-               case 0xff02: map_key_clear(BTN_2);              break;
-               case 0xff03: map_key_clear(BTN_3);              break;
-               case 0xff04: map_key_clear(BTN_4);              break;
-               case 0xff05: map_key_clear(BTN_5);              break;
-               case 0xff06: map_key_clear(BTN_6);              break;
-               case 0xff07: map_key_clear(BTN_7);              break;
-               case 0xff08: map_key_clear(BTN_8);              break;
-               case 0xff09: map_key_clear(BTN_9);              break;
-               case 0xff0a: map_key_clear(BTN_A);              break;
-               case 0xff0b: map_key_clear(BTN_B);              break;
-               default:
-                       return 0;
-       }
-       return 1;
-}
-
-static int quirk_microsoft_ergonomy_kb(struct hid_usage *usage, struct input_dev *input,
-                             unsigned long **bit, int *max)
-{
-       if ((usage->hid & HID_USAGE_PAGE) != HID_UP_MSVENDOR)
-               return 0;
-
-       switch(usage->hid & HID_USAGE) {
-               case 0xfd06: map_key_clear(KEY_CHAT);           break;
-               case 0xfd07: map_key_clear(KEY_PHONE);          break;
-               case 0xff05:
-                       set_bit(EV_REP, input->evbit);
-                       map_key_clear(KEY_F13);
-                       set_bit(KEY_F14, input->keybit);
-                       set_bit(KEY_F15, input->keybit);
-                       set_bit(KEY_F16, input->keybit);
-                       set_bit(KEY_F17, input->keybit);
-                       set_bit(KEY_F18, input->keybit);
-               default:
-                       return 0;
-       }
-       return 1;
-}
-
-static int quirk_microsoft_presenter_8k(struct hid_usage *usage, struct input_dev *input,
-                             unsigned long **bit, int *max)
-{
-       if ((usage->hid & HID_USAGE_PAGE) != HID_UP_MSVENDOR)
-               return 0;
-
-       set_bit(EV_REP, input->evbit);
-       switch(usage->hid & HID_USAGE) {
-               case 0xfd08: map_key_clear(KEY_FORWARD);        break;
-               case 0xfd09: map_key_clear(KEY_BACK);           break;
-               case 0xfd0b: map_key_clear(KEY_PLAYPAUSE);      break;
-               case 0xfd0e: map_key_clear(KEY_CLOSE);          break;
-               case 0xfd0f: map_key_clear(KEY_PLAY);           break;
-               default:
-                       return 0;
-       }
-       return 1;
-}
-
-static int quirk_petalynx_remote(struct hid_usage *usage, struct input_dev *input,
-                             unsigned long **bit, int *max)
-{
-       if (((usage->hid & HID_USAGE_PAGE) != HID_UP_LOGIVENDOR) &&
-                       ((usage->hid & HID_USAGE_PAGE) != HID_UP_CONSUMER))
-               return 0;
-
-       if ((usage->hid & HID_USAGE_PAGE) == HID_UP_LOGIVENDOR)
-               switch(usage->hid & HID_USAGE) {
-                       case 0x05a: map_key_clear(KEY_TEXT);            break;
-                       case 0x05b: map_key_clear(KEY_RED);             break;
-                       case 0x05c: map_key_clear(KEY_GREEN);           break;
-                       case 0x05d: map_key_clear(KEY_YELLOW);          break;
-                       case 0x05e: map_key_clear(KEY_BLUE);            break;
-                       default:
-                               return 0;
-               }
-
-       if ((usage->hid & HID_USAGE_PAGE) == HID_UP_CONSUMER)
-               switch(usage->hid & HID_USAGE) {
-                       case 0x0f6: map_key_clear(KEY_NEXT);            break;
-                       case 0x0fa: map_key_clear(KEY_BACK);            break;
-                       default:
-                               return 0;
-               }
-       return 1;
-}
-
-static int quirk_logitech_wireless(struct hid_usage *usage, struct input_dev *input,
-                             unsigned long **bit, int *max)
-{
-       if ((usage->hid & HID_USAGE_PAGE) != HID_UP_CONSUMER)
-               return 0;
-
-       switch (usage->hid & HID_USAGE) {
-               case 0x1001: map_key_clear(KEY_MESSENGER);      break;
-               case 0x1003: map_key_clear(KEY_SOUND);          break;
-               case 0x1004: map_key_clear(KEY_VIDEO);          break;
-               case 0x1005: map_key_clear(KEY_AUDIO);          break;
-               case 0x100a: map_key_clear(KEY_DOCUMENTS);      break;
-               case 0x1011: map_key_clear(KEY_PREVIOUSSONG);   break;
-               case 0x1012: map_key_clear(KEY_NEXTSONG);       break;
-               case 0x1013: map_key_clear(KEY_CAMERA);         break;
-               case 0x1014: map_key_clear(KEY_MESSENGER);      break;
-               case 0x1015: map_key_clear(KEY_RECORD);         break;
-               case 0x1016: map_key_clear(KEY_PLAYER);         break;
-               case 0x1017: map_key_clear(KEY_EJECTCD);        break;
-               case 0x1018: map_key_clear(KEY_MEDIA);          break;
-               case 0x1019: map_key_clear(KEY_PROG1);          break;
-               case 0x101a: map_key_clear(KEY_PROG2);          break;
-               case 0x101b: map_key_clear(KEY_PROG3);          break;
-               case 0x101f: map_key_clear(KEY_ZOOMIN);         break;
-               case 0x1020: map_key_clear(KEY_ZOOMOUT);        break;
-               case 0x1021: map_key_clear(KEY_ZOOMRESET);      break;
-               case 0x1023: map_key_clear(KEY_CLOSE);          break;
-               case 0x1027: map_key_clear(KEY_MENU);           break;
-               /* this one is marked as 'Rotate' */
-               case 0x1028: map_key_clear(KEY_ANGLE);          break;
-               case 0x1029: map_key_clear(KEY_SHUFFLE);        break;
-               case 0x102a: map_key_clear(KEY_BACK);           break;
-               case 0x102b: map_key_clear(KEY_CYCLEWINDOWS);   break;
-               case 0x1041: map_key_clear(KEY_BATTERY);        break;
-               case 0x1042: map_key_clear(KEY_WORDPROCESSOR);  break;
-               case 0x1043: map_key_clear(KEY_SPREADSHEET);    break;
-               case 0x1044: map_key_clear(KEY_PRESENTATION);   break;
-               case 0x1045: map_key_clear(KEY_UNDO);           break;
-               case 0x1046: map_key_clear(KEY_REDO);           break;
-               case 0x1047: map_key_clear(KEY_PRINT);          break;
-               case 0x1048: map_key_clear(KEY_SAVE);           break;
-               case 0x1049: map_key_clear(KEY_PROG1);          break;
-               case 0x104a: map_key_clear(KEY_PROG2);          break;
-               case 0x104b: map_key_clear(KEY_PROG3);          break;
-               case 0x104c: map_key_clear(KEY_PROG4);          break;
-
-               default:
-                       return 0;
-       }
-       return 1;
-}
-
-static int quirk_cherry_genius_29e(struct hid_usage *usage, struct input_dev *input,
-                             unsigned long **bit, int *max)
-{
-       if ((usage->hid & HID_USAGE_PAGE) != HID_UP_CONSUMER)
-               return 0;
-
-       switch (usage->hid & HID_USAGE) {
-               case 0x156: map_key_clear(KEY_WORDPROCESSOR);   break;
-               case 0x157: map_key_clear(KEY_SPREADSHEET);     break;
-               case 0x158: map_key_clear(KEY_PRESENTATION);    break;
-               case 0x15c: map_key_clear(KEY_STOP);            break;
-
-               default:
-                       return 0;
-       }
-       return 1;
-}
-
-static int quirk_btc_8193(struct hid_usage *usage, struct input_dev *input,
-                             unsigned long **bit, int *max)
-{
-       if ((usage->hid & HID_USAGE_PAGE) != HID_UP_CONSUMER)
-               return 0;
-
-       switch (usage->hid & HID_USAGE) {
-               case 0x230: map_key(BTN_MOUSE);                 break;
-               case 0x231: map_rel(REL_WHEEL);                 break;
-               /* 
-                * this keyboard has a scrollwheel implemented in
-                * totally broken way. We map this usage temporarily
-                * to HWHEEL and handle it in the event quirk handler
-                */
-               case 0x232: map_rel(REL_HWHEEL);                break;
-
-               default:
-                       return 0;
-       }
-       return 1;
-}
-
-static int quirk_sunplus_wdesktop(struct hid_usage *usage, struct input_dev *input,
-                             unsigned long **bit, int *max)
-{
-       if ((usage->hid & HID_USAGE_PAGE) != HID_UP_CONSUMER)
-               return 0;
-
-       switch (usage->hid & HID_USAGE) {
-               case 0x2003: map_key_clear(KEY_ZOOMIN);         break;
-               case 0x2103: map_key_clear(KEY_ZOOMOUT);        break;
-               default:
-                       return 0;
-       }
-       return 1;
-}
-
-#define VENDOR_ID_BELKIN                       0x1020
-#define DEVICE_ID_BELKIN_WIRELESS_KEYBOARD     0x0006
-
-#define VENDOR_ID_CHERRY                       0x046a
-#define DEVICE_ID_CHERRY_CYMOTION              0x0023
-
-#define VENDOR_ID_CHICONY                      0x04f2
-#define DEVICE_ID_CHICONY_TACTICAL_PAD         0x0418
-
-#define VENDOR_ID_EZKEY                                0x0518
-#define DEVICE_ID_BTC_8193                     0x0002
-
-#define VENDOR_ID_GYRATION                     0x0c16
-#define DEVICE_ID_GYRATION_REMOTE              0x0002
-
-#define VENDOR_ID_LOGITECH                     0x046d
-#define DEVICE_ID_LOGITECH_RECEIVER            0xc101
-#define DEVICE_ID_S510_RECEIVER                        0xc50c
-#define DEVICE_ID_S510_RECEIVER_2              0xc517
-#define DEVICE_ID_MX3000_RECEIVER              0xc513
-
-#define VENDOR_ID_MICROSOFT                    0x045e
-#define DEVICE_ID_MS4K                         0x00db
-#define DEVICE_ID_MS6K                         0x00f9
-#define DEVICE_IS_MS_PRESENTER_8K_BT           0x0701
-#define DEVICE_ID_MS_PRESENTER_8K_USB          0x0713
-
-#define VENDOR_ID_MONTEREY                     0x0566
-#define DEVICE_ID_GENIUS_KB29E                 0x3004
-
-#define VENDOR_ID_PETALYNX                     0x18b1
-#define DEVICE_ID_PETALYNX_MAXTER_REMOTE       0x0037
-
-#define VENDOR_ID_SUNPLUS                      0x04fc
-#define DEVICE_ID_SUNPLUS_WDESKTOP             0x05d8
-
-static const struct hid_input_blacklist {
-       __u16 idVendor;
-       __u16 idProduct;
-       int (*quirk)(struct hid_usage *, struct input_dev *, unsigned long **, int *);
-} hid_input_blacklist[] = {
-       { VENDOR_ID_BELKIN, DEVICE_ID_BELKIN_WIRELESS_KEYBOARD, quirk_belkin_wkbd },
-
-       { VENDOR_ID_CHERRY, DEVICE_ID_CHERRY_CYMOTION, quirk_cherry_cymotion },
-
-       { VENDOR_ID_CHICONY, DEVICE_ID_CHICONY_TACTICAL_PAD, quirk_chicony_tactical_pad },
-
-       { VENDOR_ID_EZKEY, DEVICE_ID_BTC_8193, quirk_btc_8193 },
-
-       { VENDOR_ID_GYRATION, DEVICE_ID_GYRATION_REMOTE, quirk_gyration_remote },
-
-       { VENDOR_ID_LOGITECH, DEVICE_ID_LOGITECH_RECEIVER, quirk_logitech_ultrax_remote },
-       { VENDOR_ID_LOGITECH, DEVICE_ID_S510_RECEIVER, quirk_logitech_wireless },
-       { VENDOR_ID_LOGITECH, DEVICE_ID_S510_RECEIVER_2, quirk_logitech_wireless },
-       { VENDOR_ID_LOGITECH, DEVICE_ID_MX3000_RECEIVER, quirk_logitech_wireless },
-
-       { VENDOR_ID_MICROSOFT, DEVICE_ID_MS4K, quirk_microsoft_ergonomy_kb },
-       { VENDOR_ID_MICROSOFT, DEVICE_ID_MS6K, quirk_microsoft_ergonomy_kb },
-       { VENDOR_ID_MICROSOFT, DEVICE_IS_MS_PRESENTER_8K_BT, quirk_microsoft_presenter_8k },
-       { VENDOR_ID_MICROSOFT, DEVICE_ID_MS_PRESENTER_8K_USB, quirk_microsoft_presenter_8k },
-
-       { VENDOR_ID_MONTEREY, DEVICE_ID_GENIUS_KB29E, quirk_cherry_genius_29e },
-
-       { VENDOR_ID_PETALYNX, DEVICE_ID_PETALYNX_MAXTER_REMOTE, quirk_petalynx_remote },
-
-       { VENDOR_ID_SUNPLUS, DEVICE_ID_SUNPLUS_WDESKTOP, quirk_sunplus_wdesktop },
-
-       { 0, 0, NULL }
-};
-
-int hidinput_mapping_quirks(struct hid_usage *usage, 
-                                  struct input_dev *input, 
-                                  unsigned long **bit, int *max)
-{
-       struct hid_device *device = input_get_drvdata(input);
-       int i = 0;
-       
-       while (hid_input_blacklist[i].quirk) {
-               if (hid_input_blacklist[i].idVendor == device->vendor &&
-                               hid_input_blacklist[i].idProduct == device->product)
-                       return hid_input_blacklist[i].quirk(usage, input, bit, max);
-               i++;
-       }
-       return 0;
-}
-
-int hidinput_event_quirks(struct hid_device *hid, struct hid_field *field, struct hid_usage *usage, __s32 value)
-{
-       struct input_dev *input;
-
-       input = field->hidinput->input;
-
-       if (((hid->quirks & HID_QUIRK_2WHEEL_MOUSE_HACK_5) && (usage->hid == 0x00090005))
-               || ((hid->quirks & HID_QUIRK_2WHEEL_MOUSE_HACK_7) && (usage->hid == 0x00090007))) {
-               if (value) hid->quirks |=  HID_QUIRK_2WHEEL_MOUSE_HACK_ON;
-               else       hid->quirks &= ~HID_QUIRK_2WHEEL_MOUSE_HACK_ON;
-               return 1;
-       }
-
-       if ((hid->quirks & HID_QUIRK_2WHEEL_MOUSE_HACK_B8) &&
-                       (usage->type == EV_REL) &&
-                       (usage->code == REL_WHEEL)) {
-               hid->delayed_value = value;
-               return 1;
-       }
-
-       if ((hid->quirks & HID_QUIRK_2WHEEL_MOUSE_HACK_B8) &&
-                       (usage->hid == 0x000100b8)) {
-               input_event(input, EV_REL, value ? REL_HWHEEL : REL_WHEEL, hid->delayed_value);
-               return 1;
-       }
-
-       if ((hid->quirks & HID_QUIRK_INVERT_HWHEEL) && (usage->code == REL_HWHEEL)) {
-               input_event(input, usage->type, usage->code, -value);
-               return 1;
-       }
-
-       if ((hid->quirks & HID_QUIRK_2WHEEL_MOUSE_HACK_ON) && (usage->code == REL_WHEEL)) {
-               input_event(input, usage->type, REL_HWHEEL, value);
-               return 1;
-       }
-
-       if ((hid->quirks & HID_QUIRK_APPLE_HAS_FN) && hidinput_apple_event(hid, input, usage, value))
-               return 1;
-
-       /* Handling MS keyboards special buttons */
-       if (hid->quirks & HID_QUIRK_MICROSOFT_KEYS && 
-                       usage->hid == (HID_UP_MSVENDOR | 0xff05)) {
-               int key = 0;
-               static int last_key = 0;
-               switch (value) {
-                       case 0x01: key = KEY_F14; break;
-                       case 0x02: key = KEY_F15; break;
-                       case 0x04: key = KEY_F16; break;
-                       case 0x08: key = KEY_F17; break;
-                       case 0x10: key = KEY_F18; break;
-                       default: break;
-               }
-               if (key) {
-                       input_event(input, usage->type, key, 1);
-                       last_key = key;
-               } else {
-                       input_event(input, usage->type, last_key, 0);
-               }
-       }
-
-       /* handle the temporary quirky mapping to HWHEEL */
-       if (hid->quirks & HID_QUIRK_HWHEEL_WHEEL_INVERT &&
-                       usage->type == EV_REL && usage->code == REL_HWHEEL) {
-               input_event(input, usage->type, REL_WHEEL, -value);
-               return 1;
-       }
-
-       /* Gyration MCE remote "Sleep" key */
-       if (hid->vendor == VENDOR_ID_GYRATION &&
-           hid->product == DEVICE_ID_GYRATION_REMOTE &&
-           (usage->hid & HID_USAGE_PAGE) == HID_UP_GENDESK &&
-           (usage->hid & 0xff) == 0x82) {
-               input_event(input, usage->type, usage->code, 1);
-               input_sync(input);
-               input_event(input, usage->type, usage->code, 0);
-               input_sync(input);
-               return 1;
-       }
-       return 0;
-}
-
-
index 1b2e8dc3398d7bf972243507d695f14e2093aaa0..7f183b7147e119b9ac1ea0e98d8c81bce662aaf3 100644 (file)
 #include <linux/hid.h>
 #include <linux/hid-debug.h>
 
-static int hid_apple_fnmode = 1;
-module_param_named(pb_fnmode, hid_apple_fnmode, int, 0644);
-MODULE_PARM_DESC(pb_fnmode,
-               "Mode of fn key on Apple keyboards (0 = disabled, 1 = fkeyslast, 2 = fkeysfirst)");
-
 #define unk    KEY_UNKNOWN
 
 static const unsigned char hid_keyboard[256] = {
@@ -58,227 +53,20 @@ static const unsigned char hid_keyboard[256] = {
        150,158,159,128,136,177,178,176,142,152,173,140,unk,unk,unk,unk
 };
 
-/* extended mapping for certain Logitech hardware (Logitech cordless desktop LX500) */
-#define LOGITECH_EXPANDED_KEYMAP_SIZE 80
-static int logitech_expanded_keymap[LOGITECH_EXPANDED_KEYMAP_SIZE] = {
-         0,216,  0,213,175,156,  0,  0,  0,  0,
-       144,  0,  0,  0,  0,  0,  0,  0,  0,212,
-       174,167,152,161,112,  0,  0,  0,154,  0,
-         0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
-         0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
-         0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
-         0,  0,  0,  0,  0,183,184,185,186,187,
-       188,189,190,191,192,193,194,  0,  0,  0
-};
-
 static const struct {
        __s32 x;
        __s32 y;
 }  hid_hat_to_axis[] = {{ 0, 0}, { 0,-1}, { 1,-1}, { 1, 0}, { 1, 1}, { 0, 1}, {-1, 1}, {-1, 0}, {-1,-1}};
 
-#define map_abs(c)     do { usage->code = c; usage->type = EV_ABS; bit = input->absbit; max = ABS_MAX; } while (0)
-#define map_rel(c)     do { usage->code = c; usage->type = EV_REL; bit = input->relbit; max = REL_MAX; } while (0)
-#define map_key(c)     do { usage->code = c; usage->type = EV_KEY; bit = input->keybit; max = KEY_MAX; } while (0)
-#define map_led(c)     do { usage->code = c; usage->type = EV_LED; bit = input->ledbit; max = LED_MAX; } while (0)
-
-#define map_abs_clear(c)       do { map_abs(c); clear_bit(c, bit); } while (0)
-#define map_key_clear(c)       do { map_key(c); clear_bit(c, bit); } while (0)
-
-#ifdef CONFIG_USB_HIDINPUT_POWERBOOK
-
-struct hidinput_key_translation {
-       u16 from;
-       u16 to;
-       u8 flags;
-};
-
-#define APPLE_FLAG_FKEY 0x01
-
-static struct hidinput_key_translation apple_fn_keys[] = {
-       { KEY_BACKSPACE, KEY_DELETE },
-       { KEY_F1,       KEY_BRIGHTNESSDOWN, APPLE_FLAG_FKEY },
-       { KEY_F2,       KEY_BRIGHTNESSUP,   APPLE_FLAG_FKEY },
-       { KEY_F3,       KEY_FN_F5,          APPLE_FLAG_FKEY }, /* Exposé */
-       { KEY_F4,       KEY_FN_F4,          APPLE_FLAG_FKEY }, /* Dashboard */
-       { KEY_F5,       KEY_KBDILLUMDOWN,   APPLE_FLAG_FKEY },
-       { KEY_F6,       KEY_KBDILLUMUP,     APPLE_FLAG_FKEY },
-       { KEY_F7,       KEY_PREVIOUSSONG,   APPLE_FLAG_FKEY },
-       { KEY_F8,       KEY_PLAYPAUSE,      APPLE_FLAG_FKEY },
-       { KEY_F9,       KEY_NEXTSONG,       APPLE_FLAG_FKEY },
-       { KEY_F10,      KEY_MUTE,           APPLE_FLAG_FKEY },
-       { KEY_F11,      KEY_VOLUMEDOWN,     APPLE_FLAG_FKEY },
-       { KEY_F12,      KEY_VOLUMEUP,       APPLE_FLAG_FKEY },
-       { KEY_UP,       KEY_PAGEUP },
-       { KEY_DOWN,     KEY_PAGEDOWN },
-       { KEY_LEFT,     KEY_HOME },
-       { KEY_RIGHT,    KEY_END },
-       { }
-};
-
-static struct hidinput_key_translation powerbook_fn_keys[] = {
-       { KEY_BACKSPACE, KEY_DELETE },
-       { KEY_F1,       KEY_BRIGHTNESSDOWN,     APPLE_FLAG_FKEY },
-       { KEY_F2,       KEY_BRIGHTNESSUP,       APPLE_FLAG_FKEY },
-       { KEY_F3,       KEY_MUTE,               APPLE_FLAG_FKEY },
-       { KEY_F4,       KEY_VOLUMEDOWN,         APPLE_FLAG_FKEY },
-       { KEY_F5,       KEY_VOLUMEUP,           APPLE_FLAG_FKEY },
-       { KEY_F6,       KEY_NUMLOCK,            APPLE_FLAG_FKEY },
-       { KEY_F7,       KEY_SWITCHVIDEOMODE,    APPLE_FLAG_FKEY },
-       { KEY_F8,       KEY_KBDILLUMTOGGLE,     APPLE_FLAG_FKEY },
-       { KEY_F9,       KEY_KBDILLUMDOWN,       APPLE_FLAG_FKEY },
-       { KEY_F10,      KEY_KBDILLUMUP,         APPLE_FLAG_FKEY },
-       { KEY_UP,       KEY_PAGEUP },
-       { KEY_DOWN,     KEY_PAGEDOWN },
-       { KEY_LEFT,     KEY_HOME },
-       { KEY_RIGHT,    KEY_END },
-       { }
-};
-
-static struct hidinput_key_translation powerbook_numlock_keys[] = {
-       { KEY_J,        KEY_KP1 },
-       { KEY_K,        KEY_KP2 },
-       { KEY_L,        KEY_KP3 },
-       { KEY_U,        KEY_KP4 },
-       { KEY_I,        KEY_KP5 },
-       { KEY_O,        KEY_KP6 },
-       { KEY_7,        KEY_KP7 },
-       { KEY_8,        KEY_KP8 },
-       { KEY_9,        KEY_KP9 },
-       { KEY_M,        KEY_KP0 },
-       { KEY_DOT,      KEY_KPDOT },
-       { KEY_SLASH,    KEY_KPPLUS },
-       { KEY_SEMICOLON, KEY_KPMINUS },
-       { KEY_P,        KEY_KPASTERISK },
-       { KEY_MINUS,    KEY_KPEQUAL },
-       { KEY_0,        KEY_KPSLASH },
-       { KEY_F6,       KEY_NUMLOCK },
-       { KEY_KPENTER,  KEY_KPENTER },
-       { KEY_BACKSPACE, KEY_BACKSPACE },
-       { }
-};
-
-static struct hidinput_key_translation apple_iso_keyboard[] = {
-       { KEY_GRAVE,    KEY_102ND },
-       { KEY_102ND,    KEY_GRAVE },
-       { }
-};
+#define map_abs(c)     hid_map_usage(hidinput, usage, &bit, &max, EV_ABS, (c))
+#define map_rel(c)     hid_map_usage(hidinput, usage, &bit, &max, EV_REL, (c))
+#define map_key(c)     hid_map_usage(hidinput, usage, &bit, &max, EV_KEY, (c))
+#define map_led(c)     hid_map_usage(hidinput, usage, &bit, &max, EV_LED, (c))
 
-static struct hidinput_key_translation *find_translation(struct hidinput_key_translation *table, u16 from)
-{
-       struct hidinput_key_translation *trans;
-
-       /* Look for the translation */
-       for (trans = table; trans->from; trans++)
-               if (trans->from == from)
-                       return trans;
-
-       return NULL;
-}
-
-int hidinput_apple_event(struct hid_device *hid, struct input_dev *input,
-               struct hid_usage *usage, __s32 value)
-{
-       struct hidinput_key_translation *trans;
-
-       if (usage->code == KEY_FN) {
-               if (value) hid->quirks |=  HID_QUIRK_APPLE_FN_ON;
-               else       hid->quirks &= ~HID_QUIRK_APPLE_FN_ON;
-
-               input_event(input, usage->type, usage->code, value);
-
-               return 1;
-       }
-
-       if (hid_apple_fnmode) {
-               int do_translate;
-
-               trans = find_translation((hid->product < 0x220 ||
-                                         hid->product >= 0x300) ?
-                                        powerbook_fn_keys : apple_fn_keys,
-                                        usage->code);
-               if (trans) {
-                       if (test_bit(usage->code, hid->apple_pressed_fn))
-                               do_translate = 1;
-                       else if (trans->flags & APPLE_FLAG_FKEY)
-                               do_translate =
-                                       (hid_apple_fnmode == 2 &&  (hid->quirks & HID_QUIRK_APPLE_FN_ON)) ||
-                                       (hid_apple_fnmode == 1 && !(hid->quirks & HID_QUIRK_APPLE_FN_ON));
-                       else
-                               do_translate = (hid->quirks & HID_QUIRK_APPLE_FN_ON);
-
-                       if (do_translate) {
-                               if (value)
-                                       set_bit(usage->code, hid->apple_pressed_fn);
-                               else
-                                       clear_bit(usage->code, hid->apple_pressed_fn);
-
-                               input_event(input, usage->type, trans->to, value);
-
-                               return 1;
-                       }
-               }
-
-               if (hid->quirks & HID_QUIRK_APPLE_NUMLOCK_EMULATION && (
-                               test_bit(usage->code, hid->pb_pressed_numlock) ||
-                               test_bit(LED_NUML, input->led))) {
-                       trans = find_translation(powerbook_numlock_keys, usage->code);
-
-                       if (trans) {
-                               if (value)
-                                       set_bit(usage->code, hid->pb_pressed_numlock);
-                               else
-                                       clear_bit(usage->code, hid->pb_pressed_numlock);
-
-                               input_event(input, usage->type, trans->to, value);
-                       }
-
-                       return 1;
-               }
-       }
-
-       if (hid->quirks & HID_QUIRK_APPLE_ISO_KEYBOARD) {
-               trans = find_translation(apple_iso_keyboard, usage->code);
-               if (trans) {
-                       input_event(input, usage->type, trans->to, value);
-                       return 1;
-               }
-       }
-
-       return 0;
-}
-
-static void hidinput_apple_setup(struct input_dev *input)
-{
-       struct hidinput_key_translation *trans;
-
-       set_bit(KEY_NUMLOCK, input->keybit);
-
-       /* Enable all needed keys */
-       for (trans = apple_fn_keys; trans->from; trans++)
-               set_bit(trans->to, input->keybit);
-
-       for (trans = powerbook_fn_keys; trans->from; trans++)
-               set_bit(trans->to, input->keybit);
-
-       for (trans = powerbook_numlock_keys; trans->from; trans++)
-               set_bit(trans->to, input->keybit);
-
-       for (trans = apple_iso_keyboard; trans->from; trans++)
-               set_bit(trans->to, input->keybit);
-
-}
-#else
-inline int hidinput_apple_event(struct hid_device *hid,
-                                      struct input_dev *input,
-                                      struct hid_usage *usage, __s32 value)
-{
-       return 0;
-}
-
-static inline void hidinput_apple_setup(struct input_dev *input)
-{
-}
-#endif
+#define map_abs_clear(c)       hid_map_usage_clear(hidinput, usage, &bit, \
+               &max, EV_ABS, (c))
+#define map_key_clear(c)       hid_map_usage_clear(hidinput, usage, &bit, \
+               &max, EV_KEY, (c))
 
 static inline int match_scancode(int code, int scancode)
 {
@@ -366,7 +154,7 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel
 {
        struct input_dev *input = hidinput->input;
        struct hid_device *device = input_get_drvdata(input);
-       int max = 0, code, ret;
+       int max = 0, code;
        unsigned long *bit = NULL;
 
        field->hidinput = hidinput;
@@ -385,406 +173,345 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel
                goto ignore;
        }
 
-       /* handle input mappings for quirky devices */
-       ret = hidinput_mapping_quirks(usage, input, &bit, &max);
-       if (ret)
-               goto mapped;
+       if (device->driver->input_mapping) {
+               int ret = device->driver->input_mapping(device, hidinput, field,
+                               usage, &bit, &max);
+               if (ret > 0)
+                       goto mapped;
+               if (ret < 0)
+                       goto ignore;
+       }
 
        switch (usage->hid & HID_USAGE_PAGE) {
+       case HID_UP_UNDEFINED:
+               goto ignore;
 
-               case HID_UP_UNDEFINED:
-                       goto ignore;
-
-               case HID_UP_KEYBOARD:
+       case HID_UP_KEYBOARD:
+               set_bit(EV_REP, input->evbit);
 
-                       set_bit(EV_REP, input->evbit);
+               if ((usage->hid & HID_USAGE) < 256) {
+                       if (!hid_keyboard[usage->hid & HID_USAGE]) goto ignore;
+                       map_key_clear(hid_keyboard[usage->hid & HID_USAGE]);
+               } else
+                       map_key(KEY_UNKNOWN);
 
-                       if ((usage->hid & HID_USAGE) < 256) {
-                               if (!hid_keyboard[usage->hid & HID_USAGE]) goto ignore;
-                               map_key_clear(hid_keyboard[usage->hid & HID_USAGE]);
-                       } else
-                               map_key(KEY_UNKNOWN);
+               break;
 
-                       break;
+       case HID_UP_BUTTON:
+               code = ((usage->hid - 1) & 0xf);
 
-               case HID_UP_BUTTON:
-
-                       code = ((usage->hid - 1) & 0xf);
-
-                       switch (field->application) {
-                               case HID_GD_MOUSE:
-                               case HID_GD_POINTER:  code += 0x110; break;
-                               case HID_GD_JOYSTICK: code += 0x120; break;
-                               case HID_GD_GAMEPAD:  code += 0x130; break;
-                               default:
-                                       switch (field->physical) {
-                                               case HID_GD_MOUSE:
-                                               case HID_GD_POINTER:  code += 0x110; break;
-                                               case HID_GD_JOYSTICK: code += 0x120; break;
-                                               case HID_GD_GAMEPAD:  code += 0x130; break;
-                                               default:              code += 0x100;
-                                       }
-                       }
-
-                       /* Special handling for Logitech Cordless Desktop */
-                       if (field->application != HID_GD_MOUSE) {
-                               if (device->quirks & HID_QUIRK_LOGITECH_EXPANDED_KEYMAP) {
-                                       int hid = usage->hid & HID_USAGE;
-                                       if (hid < LOGITECH_EXPANDED_KEYMAP_SIZE && logitech_expanded_keymap[hid] != 0)
-                                               code = logitech_expanded_keymap[hid];
-                               }
-                       } else {
-                               if (device->quirks & HID_QUIRK_LOGITECH_IGNORE_DOUBLED_WHEEL) {
-                                       int hid = usage->hid & HID_USAGE;
-                                       if (hid == 7 || hid == 8)
-                                               goto ignore;
-                               }
+               switch (field->application) {
+               case HID_GD_MOUSE:
+               case HID_GD_POINTER:  code += 0x110; break;
+               case HID_GD_JOYSTICK: code += 0x120; break;
+               case HID_GD_GAMEPAD:  code += 0x130; break;
+               default:
+                       switch (field->physical) {
+                       case HID_GD_MOUSE:
+                       case HID_GD_POINTER:  code += 0x110; break;
+                       case HID_GD_JOYSTICK: code += 0x120; break;
+                       case HID_GD_GAMEPAD:  code += 0x130; break;
+                       default:              code += 0x100;
                        }
+               }
 
-                       map_key(code);
-                       break;
-
-
-               case HID_UP_SIMULATION:
-
-                       switch (usage->hid & 0xffff) {
-                               case 0xba: map_abs(ABS_RUDDER);   break;
-                               case 0xbb: map_abs(ABS_THROTTLE); break;
-                               case 0xc4: map_abs(ABS_GAS);      break;
-                               case 0xc5: map_abs(ABS_BRAKE);    break;
-                               case 0xc8: map_abs(ABS_WHEEL);    break;
-                               default:   goto ignore;
+               map_key(code);
+               break;
+
+       case HID_UP_SIMULATION:
+               switch (usage->hid & 0xffff) {
+               case 0xba: map_abs(ABS_RUDDER);   break;
+               case 0xbb: map_abs(ABS_THROTTLE); break;
+               case 0xc4: map_abs(ABS_GAS);      break;
+               case 0xc5: map_abs(ABS_BRAKE);    break;
+               case 0xc8: map_abs(ABS_WHEEL);    break;
+               default:   goto ignore;
+               }
+               break;
+
+       case HID_UP_GENDESK:
+               if ((usage->hid & 0xf0) == 0x80) {      /* SystemControl */
+                       switch (usage->hid & 0xf) {
+                       case 0x1: map_key_clear(KEY_POWER);  break;
+                       case 0x2: map_key_clear(KEY_SLEEP);  break;
+                       case 0x3: map_key_clear(KEY_WAKEUP); break;
+                       default: goto unknown;
                        }
                        break;
+               }
 
-               case HID_UP_GENDESK:
-
-                       if ((usage->hid & 0xf0) == 0x80) {      /* SystemControl */
-                               switch (usage->hid & 0xf) {
-                                       case 0x1: map_key_clear(KEY_POWER);  break;
-                                       case 0x2: map_key_clear(KEY_SLEEP);  break;
-                                       case 0x3: map_key_clear(KEY_WAKEUP); break;
-                                       default: goto unknown;
-                               }
-                               break;
-                       }
-
-                       if ((usage->hid & 0xf0) == 0x90) {      /* D-pad */
-                               switch (usage->hid) {
-                                       case HID_GD_UP:    usage->hat_dir = 1; break;
-                                       case HID_GD_DOWN:  usage->hat_dir = 5; break;
-                                       case HID_GD_RIGHT: usage->hat_dir = 3; break;
-                                       case HID_GD_LEFT:  usage->hat_dir = 7; break;
-                                       default: goto unknown;
-                               }
-                               if (field->dpad) {
-                                       map_abs(field->dpad);
-                                       goto ignore;
-                               }
-                               map_abs(ABS_HAT0X);
-                               break;
-                       }
-
+               if ((usage->hid & 0xf0) == 0x90) {      /* D-pad */
                        switch (usage->hid) {
-
-                               /* These usage IDs map directly to the usage codes. */
-                               case HID_GD_X: case HID_GD_Y: case HID_GD_Z:
-                               case HID_GD_RX: case HID_GD_RY: case HID_GD_RZ:
-                               case HID_GD_SLIDER: case HID_GD_DIAL: case HID_GD_WHEEL:
-                                       if (field->flags & HID_MAIN_ITEM_RELATIVE)
-                                               map_rel(usage->hid & 0xf);
-                                       else
-                                               map_abs(usage->hid & 0xf);
-                                       break;
-
-                               case HID_GD_HATSWITCH:
-                                       usage->hat_min = field->logical_minimum;
-                                       usage->hat_max = field->logical_maximum;
-                                       map_abs(ABS_HAT0X);
-                                       break;
-
-                               case HID_GD_START:      map_key_clear(BTN_START);       break;
-                               case HID_GD_SELECT:     map_key_clear(BTN_SELECT);      break;
-
-                               default: goto unknown;
+                       case HID_GD_UP:    usage->hat_dir = 1; break;
+                       case HID_GD_DOWN:  usage->hat_dir = 5; break;
+                       case HID_GD_RIGHT: usage->hat_dir = 3; break;
+                       case HID_GD_LEFT:  usage->hat_dir = 7; break;
+                       default: goto unknown;
                        }
-
-                       break;
-
-               case HID_UP_LED:
-
-                       switch (usage->hid & 0xffff) {                        /* HID-Value:                   */
-                               case 0x01:  map_led (LED_NUML);     break;    /*   "Num Lock"                 */
-                               case 0x02:  map_led (LED_CAPSL);    break;    /*   "Caps Lock"                */
-                               case 0x03:  map_led (LED_SCROLLL);  break;    /*   "Scroll Lock"              */
-                               case 0x04:  map_led (LED_COMPOSE);  break;    /*   "Compose"                  */
-                               case 0x05:  map_led (LED_KANA);     break;    /*   "Kana"                     */
-                               case 0x27:  map_led (LED_SLEEP);    break;    /*   "Stand-By"                 */
-                               case 0x4c:  map_led (LED_SUSPEND);  break;    /*   "System Suspend"           */
-                               case 0x09:  map_led (LED_MUTE);     break;    /*   "Mute"                     */
-                               case 0x4b:  map_led (LED_MISC);     break;    /*   "Generic Indicator"        */
-                               case 0x19:  map_led (LED_MAIL);     break;    /*   "Message Waiting"          */
-                               case 0x4d:  map_led (LED_CHARGING); break;    /*   "External Power Connected" */
-
-                               default: goto ignore;
+                       if (field->dpad) {
+                               map_abs(field->dpad);
+                               goto ignore;
                        }
+                       map_abs(ABS_HAT0X);
                        break;
+               }
 
-               case HID_UP_DIGITIZER:
-
-                       switch (usage->hid & 0xff) {
-
-                               case 0x30: /* TipPressure */
-                                       if (!test_bit(BTN_TOUCH, input->keybit)) {
-                                               device->quirks |= HID_QUIRK_NOTOUCH;
-                                               set_bit(EV_KEY, input->evbit);
-                                               set_bit(BTN_TOUCH, input->keybit);
-                                       }
-
-                                       map_abs_clear(ABS_PRESSURE);
-                                       break;
-
-                               case 0x32: /* InRange */
-                                       switch (field->physical & 0xff) {
-                                               case 0x21: map_key(BTN_TOOL_MOUSE); break;
-                                               case 0x22: map_key(BTN_TOOL_FINGER); break;
-                                               default: map_key(BTN_TOOL_PEN); break;
-                                       }
-                                       break;
+               switch (usage->hid) {
+               /* These usage IDs map directly to the usage codes. */
+               case HID_GD_X: case HID_GD_Y: case HID_GD_Z:
+               case HID_GD_RX: case HID_GD_RY: case HID_GD_RZ:
+               case HID_GD_SLIDER: case HID_GD_DIAL: case HID_GD_WHEEL:
+                       if (field->flags & HID_MAIN_ITEM_RELATIVE)
+                               map_rel(usage->hid & 0xf);
+                       else
+                               map_abs(usage->hid & 0xf);
+                       break;
 
-                               case 0x3c: /* Invert */
-                                       map_key_clear(BTN_TOOL_RUBBER);
-                                       break;
+               case HID_GD_HATSWITCH:
+                       usage->hat_min = field->logical_minimum;
+                       usage->hat_max = field->logical_maximum;
+                       map_abs(ABS_HAT0X);
+                       break;
 
-                               case 0x33: /* Touch */
-                               case 0x42: /* TipSwitch */
-                               case 0x43: /* TipSwitch2 */
-                                       device->quirks &= ~HID_QUIRK_NOTOUCH;
-                                       map_key_clear(BTN_TOUCH);
-                                       break;
+               case HID_GD_START:      map_key_clear(BTN_START);       break;
+               case HID_GD_SELECT:     map_key_clear(BTN_SELECT);      break;
 
-                               case 0x44: /* BarrelSwitch */
-                                       map_key_clear(BTN_STYLUS);
-                                       break;
+               default: goto unknown;
+               }
 
-                               default:  goto unknown;
+               break;
+
+       case HID_UP_LED:
+               switch (usage->hid & 0xffff) {                /* HID-Value:                   */
+               case 0x01:  map_led (LED_NUML);     break;    /*   "Num Lock"                 */
+               case 0x02:  map_led (LED_CAPSL);    break;    /*   "Caps Lock"                */
+               case 0x03:  map_led (LED_SCROLLL);  break;    /*   "Scroll Lock"              */
+               case 0x04:  map_led (LED_COMPOSE);  break;    /*   "Compose"                  */
+               case 0x05:  map_led (LED_KANA);     break;    /*   "Kana"                     */
+               case 0x27:  map_led (LED_SLEEP);    break;    /*   "Stand-By"                 */
+               case 0x4c:  map_led (LED_SUSPEND);  break;    /*   "System Suspend"           */
+               case 0x09:  map_led (LED_MUTE);     break;    /*   "Mute"                     */
+               case 0x4b:  map_led (LED_MISC);     break;    /*   "Generic Indicator"        */
+               case 0x19:  map_led (LED_MAIL);     break;    /*   "Message Waiting"          */
+               case 0x4d:  map_led (LED_CHARGING); break;    /*   "External Power Connected" */
+
+               default: goto ignore;
+               }
+               break;
+
+       case HID_UP_DIGITIZER:
+               switch (usage->hid & 0xff) {
+               case 0x30: /* TipPressure */
+                       if (!test_bit(BTN_TOUCH, input->keybit)) {
+                               device->quirks |= HID_QUIRK_NOTOUCH;
+                               set_bit(EV_KEY, input->evbit);
+                               set_bit(BTN_TOUCH, input->keybit);
                        }
+                       map_abs_clear(ABS_PRESSURE);
                        break;
 
-               case HID_UP_CONSUMER:   /* USB HUT v1.1, pages 56-62 */
-
-                       switch (usage->hid & HID_USAGE) {
-                               case 0x000: goto ignore;
-                               case 0x034: map_key_clear(KEY_SLEEP);           break;
-                               case 0x036: map_key_clear(BTN_MISC);            break;
-
-                               case 0x040: map_key_clear(KEY_MENU);            break;
-                               case 0x045: map_key_clear(KEY_RADIO);           break;
-
-                               case 0x083: map_key_clear(KEY_LAST);            break;
-                               case 0x088: map_key_clear(KEY_PC);              break;
-                               case 0x089: map_key_clear(KEY_TV);              break;
-                               case 0x08a: map_key_clear(KEY_WWW);             break;
-                               case 0x08b: map_key_clear(KEY_DVD);             break;
-                               case 0x08c: map_key_clear(KEY_PHONE);           break;
-                               case 0x08d: map_key_clear(KEY_PROGRAM);         break;
-                               case 0x08e: map_key_clear(KEY_VIDEOPHONE);      break;
-                               case 0x08f: map_key_clear(KEY_GAMES);           break;
-                               case 0x090: map_key_clear(KEY_MEMO);            break;
-                               case 0x091: map_key_clear(KEY_CD);              break;
-                               case 0x092: map_key_clear(KEY_VCR);             break;
-                               case 0x093: map_key_clear(KEY_TUNER);           break;
-                               case 0x094: map_key_clear(KEY_EXIT);            break;
-                               case 0x095: map_key_clear(KEY_HELP);            break;
-                               case 0x096: map_key_clear(KEY_TAPE);            break;
-                               case 0x097: map_key_clear(KEY_TV2);             break;
-                               case 0x098: map_key_clear(KEY_SAT);             break;
-                               case 0x09a: map_key_clear(KEY_PVR);             break;
-
-                               case 0x09c: map_key_clear(KEY_CHANNELUP);       break;
-                               case 0x09d: map_key_clear(KEY_CHANNELDOWN);     break;
-                               case 0x0a0: map_key_clear(KEY_VCR2);            break;
-
-                               case 0x0b0: map_key_clear(KEY_PLAY);            break;
-                               case 0x0b1: map_key_clear(KEY_PAUSE);           break;
-                               case 0x0b2: map_key_clear(KEY_RECORD);          break;
-                               case 0x0b3: map_key_clear(KEY_FASTFORWARD);     break;
-                               case 0x0b4: map_key_clear(KEY_REWIND);          break;
-                               case 0x0b5: map_key_clear(KEY_NEXTSONG);        break;
-                               case 0x0b6: map_key_clear(KEY_PREVIOUSSONG);    break;
-                               case 0x0b7: map_key_clear(KEY_STOPCD);          break;
-                               case 0x0b8: map_key_clear(KEY_EJECTCD);         break;
-                               case 0x0bc: map_key_clear(KEY_MEDIA_REPEAT);    break;
-
-                               case 0x0cd: map_key_clear(KEY_PLAYPAUSE);       break;
-                               case 0x0e0: map_abs_clear(ABS_VOLUME);          break;
-                               case 0x0e2: map_key_clear(KEY_MUTE);            break;
-                               case 0x0e5: map_key_clear(KEY_BASSBOOST);       break;
-                               case 0x0e9: map_key_clear(KEY_VOLUMEUP);        break;
-                               case 0x0ea: map_key_clear(KEY_VOLUMEDOWN);      break;
-
-                               case 0x182: map_key_clear(KEY_BOOKMARKS);       break;
-                               case 0x183: map_key_clear(KEY_CONFIG);          break;
-                               case 0x184: map_key_clear(KEY_WORDPROCESSOR);   break;
-                               case 0x185: map_key_clear(KEY_EDITOR);          break;
-                               case 0x186: map_key_clear(KEY_SPREADSHEET);     break;
-                               case 0x187: map_key_clear(KEY_GRAPHICSEDITOR);  break;
-                               case 0x188: map_key_clear(KEY_PRESENTATION);    break;
-                               case 0x189: map_key_clear(KEY_DATABASE);        break;
-                               case 0x18a: map_key_clear(KEY_MAIL);            break;
-                               case 0x18b: map_key_clear(KEY_NEWS);            break;
-                               case 0x18c: map_key_clear(KEY_VOICEMAIL);       break;
-                               case 0x18d: map_key_clear(KEY_ADDRESSBOOK);     break;
-                               case 0x18e: map_key_clear(KEY_CALENDAR);        break;
-                               case 0x191: map_key_clear(KEY_FINANCE);         break;
-                               case 0x192: map_key_clear(KEY_CALC);            break;
-                               case 0x194: map_key_clear(KEY_FILE);            break;
-                               case 0x196: map_key_clear(KEY_WWW);             break;
-                               case 0x19c: map_key_clear(KEY_LOGOFF);          break;
-                               case 0x19e: map_key_clear(KEY_COFFEE);          break;
-                               case 0x1a6: map_key_clear(KEY_HELP);            break;
-                               case 0x1a7: map_key_clear(KEY_DOCUMENTS);       break;
-                               case 0x1ab: map_key_clear(KEY_SPELLCHECK);      break;
-                               case 0x1b6: map_key_clear(KEY_MEDIA);           break;
-                               case 0x1b7: map_key_clear(KEY_SOUND);           break;
-                               case 0x1bc: map_key_clear(KEY_MESSENGER);       break;
-                               case 0x1bd: map_key_clear(KEY_INFO);            break;
-                               case 0x201: map_key_clear(KEY_NEW);             break;
-                               case 0x202: map_key_clear(KEY_OPEN);            break;
-                               case 0x203: map_key_clear(KEY_CLOSE);           break;
-                               case 0x204: map_key_clear(KEY_EXIT);            break;
-                               case 0x207: map_key_clear(KEY_SAVE);            break;
-                               case 0x208: map_key_clear(KEY_PRINT);           break;
-                               case 0x209: map_key_clear(KEY_PROPS);           break;
-                               case 0x21a: map_key_clear(KEY_UNDO);            break;
-                               case 0x21b: map_key_clear(KEY_COPY);            break;
-                               case 0x21c: map_key_clear(KEY_CUT);             break;
-                               case 0x21d: map_key_clear(KEY_PASTE);           break;
-                               case 0x21f: map_key_clear(KEY_FIND);            break;
-                               case 0x221: map_key_clear(KEY_SEARCH);          break;
-                               case 0x222: map_key_clear(KEY_GOTO);            break;
-                               case 0x223: map_key_clear(KEY_HOMEPAGE);        break;
-                               case 0x224: map_key_clear(KEY_BACK);            break;
-                               case 0x225: map_key_clear(KEY_FORWARD);         break;
-                               case 0x226: map_key_clear(KEY_STOP);            break;
-                               case 0x227: map_key_clear(KEY_REFRESH);         break;
-                               case 0x22a: map_key_clear(KEY_BOOKMARKS);       break;
-                               case 0x22d: map_key_clear(KEY_ZOOMIN);          break;
-                               case 0x22e: map_key_clear(KEY_ZOOMOUT);         break;
-                               case 0x22f: map_key_clear(KEY_ZOOMRESET);       break;
-                               case 0x233: map_key_clear(KEY_SCROLLUP);        break;
-                               case 0x234: map_key_clear(KEY_SCROLLDOWN);      break;
-                               case 0x238: map_rel(REL_HWHEEL);                break;
-                               case 0x25f: map_key_clear(KEY_CANCEL);          break;
-                               case 0x279: map_key_clear(KEY_REDO);            break;
-
-                               case 0x289: map_key_clear(KEY_REPLY);           break;
-                               case 0x28b: map_key_clear(KEY_FORWARDMAIL);     break;
-                               case 0x28c: map_key_clear(KEY_SEND);            break;
-
-                               default:    goto ignore;
+               case 0x32: /* InRange */
+                       switch (field->physical & 0xff) {
+                       case 0x21: map_key(BTN_TOOL_MOUSE); break;
+                       case 0x22: map_key(BTN_TOOL_FINGER); break;
+                       default: map_key(BTN_TOOL_PEN); break;
                        }
                        break;
 
-               case HID_UP_HPVENDOR:   /* Reported on a Dutch layout HP5308 */
-
-                       set_bit(EV_REP, input->evbit);
-                       switch (usage->hid & HID_USAGE) {
-                               case 0x021: map_key_clear(KEY_PRINT);           break;
-                               case 0x070: map_key_clear(KEY_HP);              break;
-                               case 0x071: map_key_clear(KEY_CAMERA);          break;
-                               case 0x072: map_key_clear(KEY_SOUND);           break;
-                               case 0x073: map_key_clear(KEY_QUESTION);        break;
-                               case 0x080: map_key_clear(KEY_EMAIL);           break;
-                               case 0x081: map_key_clear(KEY_CHAT);            break;
-                               case 0x082: map_key_clear(KEY_SEARCH);          break;
-                               case 0x083: map_key_clear(KEY_CONNECT);         break;
-                               case 0x084: map_key_clear(KEY_FINANCE);         break;
-                               case 0x085: map_key_clear(KEY_SPORT);           break;
-                               case 0x086: map_key_clear(KEY_SHOP);            break;
-                               default:    goto ignore;
-                       }
+               case 0x3c: /* Invert */
+                       map_key_clear(BTN_TOOL_RUBBER);
                        break;
 
-               case HID_UP_MSVENDOR:
-
-                       goto ignore;
-
-               case HID_UP_CUSTOM: /* Reported on Logitech and Apple USB keyboards */
-
-                       set_bit(EV_REP, input->evbit);
-                       switch(usage->hid & HID_USAGE) {
-                               case 0x003:
-                                       /* The fn key on Apple USB keyboards */
-                                       map_key_clear(KEY_FN);
-                                       hidinput_apple_setup(input);
-                                       break;
+               case 0x33: /* Touch */
+               case 0x42: /* TipSwitch */
+               case 0x43: /* TipSwitch2 */
+                       device->quirks &= ~HID_QUIRK_NOTOUCH;
+                       map_key_clear(BTN_TOUCH);
+                       break;
 
-                               default:    goto ignore;
-                       }
+               case 0x44: /* BarrelSwitch */
+                       map_key_clear(BTN_STYLUS);
                        break;
 
-               case HID_UP_LOGIVENDOR:
+               default:  goto unknown;
+               }
+               break;
+
+       case HID_UP_CONSUMER:   /* USB HUT v1.1, pages 56-62 */
+               switch (usage->hid & HID_USAGE) {
+               case 0x000: goto ignore;
+               case 0x034: map_key_clear(KEY_SLEEP);           break;
+               case 0x036: map_key_clear(BTN_MISC);            break;
+
+               case 0x040: map_key_clear(KEY_MENU);            break;
+               case 0x045: map_key_clear(KEY_RADIO);           break;
+
+               case 0x083: map_key_clear(KEY_LAST);            break;
+               case 0x088: map_key_clear(KEY_PC);              break;
+               case 0x089: map_key_clear(KEY_TV);              break;
+               case 0x08a: map_key_clear(KEY_WWW);             break;
+               case 0x08b: map_key_clear(KEY_DVD);             break;
+               case 0x08c: map_key_clear(KEY_PHONE);           break;
+               case 0x08d: map_key_clear(KEY_PROGRAM);         break;
+               case 0x08e: map_key_clear(KEY_VIDEOPHONE);      break;
+               case 0x08f: map_key_clear(KEY_GAMES);           break;
+               case 0x090: map_key_clear(KEY_MEMO);            break;
+               case 0x091: map_key_clear(KEY_CD);              break;
+               case 0x092: map_key_clear(KEY_VCR);             break;
+               case 0x093: map_key_clear(KEY_TUNER);           break;
+               case 0x094: map_key_clear(KEY_EXIT);            break;
+               case 0x095: map_key_clear(KEY_HELP);            break;
+               case 0x096: map_key_clear(KEY_TAPE);            break;
+               case 0x097: map_key_clear(KEY_TV2);             break;
+               case 0x098: map_key_clear(KEY_SAT);             break;
+               case 0x09a: map_key_clear(KEY_PVR);             break;
+
+               case 0x09c: map_key_clear(KEY_CHANNELUP);       break;
+               case 0x09d: map_key_clear(KEY_CHANNELDOWN);     break;
+               case 0x0a0: map_key_clear(KEY_VCR2);            break;
+
+               case 0x0b0: map_key_clear(KEY_PLAY);            break;
+               case 0x0b1: map_key_clear(KEY_PAUSE);           break;
+               case 0x0b2: map_key_clear(KEY_RECORD);          break;
+               case 0x0b3: map_key_clear(KEY_FASTFORWARD);     break;
+               case 0x0b4: map_key_clear(KEY_REWIND);          break;
+               case 0x0b5: map_key_clear(KEY_NEXTSONG);        break;
+               case 0x0b6: map_key_clear(KEY_PREVIOUSSONG);    break;
+               case 0x0b7: map_key_clear(KEY_STOPCD);          break;
+               case 0x0b8: map_key_clear(KEY_EJECTCD);         break;
+               case 0x0bc: map_key_clear(KEY_MEDIA_REPEAT);    break;
+
+               case 0x0cd: map_key_clear(KEY_PLAYPAUSE);       break;
+               case 0x0e0: map_abs_clear(ABS_VOLUME);          break;
+               case 0x0e2: map_key_clear(KEY_MUTE);            break;
+               case 0x0e5: map_key_clear(KEY_BASSBOOST);       break;
+               case 0x0e9: map_key_clear(KEY_VOLUMEUP);        break;
+               case 0x0ea: map_key_clear(KEY_VOLUMEDOWN);      break;
+
+               case 0x182: map_key_clear(KEY_BOOKMARKS);       break;
+               case 0x183: map_key_clear(KEY_CONFIG);          break;
+               case 0x184: map_key_clear(KEY_WORDPROCESSOR);   break;
+               case 0x185: map_key_clear(KEY_EDITOR);          break;
+               case 0x186: map_key_clear(KEY_SPREADSHEET);     break;
+               case 0x187: map_key_clear(KEY_GRAPHICSEDITOR);  break;
+               case 0x188: map_key_clear(KEY_PRESENTATION);    break;
+               case 0x189: map_key_clear(KEY_DATABASE);        break;
+               case 0x18a: map_key_clear(KEY_MAIL);            break;
+               case 0x18b: map_key_clear(KEY_NEWS);            break;
+               case 0x18c: map_key_clear(KEY_VOICEMAIL);       break;
+               case 0x18d: map_key_clear(KEY_ADDRESSBOOK);     break;
+               case 0x18e: map_key_clear(KEY_CALENDAR);        break;
+               case 0x191: map_key_clear(KEY_FINANCE);         break;
+               case 0x192: map_key_clear(KEY_CALC);            break;
+               case 0x194: map_key_clear(KEY_FILE);            break;
+               case 0x196: map_key_clear(KEY_WWW);             break;
+               case 0x19c: map_key_clear(KEY_LOGOFF);          break;
+               case 0x19e: map_key_clear(KEY_COFFEE);          break;
+               case 0x1a6: map_key_clear(KEY_HELP);            break;
+               case 0x1a7: map_key_clear(KEY_DOCUMENTS);       break;
+               case 0x1ab: map_key_clear(KEY_SPELLCHECK);      break;
+               case 0x1b6: map_key_clear(KEY_MEDIA);           break;
+               case 0x1b7: map_key_clear(KEY_SOUND);           break;
+               case 0x1bc: map_key_clear(KEY_MESSENGER);       break;
+               case 0x1bd: map_key_clear(KEY_INFO);            break;
+               case 0x201: map_key_clear(KEY_NEW);             break;
+               case 0x202: map_key_clear(KEY_OPEN);            break;
+               case 0x203: map_key_clear(KEY_CLOSE);           break;
+               case 0x204: map_key_clear(KEY_EXIT);            break;
+               case 0x207: map_key_clear(KEY_SAVE);            break;
+               case 0x208: map_key_clear(KEY_PRINT);           break;
+               case 0x209: map_key_clear(KEY_PROPS);           break;
+               case 0x21a: map_key_clear(KEY_UNDO);            break;
+               case 0x21b: map_key_clear(KEY_COPY);            break;
+               case 0x21c: map_key_clear(KEY_CUT);             break;
+               case 0x21d: map_key_clear(KEY_PASTE);           break;
+               case 0x21f: map_key_clear(KEY_FIND);            break;
+               case 0x221: map_key_clear(KEY_SEARCH);          break;
+               case 0x222: map_key_clear(KEY_GOTO);            break;
+               case 0x223: map_key_clear(KEY_HOMEPAGE);        break;
+               case 0x224: map_key_clear(KEY_BACK);            break;
+               case 0x225: map_key_clear(KEY_FORWARD);         break;
+               case 0x226: map_key_clear(KEY_STOP);            break;
+               case 0x227: map_key_clear(KEY_REFRESH);         break;
+               case 0x22a: map_key_clear(KEY_BOOKMARKS);       break;
+               case 0x22d: map_key_clear(KEY_ZOOMIN);          break;
+               case 0x22e: map_key_clear(KEY_ZOOMOUT);         break;
+               case 0x22f: map_key_clear(KEY_ZOOMRESET);       break;
+               case 0x233: map_key_clear(KEY_SCROLLUP);        break;
+               case 0x234: map_key_clear(KEY_SCROLLDOWN);      break;
+               case 0x238: map_rel(REL_HWHEEL);                break;
+               case 0x25f: map_key_clear(KEY_CANCEL);          break;
+               case 0x279: map_key_clear(KEY_REDO);            break;
+
+               case 0x289: map_key_clear(KEY_REPLY);           break;
+               case 0x28b: map_key_clear(KEY_FORWARDMAIL);     break;
+               case 0x28c: map_key_clear(KEY_SEND);            break;
+
+               default:    goto ignore;
+               }
+               break;
+
+       case HID_UP_HPVENDOR:   /* Reported on a Dutch layout HP5308 */
+               set_bit(EV_REP, input->evbit);
+               switch (usage->hid & HID_USAGE) {
+               case 0x021: map_key_clear(KEY_PRINT);           break;
+               case 0x070: map_key_clear(KEY_HP);              break;
+               case 0x071: map_key_clear(KEY_CAMERA);          break;
+               case 0x072: map_key_clear(KEY_SOUND);           break;
+               case 0x073: map_key_clear(KEY_QUESTION);        break;
+               case 0x080: map_key_clear(KEY_EMAIL);           break;
+               case 0x081: map_key_clear(KEY_CHAT);            break;
+               case 0x082: map_key_clear(KEY_SEARCH);          break;
+               case 0x083: map_key_clear(KEY_CONNECT);         break;
+               case 0x084: map_key_clear(KEY_FINANCE);         break;
+               case 0x085: map_key_clear(KEY_SPORT);           break;
+               case 0x086: map_key_clear(KEY_SHOP);            break;
+               default:    goto ignore;
+               }
+               break;
 
-                       goto ignore;
-               
-               case HID_UP_PID:
+       case HID_UP_MSVENDOR:
+               goto ignore;
 
-                       switch(usage->hid & HID_USAGE) {
-                               case 0xa4: map_key_clear(BTN_DEAD);     break;
-                               default: goto ignore;
-                       }
-                       break;
+       case HID_UP_CUSTOM: /* Reported on Logitech and Apple USB keyboards */
+               set_bit(EV_REP, input->evbit);
+               goto ignore;
 
-               default:
-               unknown:
-                       if (field->report_size == 1) {
-                               if (field->report->type == HID_OUTPUT_REPORT) {
-                                       map_led(LED_MISC);
-                                       break;
-                               }
-                               map_key(BTN_MISC);
-                               break;
-                       }
-                       if (field->flags & HID_MAIN_ITEM_RELATIVE) {
-                               map_rel(REL_MISC);
+       case HID_UP_LOGIVENDOR:
+               goto ignore;
+       
+       case HID_UP_PID:
+               switch (usage->hid & HID_USAGE) {
+               case 0xa4: map_key_clear(BTN_DEAD);     break;
+               default: goto ignore;
+               }
+               break;
+
+       default:
+       unknown:
+               if (field->report_size == 1) {
+                       if (field->report->type == HID_OUTPUT_REPORT) {
+                               map_led(LED_MISC);
                                break;
                        }
-                       map_abs(ABS_MISC);
+                       map_key(BTN_MISC);
+                       break;
+               }
+               if (field->flags & HID_MAIN_ITEM_RELATIVE) {
+                       map_rel(REL_MISC);
                        break;
+               }
+               map_abs(ABS_MISC);
+               break;
        }
 
 mapped:
-       if (device->quirks & HID_QUIRK_MIGHTYMOUSE) {
-               if (usage->hid == HID_GD_Z)
-                       map_rel(REL_HWHEEL);
-               else if (usage->code == BTN_1)
-                       map_key(BTN_2);
-               else if (usage->code == BTN_2)
-                       map_key(BTN_1);
-       }
-
-       if ((device->quirks & (HID_QUIRK_2WHEEL_MOUSE_HACK_7 | HID_QUIRK_2WHEEL_MOUSE_HACK_5 |
-                       HID_QUIRK_2WHEEL_MOUSE_HACK_B8)) && (usage->type == EV_REL) &&
-                       (usage->code == REL_WHEEL))
-               set_bit(REL_HWHEEL, bit);
-
-       if (((device->quirks & HID_QUIRK_2WHEEL_MOUSE_HACK_5) && (usage->hid == 0x00090005))
-               || ((device->quirks & HID_QUIRK_2WHEEL_MOUSE_HACK_7) && (usage->hid == 0x00090007)))
+       if (device->driver->input_mapped && device->driver->input_mapped(device,
+                               hidinput, field, usage, &bit, &max) < 0)
                goto ignore;
 
-       if ((device->quirks & HID_QUIRK_BAD_RELATIVE_KEYS) &&
-               usage->type == EV_KEY && (field->flags & HID_MAIN_ITEM_RELATIVE))
-               field->flags &= ~HID_MAIN_ITEM_RELATIVE;
-
        set_bit(usage->type, input->evbit);
 
-       if (device->quirks & HID_QUIRK_DUPLICATE_USAGES &&
-                       (usage->type == EV_KEY ||
-                        usage->type == EV_REL ||
-                        usage->type == EV_ABS))
-               clear_bit(usage->code, bit);
-
        while (usage->code <= max && test_and_set_bit(usage->code, bit))
                usage->code = find_next_zero_bit(bit, max + 1, usage->code);
 
@@ -858,10 +585,6 @@ void hidinput_hid_event(struct hid_device *hid, struct hid_field *field, struct
        if (!usage->type)
                return;
 
-       /* handle input events for quirky devices */
-       if (hidinput_event_quirks(hid, field, usage, value))
-               return;
-
        if (usage->hat_min < usage->hat_max || usage->hat_dir) {
                int hat_dir = usage->hat_dir;
                if (!hat_dir)
@@ -961,14 +684,14 @@ static int hidinput_open(struct input_dev *dev)
 {
        struct hid_device *hid = input_get_drvdata(dev);
 
-       return hid->hid_open(hid);
+       return hid->ll_driver->open(hid);
 }
 
 static void hidinput_close(struct input_dev *dev)
 {
        struct hid_device *hid = input_get_drvdata(dev);
 
-       hid->hid_close(hid);
+       hid->ll_driver->close(hid);
 }
 
 /*
@@ -977,7 +700,7 @@ static void hidinput_close(struct input_dev *dev)
  * Read all reports and initialize the absolute field values.
  */
 
-int hidinput_connect(struct hid_device *hid)
+int hidinput_connect(struct hid_device *hid, unsigned int force)
 {
        struct hid_report *report;
        struct hid_input *hidinput = NULL;
@@ -985,19 +708,20 @@ int hidinput_connect(struct hid_device *hid)
        int i, j, k;
        int max_report_type = HID_OUTPUT_REPORT;
 
-       if (hid->quirks & HID_QUIRK_IGNORE_HIDINPUT)
-               return -1;
-
        INIT_LIST_HEAD(&hid->inputs);
 
-       for (i = 0; i < hid->maxcollection; i++)
-               if (hid->collection[i].type == HID_COLLECTION_APPLICATION ||
-                   hid->collection[i].type == HID_COLLECTION_PHYSICAL)
-                       if (IS_INPUT_APPLICATION(hid->collection[i].usage))
-                               break;
+       if (!force) {
+               for (i = 0; i < hid->maxcollection; i++) {
+                       struct hid_collection *col = &hid->collection[i];
+                       if (col->type == HID_COLLECTION_APPLICATION ||
+                                       col->type == HID_COLLECTION_PHYSICAL)
+                               if (IS_INPUT_APPLICATION(col->usage))
+                                       break;
+               }
 
-       if (i == hid->maxcollection && (hid->quirks & HID_QUIRK_HIDINPUT) == 0)
-               return -1;
+               if (i == hid->maxcollection)
+                       return -1;
+       }
 
        if (hid->quirks & HID_QUIRK_SKIP_OUTPUT_REPORTS)
                max_report_type = HID_INPUT_REPORT;
@@ -1019,7 +743,8 @@ int hidinput_connect(struct hid_device *hid)
                                }
 
                                input_set_drvdata(input_dev, hid);
-                               input_dev->event = hid->hidinput_input_event;
+                               input_dev->event =
+                                       hid->ll_driver->hidinput_input_event;
                                input_dev->open = hidinput_open;
                                input_dev->close = hidinput_close;
                                input_dev->setkeycode = hidinput_setkeycode;
@@ -1032,7 +757,7 @@ int hidinput_connect(struct hid_device *hid)
                                input_dev->id.vendor  = hid->vendor;
                                input_dev->id.product = hid->product;
                                input_dev->id.version = hid->version;
-                               input_dev->dev.parent = hid->dev;
+                               input_dev->dev.parent = hid->dev.parent;
                                hidinput->input = input_dev;
                                list_add_tail(&hidinput->list, &hid->inputs);
                        }
diff --git a/drivers/hid/hid-lg.c b/drivers/hid/hid-lg.c
new file mode 100644 (file)
index 0000000..406d8c8
--- /dev/null
@@ -0,0 +1,342 @@
+/*
+ *  HID driver for some logitech "special" devices
+ *
+ *  Copyright (c) 1999 Andreas Gal
+ *  Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
+ *  Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
+ *  Copyright (c) 2006-2007 Jiri Kosina
+ *  Copyright (c) 2007 Paul Walmsley
+ *  Copyright (c) 2008 Jiri Slaby
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ */
+
+#include <linux/device.h>
+#include <linux/hid.h>
+#include <linux/module.h>
+
+#include "hid-ids.h"
+#include "hid-lg.h"
+
+#define LG_RDESC               0x001
+#define LG_BAD_RELATIVE_KEYS   0x002
+#define LG_DUPLICATE_USAGES    0x004
+#define LG_RESET_LEDS          0x008
+#define LG_EXPANDED_KEYMAP     0x010
+#define LG_IGNORE_DOUBLED_WHEEL        0x020
+#define LG_WIRELESS            0x040
+#define LG_INVERT_HWHEEL       0x080
+#define LG_NOGET               0x100
+#define LG_FF                  0x200
+#define LG_FF2                 0x400
+
+/*
+ * Certain Logitech keyboards send in report #3 keys which are far
+ * above the logical maximum described in descriptor. This extends
+ * the original value of 0x28c of logical maximum to 0x104d
+ */
+static void lg_report_fixup(struct hid_device *hdev, __u8 *rdesc,
+               unsigned int rsize)
+{
+       unsigned long quirks = (unsigned long)hid_get_drvdata(hdev);
+
+       if ((quirks & LG_RDESC) && rsize >= 90 && rdesc[83] == 0x26 &&
+                       rdesc[84] == 0x8c && rdesc[85] == 0x02) {
+               dev_info(&hdev->dev, "fixing up Logitech keyboard report "
+                               "descriptor\n");
+               rdesc[84] = rdesc[89] = 0x4d;
+               rdesc[85] = rdesc[90] = 0x10;
+       }
+}
+
+#define lg_map_key_clear(c)    hid_map_usage_clear(hi, usage, bit, max, \
+               EV_KEY, (c))
+
+static int lg_ultrax_remote_mapping(struct hid_input *hi,
+               struct hid_usage *usage, unsigned long **bit, int *max)
+{
+       if ((usage->hid & HID_USAGE_PAGE) != HID_UP_LOGIVENDOR)
+               return 0;
+
+       set_bit(EV_REP, hi->input->evbit);
+       switch (usage->hid & HID_USAGE) {
+       /* Reported on Logitech Ultra X Media Remote */
+       case 0x004: lg_map_key_clear(KEY_AGAIN);        break;
+       case 0x00d: lg_map_key_clear(KEY_HOME);         break;
+       case 0x024: lg_map_key_clear(KEY_SHUFFLE);      break;
+       case 0x025: lg_map_key_clear(KEY_TV);           break;
+       case 0x026: lg_map_key_clear(KEY_MENU);         break;
+       case 0x031: lg_map_key_clear(KEY_AUDIO);        break;
+       case 0x032: lg_map_key_clear(KEY_TEXT);         break;
+       case 0x033: lg_map_key_clear(KEY_LAST);         break;
+       case 0x047: lg_map_key_clear(KEY_MP3);          break;
+       case 0x048: lg_map_key_clear(KEY_DVD);          break;
+       case 0x049: lg_map_key_clear(KEY_MEDIA);        break;
+       case 0x04a: lg_map_key_clear(KEY_VIDEO);        break;
+       case 0x04b: lg_map_key_clear(KEY_ANGLE);        break;
+       case 0x04c: lg_map_key_clear(KEY_LANGUAGE);     break;
+       case 0x04d: lg_map_key_clear(KEY_SUBTITLE);     break;
+       case 0x051: lg_map_key_clear(KEY_RED);          break;
+       case 0x052: lg_map_key_clear(KEY_CLOSE);        break;
+
+       default:
+               return 0;
+       }
+       return 1;
+}
+
+static int lg_wireless_mapping(struct hid_input *hi, struct hid_usage *usage,
+               unsigned long **bit, int *max)
+{
+       if ((usage->hid & HID_USAGE_PAGE) != HID_UP_CONSUMER)
+               return 0;
+
+       switch (usage->hid & HID_USAGE) {
+       case 0x1001: lg_map_key_clear(KEY_MESSENGER);           break;
+       case 0x1003: lg_map_key_clear(KEY_SOUND);               break;
+       case 0x1004: lg_map_key_clear(KEY_VIDEO);               break;
+       case 0x1005: lg_map_key_clear(KEY_AUDIO);               break;
+       case 0x100a: lg_map_key_clear(KEY_DOCUMENTS);           break;
+       case 0x1011: lg_map_key_clear(KEY_PREVIOUSSONG);        break;
+       case 0x1012: lg_map_key_clear(KEY_NEXTSONG);            break;
+       case 0x1013: lg_map_key_clear(KEY_CAMERA);              break;
+       case 0x1014: lg_map_key_clear(KEY_MESSENGER);           break;
+       case 0x1015: lg_map_key_clear(KEY_RECORD);              break;
+       case 0x1016: lg_map_key_clear(KEY_PLAYER);              break;
+       case 0x1017: lg_map_key_clear(KEY_EJECTCD);             break;
+       case 0x1018: lg_map_key_clear(KEY_MEDIA);               break;
+       case 0x1019: lg_map_key_clear(KEY_PROG1);               break;
+       case 0x101a: lg_map_key_clear(KEY_PROG2);               break;
+       case 0x101b: lg_map_key_clear(KEY_PROG3);               break;
+       case 0x101f: lg_map_key_clear(KEY_ZOOMIN);              break;
+       case 0x1020: lg_map_key_clear(KEY_ZOOMOUT);             break;
+       case 0x1021: lg_map_key_clear(KEY_ZOOMRESET);           break;
+       case 0x1023: lg_map_key_clear(KEY_CLOSE);               break;
+       case 0x1027: lg_map_key_clear(KEY_MENU);                break;
+       /* this one is marked as 'Rotate' */
+       case 0x1028: lg_map_key_clear(KEY_ANGLE);               break;
+       case 0x1029: lg_map_key_clear(KEY_SHUFFLE);             break;
+       case 0x102a: lg_map_key_clear(KEY_BACK);                break;
+       case 0x102b: lg_map_key_clear(KEY_CYCLEWINDOWS);        break;
+       case 0x1041: lg_map_key_clear(KEY_BATTERY);             break;
+       case 0x1042: lg_map_key_clear(KEY_WORDPROCESSOR);       break;
+       case 0x1043: lg_map_key_clear(KEY_SPREADSHEET);         break;
+       case 0x1044: lg_map_key_clear(KEY_PRESENTATION);        break;
+       case 0x1045: lg_map_key_clear(KEY_UNDO);                break;
+       case 0x1046: lg_map_key_clear(KEY_REDO);                break;
+       case 0x1047: lg_map_key_clear(KEY_PRINT);               break;
+       case 0x1048: lg_map_key_clear(KEY_SAVE);                break;
+       case 0x1049: lg_map_key_clear(KEY_PROG1);               break;
+       case 0x104a: lg_map_key_clear(KEY_PROG2);               break;
+       case 0x104b: lg_map_key_clear(KEY_PROG3);               break;
+       case 0x104c: lg_map_key_clear(KEY_PROG4);               break;
+
+       default:
+               return 0;
+       }
+       return 1;
+}
+
+static int lg_input_mapping(struct hid_device *hdev, struct hid_input *hi,
+               struct hid_field *field, struct hid_usage *usage,
+               unsigned long **bit, int *max)
+{
+       /* extended mapping for certain Logitech hardware (Logitech cordless
+          desktop LX500) */
+       static const u8 e_keymap[] = {
+                 0,216,  0,213,175,156,  0,  0,  0,  0,
+               144,  0,  0,  0,  0,  0,  0,  0,  0,212,
+               174,167,152,161,112,  0,  0,  0,154,  0,
+                 0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+                 0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+                 0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+                 0,  0,  0,  0,  0,183,184,185,186,187,
+               188,189,190,191,192,193,194,  0,  0,  0
+       };
+       unsigned long quirks = (unsigned long)hid_get_drvdata(hdev);
+       unsigned int hid = usage->hid;
+
+       if (hdev->product == USB_DEVICE_ID_LOGITECH_RECEIVER &&
+                       lg_ultrax_remote_mapping(hi, usage, bit, max))
+               return 1;
+
+       if ((quirks & LG_WIRELESS) && lg_wireless_mapping(hi, usage, bit, max))
+               return 1;
+
+       if ((hid & HID_USAGE_PAGE) != HID_UP_BUTTON)
+               return 0;
+
+       hid &= HID_USAGE;
+
+       /* Special handling for Logitech Cordless Desktop */
+       if (field->application == HID_GD_MOUSE) {
+               if ((quirks & LG_IGNORE_DOUBLED_WHEEL) &&
+                               (hid == 7 || hid == 8))
+                       return -1;
+       } else {
+               if ((quirks & LG_EXPANDED_KEYMAP) &&
+                               hid < ARRAY_SIZE(e_keymap) &&
+                               e_keymap[hid] != 0) {
+                       hid_map_usage(hi, usage, bit, max, EV_KEY,
+                                       e_keymap[hid]);
+                       return 1;
+               }
+       }
+
+       return 0;
+}
+
+static int lg_input_mapped(struct hid_device *hdev, struct hid_input *hi,
+               struct hid_field *field, struct hid_usage *usage,
+               unsigned long **bit, int *max)
+{
+       unsigned long quirks = (unsigned long)hid_get_drvdata(hdev);
+
+       if ((quirks & LG_BAD_RELATIVE_KEYS) && usage->type == EV_KEY &&
+                       (field->flags & HID_MAIN_ITEM_RELATIVE))
+               field->flags &= ~HID_MAIN_ITEM_RELATIVE;
+
+       if ((quirks & LG_DUPLICATE_USAGES) && (usage->type == EV_KEY ||
+                        usage->type == EV_REL || usage->type == EV_ABS))
+               clear_bit(usage->code, *bit);
+
+       return 0;
+}
+
+static int lg_event(struct hid_device *hdev, struct hid_field *field,
+               struct hid_usage *usage, __s32 value)
+{
+       unsigned long quirks = (unsigned long)hid_get_drvdata(hdev);
+
+       if ((quirks & LG_INVERT_HWHEEL) && usage->code == REL_HWHEEL) {
+               input_event(field->hidinput->input, usage->type, usage->code,
+                               -value);
+               return 1;
+       }
+
+       return 0;
+}
+
+static int lg_probe(struct hid_device *hdev, const struct hid_device_id *id)
+{
+       unsigned long quirks = id->driver_data;
+       unsigned int connect_mask = HID_CONNECT_DEFAULT;
+       int ret;
+
+       hid_set_drvdata(hdev, (void *)quirks);
+
+       if (quirks & LG_NOGET)
+               hdev->quirks |= HID_QUIRK_NOGET;
+
+       ret = hid_parse(hdev);
+       if (ret) {
+               dev_err(&hdev->dev, "parse failed\n");
+               goto err_free;
+       }
+
+       if (quirks & (LG_FF | LG_FF2))
+               connect_mask &= ~HID_CONNECT_FF;
+
+       ret = hid_hw_start(hdev, connect_mask);
+       if (ret) {
+               dev_err(&hdev->dev, "hw start failed\n");
+               goto err_free;
+       }
+
+       if (quirks & LG_RESET_LEDS)
+               usbhid_set_leds(hdev);
+
+       if (quirks & LG_FF)
+               lgff_init(hdev);
+       if (quirks & LG_FF2)
+               lg2ff_init(hdev);
+
+       return 0;
+err_free:
+       return ret;
+}
+
+static const struct hid_device_id lg_devices[] = {
+       { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_MX3000_RECEIVER),
+               .driver_data = LG_RDESC | LG_WIRELESS },
+       { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_S510_RECEIVER),
+               .driver_data = LG_RDESC | LG_WIRELESS },
+       { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_S510_RECEIVER_2),
+               .driver_data = LG_RDESC | LG_WIRELESS },
+
+       { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RECEIVER),
+               .driver_data = LG_BAD_RELATIVE_KEYS },
+
+       { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_DINOVO_DESKTOP),
+               .driver_data = LG_DUPLICATE_USAGES },
+       { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_DINOVO_EDGE),
+               .driver_data = LG_DUPLICATE_USAGES },
+       { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_DINOVO_MINI),
+               .driver_data = LG_DUPLICATE_USAGES },
+
+       { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_KBD),
+               .driver_data = LG_RESET_LEDS },
+
+       { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_ELITE_KBD),
+               .driver_data = LG_IGNORE_DOUBLED_WHEEL | LG_EXPANDED_KEYMAP },
+       { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_CORDLESS_DESKTOP_LX500),
+               .driver_data = LG_IGNORE_DOUBLED_WHEEL | LG_EXPANDED_KEYMAP },
+
+       { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_LX3),
+               .driver_data = LG_INVERT_HWHEEL },
+       { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_V150),
+               .driver_data = LG_INVERT_HWHEEL },
+
+       { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_EXTREME_3D),
+               .driver_data = LG_NOGET },
+       { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WHEEL),
+               .driver_data = LG_NOGET | LG_FF },
+
+       { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD),
+               .driver_data = LG_FF },
+       { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD2_2),
+               .driver_data = LG_FF },
+       { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WINGMAN_F3D),
+               .driver_data = LG_FF },
+       { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_FORCE3D_PRO),
+               .driver_data = LG_FF },
+       { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_MOMO_WHEEL),
+               .driver_data = LG_FF },
+       { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_MOMO_WHEEL2),
+               .driver_data = LG_FF },
+       { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD2),
+               .driver_data = LG_FF2 },
+       { }
+};
+MODULE_DEVICE_TABLE(hid, lg_devices);
+
+static struct hid_driver lg_driver = {
+       .name = "logitech",
+       .id_table = lg_devices,
+       .report_fixup = lg_report_fixup,
+       .input_mapping = lg_input_mapping,
+       .input_mapped = lg_input_mapped,
+       .event = lg_event,
+       .probe = lg_probe,
+};
+
+static int lg_init(void)
+{
+       return hid_register_driver(&lg_driver);
+}
+
+static void lg_exit(void)
+{
+       hid_unregister_driver(&lg_driver);
+}
+
+module_init(lg_init);
+module_exit(lg_exit);
+MODULE_LICENSE("GPL");
+
+HID_COMPAT_LOAD_DRIVER(logitech);
diff --git a/drivers/hid/hid-lg.h b/drivers/hid/hid-lg.h
new file mode 100644 (file)
index 0000000..27ae750
--- /dev/null
@@ -0,0 +1,18 @@
+#ifndef __HID_LG_H
+#define __HID_LG_H
+
+#include <linux/autoconf.h>
+
+#ifdef CONFIG_LOGITECH_FF
+int lgff_init(struct hid_device *hdev);
+#else
+static inline int lgff_init(struct hid_device *hdev) { return -1; }
+#endif
+
+#ifdef CONFIG_LOGIRUMBLEPAD2_FF
+int lg2ff_init(struct hid_device *hdev);
+#else
+static inline int lg2ff_init(struct hid_device *hdev) { return -1; }
+#endif
+
+#endif
similarity index 90%
rename from drivers/hid/usbhid/hid-lg2ff.c
rename to drivers/hid/hid-lg2ff.c
index d469bd0061c99cc93d6553a0cc634fbe91df3f34..4e6dc6e26523a22a8d109d4d71fe68d89628641b 100644 (file)
@@ -24,7 +24,9 @@
 #include <linux/input.h>
 #include <linux/usb.h>
 #include <linux/hid.h>
-#include "usbhid.h"
+
+#include "usbhid/usbhid.h"
+#include "hid-lg.h"
 
 struct lg2ff_device {
        struct hid_report *report;
@@ -57,7 +59,7 @@ static int play_effect(struct input_dev *dev, void *data,
        return 0;
 }
 
-int hid_lg2ff_init(struct hid_device *hid)
+int lg2ff_init(struct hid_device *hid)
 {
        struct lg2ff_device *lg2ff;
        struct hid_report *report;
@@ -69,18 +71,18 @@ int hid_lg2ff_init(struct hid_device *hid)
        int error;
 
        if (list_empty(report_list)) {
-               printk(KERN_ERR "hid-lg2ff: no output report found\n");
+               dev_err(&hid->dev, "no output report found\n");
                return -ENODEV;
        }
 
        report = list_entry(report_list->next, struct hid_report, list);
 
        if (report->maxfield < 1) {
-               printk(KERN_ERR "hid-lg2ff: output report is empty\n");
+               dev_err(&hid->dev, "output report is empty\n");
                return -ENODEV;
        }
        if (report->field[0]->report_count < 7) {
-               printk(KERN_ERR "hid-lg2ff: not enough values in the field\n");
+               dev_err(&hid->dev, "not enough values in the field\n");
                return -ENODEV;
        }
 
@@ -107,7 +109,7 @@ int hid_lg2ff_init(struct hid_device *hid)
 
        usbhid_submit_report(hid, report, USB_DIR_OUT);
 
-       printk(KERN_INFO "Force feedback for Logitech Rumblepad 2 by "
+       dev_info(&hid->dev, "Force feedback for Logitech Rumblepad 2 by "
               "Anssi Hannula <anssi.hannula@gmail.com>\n");
 
        return 0;
similarity index 81%
rename from drivers/hid/usbhid/hid-lgff.c
rename to drivers/hid/hid-lgff.c
index 4b7ab6a46d93b9d2e539820382a33b8edd76ee71..51aff08e10ce66d78853a48b2536ab1179ee8d8f 100644 (file)
@@ -30,7 +30,9 @@
 #include <linux/input.h>
 #include <linux/usb.h>
 #include <linux/hid.h>
-#include "usbhid.h"
+
+#include "usbhid/usbhid.h"
+#include "hid-lg.h"
 
 struct dev_type {
        u16 idVendor;
@@ -48,6 +50,12 @@ static const signed short ff_joystick[] = {
        -1
 };
 
+static const signed short ff_wheel[] = {
+       FF_CONSTANT,
+       FF_AUTOCENTER,
+       -1
+};
+
 static const struct dev_type devices[] = {
        { 0x046d, 0xc211, ff_rumble },
        { 0x046d, 0xc219, ff_rumble },
@@ -55,7 +63,7 @@ static const struct dev_type devices[] = {
        { 0x046d, 0xc286, ff_joystick },
        { 0x046d, 0xc294, ff_joystick },
        { 0x046d, 0xc295, ff_joystick },
-       { 0x046d, 0xca03, ff_joystick },
+       { 0x046d, 0xca03, ff_wheel },
 };
 
 static int hid_lgff_play(struct input_dev *dev, void *data, struct ff_effect *effect)
@@ -100,7 +108,24 @@ static int hid_lgff_play(struct input_dev *dev, void *data, struct ff_effect *ef
        return 0;
 }
 
-int hid_lgff_init(struct hid_device* hid)
+static void hid_lgff_set_autocenter(struct input_dev *dev, u16 magnitude)
+{
+       struct hid_device *hid = input_get_drvdata(dev);
+       struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list;
+       struct hid_report *report = list_entry(report_list->next, struct hid_report, list);
+       __s32 *value = report->field[0]->value;
+       magnitude = (magnitude >> 12) & 0xf;
+       *value++ = 0xfe;
+       *value++ = 0x0d;
+       *value++ = magnitude;   /* clockwise strength */
+       *value++ = magnitude;   /* counter-clockwise strength */
+       *value++ = 0x80;
+       *value++ = 0x00;
+       *value = 0x00;
+       usbhid_submit_report(hid, report, USB_DIR_OUT);
+}
+
+int lgff_init(struct hid_device* hid)
 {
        struct hid_input *hidinput = list_entry(hid->inputs.next, struct hid_input, list);
        struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list;
@@ -145,6 +170,9 @@ int hid_lgff_init(struct hid_device* hid)
        if (error)
                return error;
 
+       if ( test_bit(FF_AUTOCENTER, dev->ffbit) )
+               dev->ff->set_autocenter = hid_lgff_set_autocenter;
+
        printk(KERN_INFO "Force feedback for Logitech force feedback devices by Johann Deneux <johann.deneux@it.uu.se>\n");
 
        return 0;
diff --git a/drivers/hid/hid-microsoft.c b/drivers/hid/hid-microsoft.c
new file mode 100644 (file)
index 0000000..d718b16
--- /dev/null
@@ -0,0 +1,219 @@
+/*
+ *  HID driver for some microsoft "special" devices
+ *
+ *  Copyright (c) 1999 Andreas Gal
+ *  Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
+ *  Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
+ *  Copyright (c) 2006-2007 Jiri Kosina
+ *  Copyright (c) 2007 Paul Walmsley
+ *  Copyright (c) 2008 Jiri Slaby
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ */
+
+#include <linux/device.h>
+#include <linux/input.h>
+#include <linux/hid.h>
+#include <linux/module.h>
+
+#include "hid-ids.h"
+
+#define MS_HIDINPUT    0x01
+#define MS_ERGONOMY    0x02
+#define MS_PRESENTER   0x04
+#define MS_RDESC       0x08
+#define MS_NOGET       0x10
+
+/*
+ * Microsoft Wireless Desktop Receiver (Model 1028) has several
+ * 'Usage Min/Max' where it ought to have 'Physical Min/Max'
+ */
+static void ms_report_fixup(struct hid_device *hdev, __u8 *rdesc,
+               unsigned int rsize)
+{
+       unsigned long quirks = (unsigned long)hid_get_drvdata(hdev);
+
+       if ((quirks & MS_RDESC) && rsize == 571 && rdesc[284] == 0x19 &&
+                       rdesc[286] == 0x2a && rdesc[304] == 0x19 &&
+                       rdesc[306] == 0x29 && rdesc[352] == 0x1a &&
+                       rdesc[355] == 0x2a && rdesc[557] == 0x19 &&
+                       rdesc[559] == 0x29) {
+               dev_info(&hdev->dev, "fixing up Microsoft Wireless Receiver "
+                               "Model 1028 report descriptor\n");
+               rdesc[284] = rdesc[304] = rdesc[557] = 0x35;
+               rdesc[352] = 0x36;
+               rdesc[286] = rdesc[355] = 0x46;
+               rdesc[306] = rdesc[559] = 0x45;
+       }
+}
+
+#define ms_map_key_clear(c)    hid_map_usage_clear(hi, usage, bit, max, \
+                                       EV_KEY, (c))
+static int ms_ergonomy_kb_quirk(struct hid_input *hi, struct hid_usage *usage,
+               unsigned long **bit, int *max)
+{
+       struct input_dev *input = hi->input;
+
+       switch (usage->hid & HID_USAGE) {
+       case 0xfd06: ms_map_key_clear(KEY_CHAT);        break;
+       case 0xfd07: ms_map_key_clear(KEY_PHONE);       break;
+       case 0xff05:
+               set_bit(EV_REP, input->evbit);
+               ms_map_key_clear(KEY_F13);
+               set_bit(KEY_F14, input->keybit);
+               set_bit(KEY_F15, input->keybit);
+               set_bit(KEY_F16, input->keybit);
+               set_bit(KEY_F17, input->keybit);
+               set_bit(KEY_F18, input->keybit);
+       default:
+               return 0;
+       }
+       return 1;
+}
+
+static int ms_presenter_8k_quirk(struct hid_input *hi, struct hid_usage *usage,
+               unsigned long **bit, int *max)
+{
+       set_bit(EV_REP, hi->input->evbit);
+       switch (usage->hid & HID_USAGE) {
+       case 0xfd08: ms_map_key_clear(KEY_FORWARD);     break;
+       case 0xfd09: ms_map_key_clear(KEY_BACK);        break;
+       case 0xfd0b: ms_map_key_clear(KEY_PLAYPAUSE);   break;
+       case 0xfd0e: ms_map_key_clear(KEY_CLOSE);       break;
+       case 0xfd0f: ms_map_key_clear(KEY_PLAY);        break;
+       default:
+               return 0;
+       }
+       return 1;
+}
+
+static int ms_input_mapping(struct hid_device *hdev, struct hid_input *hi,
+               struct hid_field *field, struct hid_usage *usage,
+               unsigned long **bit, int *max)
+{
+       unsigned long quirks = (unsigned long)hid_get_drvdata(hdev);
+
+       if ((usage->hid & HID_USAGE_PAGE) != HID_UP_MSVENDOR)
+               return 0;
+
+       if (quirks & MS_ERGONOMY) {
+               int ret = ms_ergonomy_kb_quirk(hi, usage, bit, max);
+               if (ret)
+                       return ret;
+       }
+
+       if ((quirks & MS_PRESENTER) &&
+                       ms_presenter_8k_quirk(hi, usage, bit, max))
+               return 1;
+
+       return 0;
+}
+
+static int ms_event(struct hid_device *hdev, struct hid_field *field,
+               struct hid_usage *usage, __s32 value)
+{
+       unsigned long quirks = (unsigned long)hid_get_drvdata(hdev);
+
+       if (!(hdev->claimed & HID_CLAIMED_INPUT) || !field->hidinput ||
+                       !usage->type)
+               return 0;
+
+       /* Handling MS keyboards special buttons */
+       if (quirks & MS_ERGONOMY && usage->hid == (HID_UP_MSVENDOR | 0xff05)) {
+               struct input_dev *input = field->hidinput->input;
+               static unsigned int last_key = 0;
+               unsigned int key = 0;
+               switch (value) {
+               case 0x01: key = KEY_F14; break;
+               case 0x02: key = KEY_F15; break;
+               case 0x04: key = KEY_F16; break;
+               case 0x08: key = KEY_F17; break;
+               case 0x10: key = KEY_F18; break;
+               }
+               if (key) {
+                       input_event(input, usage->type, key, 1);
+                       last_key = key;
+               } else
+                       input_event(input, usage->type, last_key, 0);
+
+               return 1;
+       }
+
+       return 0;
+}
+
+static int ms_probe(struct hid_device *hdev, const struct hid_device_id *id)
+{
+       unsigned long quirks = id->driver_data;
+       int ret;
+
+       hid_set_drvdata(hdev, (void *)quirks);
+
+       if (quirks & MS_NOGET)
+               hdev->quirks |= HID_QUIRK_NOGET;
+
+       ret = hid_parse(hdev);
+       if (ret) {
+               dev_err(&hdev->dev, "parse failed\n");
+               goto err_free;
+       }
+
+       ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT | ((quirks & MS_HIDINPUT) ?
+                               HID_CONNECT_HIDINPUT_FORCE : 0));
+       if (ret) {
+               dev_err(&hdev->dev, "hw start failed\n");
+               goto err_free;
+       }
+
+       return 0;
+err_free:
+       return ret;
+}
+
+static const struct hid_device_id ms_devices[] = {
+       { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_SIDEWINDER_GV),
+               .driver_data = MS_HIDINPUT },
+       { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_NE4K),
+               .driver_data = MS_ERGONOMY },
+       { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_LK6K),
+               .driver_data = MS_ERGONOMY | MS_RDESC },
+       { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_PRESENTER_8K_USB),
+               .driver_data = MS_PRESENTER },
+       { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_WIRELESS_OPTICAL_DESKTOP_3_0),
+               .driver_data = MS_NOGET },
+
+       { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_PRESENTER_8K_BT),
+               .driver_data = MS_PRESENTER },
+       { }
+};
+MODULE_DEVICE_TABLE(hid, ms_devices);
+
+static struct hid_driver ms_driver = {
+       .name = "microsoft",
+       .id_table = ms_devices,
+       .report_fixup = ms_report_fixup,
+       .input_mapping = ms_input_mapping,
+       .event = ms_event,
+       .probe = ms_probe,
+};
+
+static int ms_init(void)
+{
+       return hid_register_driver(&ms_driver);
+}
+
+static void ms_exit(void)
+{
+       hid_unregister_driver(&ms_driver);
+}
+
+module_init(ms_init);
+module_exit(ms_exit);
+MODULE_LICENSE("GPL");
+
+HID_COMPAT_LOAD_DRIVER(microsoft);
diff --git a/drivers/hid/hid-monterey.c b/drivers/hid/hid-monterey.c
new file mode 100644 (file)
index 0000000..f3a85a0
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+ *  HID driver for some monterey "special" devices
+ *
+ *  Copyright (c) 1999 Andreas Gal
+ *  Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
+ *  Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
+ *  Copyright (c) 2006-2007 Jiri Kosina
+ *  Copyright (c) 2007 Paul Walmsley
+ *  Copyright (c) 2008 Jiri Slaby
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ */
+
+#include <linux/device.h>
+#include <linux/hid.h>
+#include <linux/module.h>
+
+#include "hid-ids.h"
+
+static void mr_report_fixup(struct hid_device *hdev, __u8 *rdesc,
+               unsigned int rsize)
+{
+       if (rsize >= 30 && rdesc[29] == 0x05 && rdesc[30] == 0x09) {
+               dev_info(&hdev->dev, "fixing up button/consumer in HID report "
+                               "descriptor\n");
+               rdesc[30] = 0x0c;
+       }
+}
+
+#define mr_map_key_clear(c)    hid_map_usage_clear(hi, usage, bit, max, \
+                                       EV_KEY, (c))
+static int mr_input_mapping(struct hid_device *hdev, struct hid_input *hi,
+               struct hid_field *field, struct hid_usage *usage,
+               unsigned long **bit, int *max)
+{
+       if ((usage->hid & HID_USAGE_PAGE) != HID_UP_CONSUMER)
+               return 0;
+
+       switch (usage->hid & HID_USAGE) {
+       case 0x156: mr_map_key_clear(KEY_WORDPROCESSOR);        break;
+       case 0x157: mr_map_key_clear(KEY_SPREADSHEET);          break;
+       case 0x158: mr_map_key_clear(KEY_PRESENTATION);         break;
+       case 0x15c: mr_map_key_clear(KEY_STOP);                 break;
+       default:
+               return 0;
+       }
+       return 1;
+}
+
+static const struct hid_device_id mr_devices[] = {
+       { HID_USB_DEVICE(USB_VENDOR_ID_MONTEREY, USB_DEVICE_ID_GENIUS_KB29E) },
+       { }
+};
+MODULE_DEVICE_TABLE(hid, mr_devices);
+
+static struct hid_driver mr_driver = {
+       .name = "monterey",
+       .id_table = mr_devices,
+       .report_fixup = mr_report_fixup,
+       .input_mapping = mr_input_mapping,
+};
+
+static int mr_init(void)
+{
+       return hid_register_driver(&mr_driver);
+}
+
+static void mr_exit(void)
+{
+       hid_unregister_driver(&mr_driver);
+}
+
+module_init(mr_init);
+module_exit(mr_exit);
+MODULE_LICENSE("GPL");
+
+HID_COMPAT_LOAD_DRIVER(monterey);
diff --git a/drivers/hid/hid-petalynx.c b/drivers/hid/hid-petalynx.c
new file mode 100644 (file)
index 0000000..10945fe
--- /dev/null
@@ -0,0 +1,122 @@
+/*
+ *  HID driver for some petalynx "special" devices
+ *
+ *  Copyright (c) 1999 Andreas Gal
+ *  Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
+ *  Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
+ *  Copyright (c) 2006-2007 Jiri Kosina
+ *  Copyright (c) 2007 Paul Walmsley
+ *  Copyright (c) 2008 Jiri Slaby
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ */
+
+#include <linux/device.h>
+#include <linux/hid.h>
+#include <linux/module.h>
+
+#include "hid-ids.h"
+
+/* Petalynx Maxter Remote has maximum for consumer page set too low */
+static void pl_report_fixup(struct hid_device *hdev, __u8 *rdesc,
+               unsigned int rsize)
+{
+       if (rsize >= 60 && rdesc[39] == 0x2a && rdesc[40] == 0xf5 &&
+                       rdesc[41] == 0x00 && rdesc[59] == 0x26 &&
+                       rdesc[60] == 0xf9 && rdesc[61] == 0x00) {
+               dev_info(&hdev->dev, "fixing up Petalynx Maxter Remote report "
+                               "descriptor\n");
+               rdesc[60] = 0xfa;
+               rdesc[40] = 0xfa;
+       }
+}
+
+#define pl_map_key_clear(c)    hid_map_usage_clear(hi, usage, bit, max, \
+                                       EV_KEY, (c))
+static int pl_input_mapping(struct hid_device *hdev, struct hid_input *hi,
+               struct hid_field *field, struct hid_usage *usage,
+               unsigned long **bit, int *max)
+{
+       if ((usage->hid & HID_USAGE_PAGE) == HID_UP_LOGIVENDOR) {
+               switch (usage->hid & HID_USAGE) {
+               case 0x05a: pl_map_key_clear(KEY_TEXT);         break;
+               case 0x05b: pl_map_key_clear(KEY_RED);          break;
+               case 0x05c: pl_map_key_clear(KEY_GREEN);        break;
+               case 0x05d: pl_map_key_clear(KEY_YELLOW);       break;
+               case 0x05e: pl_map_key_clear(KEY_BLUE);         break;
+               default:
+                       return 0;
+               }
+               return 1;
+       }
+
+       if ((usage->hid & HID_USAGE_PAGE) == HID_UP_CONSUMER) {
+               switch (usage->hid & HID_USAGE) {
+               case 0x0f6: pl_map_key_clear(KEY_NEXT);         break;
+               case 0x0fa: pl_map_key_clear(KEY_BACK);         break;
+               default:
+                       return 0;
+               }
+               return 1;
+       }
+
+       return 0;
+}
+
+static int pl_probe(struct hid_device *hdev, const struct hid_device_id *id)
+{
+       int ret;
+
+       hdev->quirks |= HID_QUIRK_NOGET;
+
+       ret = hid_parse(hdev);
+       if (ret) {
+               dev_err(&hdev->dev, "parse failed\n");
+               goto err_free;
+       }
+
+       ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
+       if (ret) {
+               dev_err(&hdev->dev, "hw start failed\n");
+               goto err_free;
+       }
+
+       return 0;
+err_free:
+       return ret;
+}
+
+static const struct hid_device_id pl_devices[] = {
+       { HID_USB_DEVICE(USB_VENDOR_ID_PETALYNX, USB_DEVICE_ID_PETALYNX_MAXTER_REMOTE) },
+       { }
+};
+MODULE_DEVICE_TABLE(hid, pl_devices);
+
+static struct hid_driver pl_driver = {
+       .name = "petalynx",
+       .id_table = pl_devices,
+       .report_fixup = pl_report_fixup,
+       .input_mapping = pl_input_mapping,
+       .probe = pl_probe,
+};
+
+static int pl_init(void)
+{
+       return hid_register_driver(&pl_driver);
+}
+
+static void pl_exit(void)
+{
+       hid_unregister_driver(&pl_driver);
+}
+
+module_init(pl_init);
+module_exit(pl_exit);
+MODULE_LICENSE("GPL");
+
+HID_COMPAT_LOAD_DRIVER(petalynx);
similarity index 68%
rename from drivers/hid/usbhid/hid-plff.c
rename to drivers/hid/hid-pl.c
index 9eb83cf9d22b98021d540b61f08be3a690f45466..acd81558618285ffd25f0ec1c5b01c9ec1de5d01 100644 (file)
@@ -9,7 +9,7 @@
  *   - contains two reports, one for each port (HID_QUIRK_MULTI_INPUT)
  *
  *  0e8f:0003 "GreenAsia Inc.    USB Joystick     "
- *   - tested with Köng Gaming gamepad
+ *   - tested with K??ng Gaming gamepad
  *
  *  Copyright (c) 2007 Anssi Hannula <anssi.hannula@gmail.com>
  */
 #include <linux/input.h>
 #include <linux/usb.h>
 #include <linux/hid.h>
-#include "usbhid.h"
+
+#include "hid-ids.h"
+
+#ifdef CONFIG_PANTHERLORD_FF
+#include "usbhid/usbhid.h"
 
 struct plff_device {
        struct hid_report *report;
@@ -66,7 +70,7 @@ static int hid_plff_play(struct input_dev *dev, void *data,
        return 0;
 }
 
-int hid_plff_init(struct hid_device *hid)
+static int plff_init(struct hid_device *hid)
 {
        struct plff_device *plff;
        struct hid_report *report;
@@ -86,7 +90,7 @@ int hid_plff_init(struct hid_device *hid)
           currently unknown. */
 
        if (list_empty(report_list)) {
-               printk(KERN_ERR "hid-plff: no output reports found\n");
+               dev_err(&hid->dev, "no output reports found\n");
                return -ENODEV;
        }
 
@@ -95,18 +99,19 @@ int hid_plff_init(struct hid_device *hid)
                report_ptr = report_ptr->next;
 
                if (report_ptr == report_list) {
-                       printk(KERN_ERR "hid-plff: required output report is missing\n");
+                       dev_err(&hid->dev, "required output report is "
+                                       "missing\n");
                        return -ENODEV;
                }
 
                report = list_entry(report_ptr, struct hid_report, list);
                if (report->maxfield < 1) {
-                       printk(KERN_ERR "hid-plff: no fields in the report\n");
+                       dev_err(&hid->dev, "no fields in the report\n");
                        return -ENODEV;
                }
 
                if (report->field[0]->report_count < 4) {
-                       printk(KERN_ERR "hid-plff: not enough values in the field\n");
+                       dev_err(&hid->dev, "not enough values in the field\n");
                        return -ENODEV;
                }
 
@@ -132,8 +137,70 @@ int hid_plff_init(struct hid_device *hid)
                usbhid_submit_report(hid, plff->report, USB_DIR_OUT);
        }
 
-       printk(KERN_INFO "hid-plff: Force feedback for PantherLord/GreenAsia "
+       dev_info(&hid->dev, "Force feedback for PantherLord/GreenAsia "
               "devices by Anssi Hannula <anssi.hannula@gmail.com>\n");
 
        return 0;
 }
+#else
+static inline int plff_init(struct hid_device *hid)
+{
+       return 0;
+}
+#endif
+
+static int pl_probe(struct hid_device *hdev, const struct hid_device_id *id)
+{
+       int ret;
+
+       if (id->driver_data)
+               hdev->quirks |= HID_QUIRK_MULTI_INPUT;
+
+       ret = hid_parse(hdev);
+       if (ret) {
+               dev_err(&hdev->dev, "parse failed\n");
+               goto err;
+       }
+
+       ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT & ~HID_CONNECT_FF);
+       if (ret) {
+               dev_err(&hdev->dev, "hw start failed\n");
+               goto err;
+       }
+
+       plff_init(hdev);
+
+       return 0;
+err:
+       return ret;
+}
+
+static const struct hid_device_id pl_devices[] = {
+       { HID_USB_DEVICE(USB_VENDOR_ID_GAMERON, USB_DEVICE_ID_GAMERON_DUAL_PSX_ADAPTOR),
+               .driver_data = 1 }, /* Twin USB Joystick */
+       { HID_USB_DEVICE(USB_VENDOR_ID_GREENASIA, 0x0003), }, /* GreenAsia Inc. USB Joystick */
+       { }
+};
+MODULE_DEVICE_TABLE(hid, pl_devices);
+
+static struct hid_driver pl_driver = {
+       .name = "pantherlord",
+       .id_table = pl_devices,
+       .probe = pl_probe,
+};
+
+static int pl_init(void)
+{
+       return hid_register_driver(&pl_driver);
+}
+
+static void pl_exit(void)
+{
+       hid_unregister_driver(&pl_driver);
+}
+
+module_init(pl_init);
+module_exit(pl_exit);
+MODULE_LICENSE("GPL");
+
+HID_COMPAT_LOAD_DRIVER(pantherlord);
diff --git a/drivers/hid/hid-samsung.c b/drivers/hid/hid-samsung.c
new file mode 100644 (file)
index 0000000..15f3c04
--- /dev/null
@@ -0,0 +1,100 @@
+/*
+ *  HID driver for some samsung "special" devices
+ *
+ *  Copyright (c) 1999 Andreas Gal
+ *  Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
+ *  Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
+ *  Copyright (c) 2006-2007 Jiri Kosina
+ *  Copyright (c) 2007 Paul Walmsley
+ *  Copyright (c) 2008 Jiri Slaby
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ */
+
+#include <linux/device.h>
+#include <linux/hid.h>
+#include <linux/module.h>
+
+#include "hid-ids.h"
+
+/*
+ * Samsung IrDA remote controller (reports as Cypress USB Mouse).
+ *
+ * Vendor specific report #4 has a size of 48 bit,
+ * and therefore is not accepted when inspecting the descriptors.
+ * As a workaround we reinterpret the report as:
+ *   Variable type, count 6, size 8 bit, log. maximum 255
+ * The burden to reconstruct the data is moved into user space.
+ */
+static void samsung_report_fixup(struct hid_device *hdev, __u8 *rdesc,
+               unsigned int rsize)
+{
+       if (rsize >= 182 && rdesc[175] == 0x25 && rdesc[176] == 0x40 &&
+                       rdesc[177] == 0x75 && rdesc[178] == 0x30 &&
+                       rdesc[179] == 0x95 && rdesc[180] == 0x01 &&
+                       rdesc[182] == 0x40) {
+               dev_info(&hdev->dev, "fixing up Samsung IrDA report "
+                               "descriptor\n");
+               rdesc[176] = 0xff;
+               rdesc[178] = 0x08;
+               rdesc[180] = 0x06;
+               rdesc[182] = 0x42;
+       }
+}
+
+static int samsung_probe(struct hid_device *hdev,
+               const struct hid_device_id *id)
+{
+       int ret;
+
+       ret = hid_parse(hdev);
+       if (ret) {
+               dev_err(&hdev->dev, "parse failed\n");
+               goto err_free;
+       }
+
+       ret = hid_hw_start(hdev, (HID_CONNECT_DEFAULT & ~HID_CONNECT_HIDINPUT) |
+                       HID_CONNECT_HIDDEV_FORCE);
+       if (ret) {
+               dev_err(&hdev->dev, "hw start failed\n");
+               goto err_free;
+       }
+
+       return 0;
+err_free:
+       return ret;
+}
+
+static const struct hid_device_id samsung_devices[] = {
+       { HID_USB_DEVICE(USB_VENDOR_ID_SAMSUNG, USB_DEVICE_ID_SAMSUNG_IR_REMOTE) },
+       { }
+};
+MODULE_DEVICE_TABLE(hid, samsung_devices);
+
+static struct hid_driver samsung_driver = {
+       .name = "samsung",
+       .id_table = samsung_devices,
+       .report_fixup = samsung_report_fixup,
+       .probe = samsung_probe,
+};
+
+static int samsung_init(void)
+{
+       return hid_register_driver(&samsung_driver);
+}
+
+static void samsung_exit(void)
+{
+       hid_unregister_driver(&samsung_driver);
+}
+
+module_init(samsung_init);
+module_exit(samsung_exit);
+MODULE_LICENSE("GPL");
+
+HID_COMPAT_LOAD_DRIVER(samsung);
diff --git a/drivers/hid/hid-sony.c b/drivers/hid/hid-sony.c
new file mode 100644 (file)
index 0000000..3af8095
--- /dev/null
@@ -0,0 +1,110 @@
+/*
+ *  HID driver for some sony "special" devices
+ *
+ *  Copyright (c) 1999 Andreas Gal
+ *  Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
+ *  Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
+ *  Copyright (c) 2006-2007 Jiri Kosina
+ *  Copyright (c) 2007 Paul Walmsley
+ *  Copyright (c) 2008 Jiri Slaby
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ */
+
+#include <linux/device.h>
+#include <linux/hid.h>
+#include <linux/module.h>
+#include <linux/usb.h>
+
+#include "hid-ids.h"
+
+/*
+ * Sending HID_REQ_GET_REPORT changes the operation mode of the ps3 controller
+ * to "operational".  Without this, the ps3 controller will not report any
+ * events.
+ */
+static int sony_set_operational(struct hid_device *hdev)
+{
+       struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
+       struct usb_device *dev = interface_to_usbdev(intf);
+       __u16 ifnum = intf->cur_altsetting->desc.bInterfaceNumber;
+       int ret;
+       char *buf = kmalloc(18, GFP_KERNEL);
+
+       if (!buf)
+               return -ENOMEM;
+
+       ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
+                                HID_REQ_GET_REPORT,
+                                USB_DIR_IN | USB_TYPE_CLASS |
+                                USB_RECIP_INTERFACE,
+                                (3 << 8) | 0xf2, ifnum, buf, 17,
+                                USB_CTRL_GET_TIMEOUT);
+       if (ret < 0)
+               dev_err(&hdev->dev, "can't set operational mode\n");
+
+       kfree(buf);
+
+       return ret;
+}
+
+static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id)
+{
+       int ret;
+
+       ret = hid_parse(hdev);
+       if (ret) {
+               dev_err(&hdev->dev, "parse failed\n");
+               goto err_free;
+       }
+
+       ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT |
+                       HID_CONNECT_HIDDEV_FORCE);
+       if (ret) {
+               dev_err(&hdev->dev, "hw start failed\n");
+               goto err_free;
+       }
+
+       ret = sony_set_operational(hdev);
+       if (ret)
+               goto err_stop;
+
+       return 0;
+err_stop:
+       hid_hw_stop(hdev);
+err_free:
+       return ret;
+}
+
+static const struct hid_device_id sony_devices[] = {
+       { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS3_CONTROLLER) },
+       { }
+};
+MODULE_DEVICE_TABLE(hid, sony_devices);
+
+static struct hid_driver sony_driver = {
+       .name = "sony",
+       .id_table = sony_devices,
+       .probe = sony_probe,
+};
+
+static int sony_init(void)
+{
+       return hid_register_driver(&sony_driver);
+}
+
+static void sony_exit(void)
+{
+       hid_unregister_driver(&sony_driver);
+}
+
+module_init(sony_init);
+module_exit(sony_exit);
+MODULE_LICENSE("GPL");
+
+HID_COMPAT_LOAD_DRIVER(sony);
diff --git a/drivers/hid/hid-sunplus.c b/drivers/hid/hid-sunplus.c
new file mode 100644 (file)
index 0000000..5ba68f7
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+ *  HID driver for some sunplus "special" devices
+ *
+ *  Copyright (c) 1999 Andreas Gal
+ *  Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
+ *  Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
+ *  Copyright (c) 2006-2007 Jiri Kosina
+ *  Copyright (c) 2007 Paul Walmsley
+ *  Copyright (c) 2008 Jiri Slaby
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ */
+
+#include <linux/device.h>
+#include <linux/hid.h>
+#include <linux/module.h>
+
+#include "hid-ids.h"
+
+static void sp_report_fixup(struct hid_device *hdev, __u8 *rdesc,
+               unsigned int rsize)
+{
+       if (rsize >= 107 && rdesc[104] == 0x26 && rdesc[105] == 0x80 &&
+                       rdesc[106] == 0x03) {
+               dev_info(&hdev->dev, "fixing up Sunplus Wireless Desktop "
+                               "report descriptor\n");
+               rdesc[105] = rdesc[110] = 0x03;
+               rdesc[106] = rdesc[111] = 0x21;
+       }
+}
+
+#define sp_map_key_clear(c)    hid_map_usage_clear(hi, usage, bit, max, \
+               EV_KEY, (c))
+static int sp_input_mapping(struct hid_device *hdev, struct hid_input *hi,
+               struct hid_field *field, struct hid_usage *usage,
+               unsigned long **bit, int *max)
+{
+       if ((usage->hid & HID_USAGE_PAGE) != HID_UP_CONSUMER)
+               return 0;
+
+       switch (usage->hid & HID_USAGE) {
+       case 0x2003: sp_map_key_clear(KEY_ZOOMIN);              break;
+       case 0x2103: sp_map_key_clear(KEY_ZOOMOUT);     break;
+       default:
+               return 0;
+       }
+       return 1;
+}
+
+static const struct hid_device_id sp_devices[] = {
+       { HID_USB_DEVICE(USB_VENDOR_ID_SUNPLUS, USB_DEVICE_ID_SUNPLUS_WDESKTOP) },
+       { }
+};
+MODULE_DEVICE_TABLE(hid, sp_devices);
+
+static struct hid_driver sp_driver = {
+       .name = "sunplus",
+       .id_table = sp_devices,
+       .report_fixup = sp_report_fixup,
+       .input_mapping = sp_input_mapping,
+};
+
+static int sp_init(void)
+{
+       return hid_register_driver(&sp_driver);
+}
+
+static void sp_exit(void)
+{
+       hid_unregister_driver(&sp_driver);
+}
+
+module_init(sp_init);
+module_exit(sp_exit);
+MODULE_LICENSE("GPL");
+
+HID_COMPAT_LOAD_DRIVER(sunplus);
similarity index 60%
rename from drivers/hid/usbhid/hid-tmff.c
rename to drivers/hid/hid-tmff.c
index 144578b1a00c832a32ae7a65659ae5316b6d087e..1b7cba0f7e1f71b289f984ba7b26f95dfdab173c 100644 (file)
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  */
 
+#include <linux/hid.h>
 #include <linux/input.h>
-
-#undef DEBUG
 #include <linux/usb.h>
 
-#include <linux/hid.h>
-#include "usbhid.h"
+#include "hid-ids.h"
+
+#include "usbhid/usbhid.h"
 
 /* Usages for thrustmaster devices I know about */
 #define THRUSTMASTER_USAGE_FF  (HID_UP_GENDESK | 0xbb)
 
-struct dev_type {
-       u16 idVendor;
-       u16 idProduct;
-       const signed short *ff;
-};
-
 static const signed short ff_rumble[] = {
        FF_RUMBLE,
        -1
@@ -54,21 +48,13 @@ static const signed short ff_joystick[] = {
        -1
 };
 
-static const struct dev_type devices[] = {
-       { 0x44f, 0xb300, ff_rumble },
-       { 0x44f, 0xb304, ff_rumble },
-       { 0x44f, 0xb651, ff_rumble },   /* FGT Rumble Force Wheel */
-       { 0x44f, 0xb654, ff_joystick }, /* FGT Force Feedback Wheel */
-};
-
 struct tmff_device {
        struct hid_report *report;
        struct hid_field *ff_field;
 };
 
 /* Changes values from 0 to 0xffff into values from minimum to maximum */
-static inline int hid_tmff_scale_u16(unsigned int in,
-                               int minimum, int maximum)
+static inline int tmff_scale_u16(unsigned int in, int minimum, int maximum)
 {
        int ret;
 
@@ -81,8 +67,7 @@ static inline int hid_tmff_scale_u16(unsigned int in,
 }
 
 /* Changes values from -0x80 to 0x7f into values from minimum to maximum */
-static inline int hid_tmff_scale_s8(int in,
-                                   int minimum, int maximum)
+static inline int tmff_scale_s8(int in, int minimum, int maximum)
 {
        int ret;
 
@@ -94,7 +79,8 @@ static inline int hid_tmff_scale_s8(int in,
        return ret;
 }
 
-static int hid_tmff_play(struct input_dev *dev, void *data, struct ff_effect *effect)
+static int tmff_play(struct input_dev *dev, void *data,
+               struct ff_effect *effect)
 {
        struct hid_device *hid = input_get_drvdata(dev);
        struct tmff_device *tmff = data;
@@ -104,10 +90,10 @@ static int hid_tmff_play(struct input_dev *dev, void *data, struct ff_effect *ef
 
        switch (effect->type) {
        case FF_CONSTANT:
-               x = hid_tmff_scale_s8(effect->u.ramp.start_level,
+               x = tmff_scale_s8(effect->u.ramp.start_level,
                                        ff_field->logical_minimum,
                                        ff_field->logical_maximum);
-               y = hid_tmff_scale_s8(effect->u.ramp.end_level,
+               y = tmff_scale_s8(effect->u.ramp.end_level,
                                        ff_field->logical_minimum,
                                        ff_field->logical_maximum);
 
@@ -118,10 +104,10 @@ static int hid_tmff_play(struct input_dev *dev, void *data, struct ff_effect *ef
                break;
 
        case FF_RUMBLE:
-               left = hid_tmff_scale_u16(effect->u.rumble.weak_magnitude,
+               left = tmff_scale_u16(effect->u.rumble.weak_magnitude,
                                        ff_field->logical_minimum,
                                        ff_field->logical_maximum);
-               right = hid_tmff_scale_u16(effect->u.rumble.strong_magnitude,
+               right = tmff_scale_u16(effect->u.rumble.strong_magnitude,
                                        ff_field->logical_minimum,
                                        ff_field->logical_maximum);
 
@@ -134,14 +120,14 @@ static int hid_tmff_play(struct input_dev *dev, void *data, struct ff_effect *ef
        return 0;
 }
 
-int hid_tmff_init(struct hid_device *hid)
+static int tmff_init(struct hid_device *hid, const signed short *ff_bits)
 {
        struct tmff_device *tmff;
        struct hid_report *report;
        struct list_head *report_list;
-       struct hid_input *hidinput = list_entry(hid->inputs.next, struct hid_input, list);
+       struct hid_input *hidinput = list_entry(hid->inputs.next,
+                                                       struct hid_input, list);
        struct input_dev *input_dev = hidinput->input;
-       const signed short *ff_bits = ff_joystick;
        int error;
        int i;
 
@@ -163,63 +149,121 @@ int hid_tmff_init(struct hid_device *hid)
                        switch (field->usage[0].hid) {
                        case THRUSTMASTER_USAGE_FF:
                                if (field->report_count < 2) {
-                                       warn("ignoring FF field with report_count < 2");
+                                       dev_warn(&hid->dev, "ignoring FF field "
+                                               "with report_count < 2\n");
                                        continue;
                                }
 
-                               if (field->logical_maximum == field->logical_minimum) {
-                                       warn("ignoring FF field with logical_maximum == logical_minimum");
+                               if (field->logical_maximum ==
+                                               field->logical_minimum) {
+                                       dev_warn(&hid->dev, "ignoring FF field "
+                                                       "with logical_maximum "
+                                                       "== logical_minimum\n");
                                        continue;
                                }
 
                                if (tmff->report && tmff->report != report) {
-                                       warn("ignoring FF field in other report");
+                                       dev_warn(&hid->dev, "ignoring FF field "
+                                                       "in other report\n");
                                        continue;
                                }
 
                                if (tmff->ff_field && tmff->ff_field != field) {
-                                       warn("ignoring duplicate FF field");
+                                       dev_warn(&hid->dev, "ignoring "
+                                                       "duplicate FF field\n");
                                        continue;
                                }
 
                                tmff->report = report;
                                tmff->ff_field = field;
 
-                               for (i = 0; i < ARRAY_SIZE(devices); i++) {
-                                       if (input_dev->id.vendor == devices[i].idVendor &&
-                                           input_dev->id.product == devices[i].idProduct) {
-                                               ff_bits = devices[i].ff;
-                                               break;
-                                       }
-                               }
-
                                for (i = 0; ff_bits[i] >= 0; i++)
                                        set_bit(ff_bits[i], input_dev->ffbit);
 
                                break;
 
                        default:
-                               warn("ignoring unknown output usage %08x", field->usage[0].hid);
+                               dev_warn(&hid->dev, "ignoring unknown output "
+                                               "usage %08x\n",
+                                               field->usage[0].hid);
                                continue;
                        }
                }
        }
 
        if (!tmff->report) {
-               err("cant find FF field in output reports\n");
+               dev_err(&hid->dev, "can't find FF field in output reports\n");
                error = -ENODEV;
                goto fail;
        }
 
-       error = input_ff_create_memless(input_dev, tmff, hid_tmff_play);
+       error = input_ff_create_memless(input_dev, tmff, tmff_play);
        if (error)
                goto fail;
 
-       info("Force feedback for ThrustMaster devices by Zinx Verituse <zinx@epicsol.org>");
+       dev_info(&hid->dev, "force feedback for ThrustMaster devices by Zinx "
+                       "Verituse <zinx@epicsol.org>");
        return 0;
 
- fail:
+fail:
        kfree(tmff);
        return error;
 }
 
+static int tm_probe(struct hid_device *hdev, const struct hid_device_id *id)
+{
+       int ret;
+
+       ret = hid_parse(hdev);
+       if (ret) {
+               dev_err(&hdev->dev, "parse failed\n");
+               goto err;
+       }
+
+       ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT & ~HID_CONNECT_FF);
+       if (ret) {
+               dev_err(&hdev->dev, "hw start failed\n");
+               goto err;
+       }
+
+       tmff_init(hdev, (void *)id->driver_data);
+
+       return 0;
+err:
+       return ret;
+}
+
+static const struct hid_device_id tm_devices[] = {
+       { HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb300),
+               .driver_data = (unsigned long)ff_rumble },
+       { HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb304),
+               .driver_data = (unsigned long)ff_rumble },
+       { HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb651),   /* FGT Rumble Force Wheel */
+               .driver_data = (unsigned long)ff_rumble },
+       { HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb654),   /* FGT Force Feedback Wheel */
+               .driver_data = (unsigned long)ff_joystick },
+       { }
+};
+MODULE_DEVICE_TABLE(hid, tm_devices);
+
+static struct hid_driver tm_driver = {
+       .name = "thrustmaster",
+       .id_table = tm_devices,
+       .probe = tm_probe,
+};
+
+static int tm_init(void)
+{
+       return hid_register_driver(&tm_driver);
+}
+
+static void tm_exit(void)
+{
+       hid_unregister_driver(&tm_driver);
+}
+
+module_init(tm_init);
+module_exit(tm_exit);
+MODULE_LICENSE("GPL");
+
+HID_COMPAT_LOAD_DRIVER(thrustmaster);
similarity index 67%
rename from drivers/hid/usbhid/hid-zpff.c
rename to drivers/hid/hid-zpff.c
index 5a688274f6a3331d2264d10de5fff780de473b9c..ea82f3718b218c2d6262a5b7f50db3f627ddfde1 100644 (file)
  */
 
 
+#include <linux/hid.h>
 #include <linux/input.h>
 #include <linux/usb.h>
-#include <linux/hid.h>
-#include "usbhid.h"
+
+#include "hid-ids.h"
+
+#include "usbhid/usbhid.h"
 
 struct zpff_device {
        struct hid_report *report;
 };
 
-static int hid_zpff_play(struct input_dev *dev, void *data,
+static int zpff_play(struct input_dev *dev, void *data,
                         struct ff_effect *effect)
 {
        struct hid_device *hid = input_get_drvdata(dev);
@@ -58,7 +61,7 @@ static int hid_zpff_play(struct input_dev *dev, void *data,
        return 0;
 }
 
-int hid_zpff_init(struct hid_device *hid)
+static int zpff_init(struct hid_device *hid)
 {
        struct zpff_device *zpff;
        struct hid_report *report;
@@ -70,14 +73,14 @@ int hid_zpff_init(struct hid_device *hid)
        int error;
 
        if (list_empty(report_list)) {
-               printk(KERN_ERR "hid-zpff: no output report found\n");
+               dev_err(&hid->dev, "no output report found\n");
                return -ENODEV;
        }
 
        report = list_entry(report_list->next, struct hid_report, list);
 
        if (report->maxfield < 4) {
-               printk(KERN_ERR "hid-zpff: not enough fields in report\n");
+               dev_err(&hid->dev, "not enough fields in report\n");
                return -ENODEV;
        }
 
@@ -87,7 +90,7 @@ int hid_zpff_init(struct hid_device *hid)
 
        set_bit(FF_RUMBLE, dev->ffbit);
 
-       error = input_ff_create_memless(dev, zpff, hid_zpff_play);
+       error = input_ff_create_memless(dev, zpff, zpff_play);
        if (error) {
                kfree(zpff);
                return error;
@@ -100,8 +103,60 @@ int hid_zpff_init(struct hid_device *hid)
        zpff->report->field[3]->value[0] = 0x00;
        usbhid_submit_report(hid, zpff->report, USB_DIR_OUT);
 
-       printk(KERN_INFO "Force feedback for Zeroplus based devices by "
+       dev_info(&hid->dev, "force feedback for Zeroplus based devices by "
               "Anssi Hannula <anssi.hannula@gmail.com>\n");
 
        return 0;
 }
+
+static int zp_probe(struct hid_device *hdev, const struct hid_device_id *id)
+{
+       int ret;
+
+       ret = hid_parse(hdev);
+       if (ret) {
+               dev_err(&hdev->dev, "parse failed\n");
+               goto err;
+       }
+
+       ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT & ~HID_CONNECT_FF);
+       if (ret) {
+               dev_err(&hdev->dev, "hw start failed\n");
+               goto err;
+       }
+
+       zpff_init(hdev);
+
+       return 0;
+err:
+       return ret;
+}
+
+static const struct hid_device_id zp_devices[] = {
+       { HID_USB_DEVICE(USB_VENDOR_ID_ZEROPLUS, 0x0005) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_ZEROPLUS, 0x0030) },
+       { }
+};
+MODULE_DEVICE_TABLE(hid, zp_devices);
+
+static struct hid_driver zp_driver = {
+       .name = "zeroplus",
+       .id_table = zp_devices,
+       .probe = zp_probe,
+};
+
+static int zp_init(void)
+{
+       return hid_register_driver(&zp_driver);
+}
+
+static void zp_exit(void)
+{
+       hid_unregister_driver(&zp_driver);
+}
+
+module_init(zp_init);
+module_exit(zp_exit);
+MODULE_LICENSE("GPL");
+
+HID_COMPAT_LOAD_DRIVER(zeroplus);
index c40f0403edafe1390d5fecb66604e095c8f70057..497e0d1dd3c374c7f65129a487f11bb4c807364a 100644 (file)
@@ -113,7 +113,7 @@ static ssize_t hidraw_write(struct file *file, const char __user *buffer, size_t
        if (!dev->hid_output_raw_report)
                return -ENODEV;
 
-       if (count > HID_MIN_BUFFER_SIZE) {
+       if (count > HID_MAX_BUFFER_SIZE) {
                printk(KERN_WARNING "hidraw: pid %d passed too large report\n",
                                task_pid_nr(current));
                return -EINVAL;
@@ -181,7 +181,7 @@ static int hidraw_open(struct inode *inode, struct file *file)
 
        dev = hidraw_table[minor];
        if (!dev->open++)
-               dev->hid->hid_open(dev->hid);
+               dev->hid->ll_driver->open(dev->hid);
 
 out_unlock:
        spin_unlock(&minors_lock);
@@ -207,7 +207,7 @@ static int hidraw_release(struct inode * inode, struct file * file)
        dev = hidraw_table[minor];
        if (!dev->open--) {
                if (list->hidraw->exist)
-                       dev->hid->hid_close(dev->hid);
+                       dev->hid->ll_driver->close(dev->hid);
                else
                        kfree(list->hidraw);
        }
@@ -367,7 +367,7 @@ void hidraw_disconnect(struct hid_device *hid)
        device_destroy(hidraw_class, MKDEV(hidraw_major, hidraw->minor));
 
        if (hidraw->open) {
-               hid->hid_close(hid);
+               hid->ll_driver->close(hid);
                wake_up_interruptible(&hidraw->wait);
        } else {
                kfree(hidraw);
index 18f09104765c71406020bc2031c76ef901fcaa2c..5d9aa95fc3ef1570c22cb8b0927461dbfba68465 100644 (file)
@@ -24,88 +24,13 @@ config USB_HID
 comment "Input core support is needed for USB HID input layer or HIDBP support"
        depends on USB_HID && INPUT=n
 
-config USB_HIDINPUT_POWERBOOK
-       bool "Enable support for Apple laptop/aluminum USB special keys"
-       default n
-       depends on USB_HID
-       help
-         Say Y here if you want support for the special keys (Fn, Numlock) on
-         Apple iBooks, PowerBooks, MacBooks, MacBook Pros and aluminum USB
-         keyboards.
-
-         If unsure, say N.
-
-config HID_FF
-       bool "Force feedback support (EXPERIMENTAL)"
-       depends on USB_HID && EXPERIMENTAL
-       help
-         Say Y here is you want force feedback support for a few HID devices.
-         See below for a list of supported devices.
-
-         See <file:Documentation/input/ff.txt> for a description of the force
-         feedback API.
-
-         If unsure, say N.
-
 config HID_PID
        bool "PID device support"
-       depends on HID_FF
        help
          Say Y here if you have a PID-compliant device and wish to enable force
          feedback for it. Microsoft Sidewinder Force Feedback 2 is one of such
          devices.
 
-config LOGITECH_FF
-       bool "Logitech devices support"
-       depends on HID_FF
-       select INPUT_FF_MEMLESS if USB_HID
-       help
-         Say Y here if you have one of these devices:
-         - Logitech WingMan Cordless RumblePad
-         - Logitech WingMan Cordless RumblePad 2
-         - Logitech WingMan Force 3D
-         - Logitech Formula Force EX
-         - Logitech MOMO Force wheel
-
-         and if you want to enable force feedback for them.
-         Note: if you say N here, this device will still be supported, but without
-         force feedback.
-
-config LOGIRUMBLEPAD2_FF
-       bool "Logitech Rumblepad 2 support"
-       depends on HID_FF
-       select INPUT_FF_MEMLESS if USB_HID
-       help
-         Say Y here if you want to enable force feedback support for Logitech
-         Rumblepad 2 devices.
-
-config PANTHERLORD_FF
-       bool "PantherLord/GreenAsia based device support"
-       depends on HID_FF
-       select INPUT_FF_MEMLESS if USB_HID
-       help
-         Say Y here if you have a PantherLord/GreenAsia based game controller
-         or adapter and want to enable force feedback support for it.
-
-config THRUSTMASTER_FF
-       bool "ThrustMaster devices support"
-       depends on HID_FF
-       select INPUT_FF_MEMLESS if USB_HID
-       help
-         Say Y here if you have a THRUSTMASTER FireStore Dual Power 2 or
-         a THRUSTMASTER Ferrari GT Rumble Force or Force Feedback Wheel,
-         and want to enable force feedback support for it.
-         Note: if you say N here, this device will still be supported, but without
-         force feedback.
-
-config ZEROPLUS_FF
-       bool "Zeroplus based game controller support"
-       depends on HID_FF
-       select INPUT_FF_MEMLESS if USB_HID
-       help
-         Say Y here if you have a Zeroplus based game controller and want to
-         enable force feedback for it.
-
 config USB_HIDDEV
        bool "/dev/hiddev raw HID device support"
        depends on USB_HID
index 00a7b70901925276e63237b023580c94ec21cec8..1329ecb37a1c7b8b4e0c24f4f3ce341b4e188061 100644 (file)
@@ -13,24 +13,6 @@ endif
 ifeq ($(CONFIG_HID_PID),y)
        usbhid-objs     += hid-pidff.o
 endif
-ifeq ($(CONFIG_LOGITECH_FF),y)
-       usbhid-objs     += hid-lgff.o
-endif
-ifeq ($(CONFIG_LOGIRUMBLEPAD2_FF),y)
-       usbhid-objs     += hid-lg2ff.o
-endif
-ifeq ($(CONFIG_PANTHERLORD_FF),y)
-       usbhid-objs     += hid-plff.o
-endif
-ifeq ($(CONFIG_THRUSTMASTER_FF),y)
-       usbhid-objs     += hid-tmff.o
-endif
-ifeq ($(CONFIG_ZEROPLUS_FF),y)
-       usbhid-objs     += hid-zpff.o
-endif
-ifeq ($(CONFIG_HID_FF),y)
-       usbhid-objs     += hid-ff.o
-endif
 
 obj-$(CONFIG_USB_HID)          += usbhid.o
 obj-$(CONFIG_USB_KBD)          += usbkbd.o
index 27fe4d8912cb9a6a1e1b847b0899103199fdbbe9..1d3b8a394d468d28d8364941cf21a2b178532b59 100644 (file)
@@ -44,8 +44,6 @@
 #define DRIVER_DESC "USB HID core driver"
 #define DRIVER_LICENSE "GPL"
 
-static char *hid_types[] = {"Device", "Pointer", "Mouse", "Device", "Joystick",
-                               "Gamepad", "Keyboard", "Keypad", "Multi-Axis Controller"};
 /*
  * Module parameters.
  */
@@ -61,12 +59,6 @@ MODULE_PARM_DESC(quirks, "Add/modify USB HID quirks by specifying "
                " quirks=vendorID:productID:quirks"
                " where vendorID, productID, and quirks are all in"
                " 0x-prefixed hex");
-static char *rdesc_quirks_param[MAX_USBHID_BOOT_QUIRKS] = { [ 0 ... (MAX_USBHID_BOOT_QUIRKS - 1) ] = NULL };
-module_param_array_named(rdesc_quirks, rdesc_quirks_param, charp, NULL, 0444);
-MODULE_PARM_DESC(rdesc_quirks, "Add/modify report descriptor quirks by specifying "
-               " rdesc_quirks=vendorID:productID:rdesc_quirks"
-               " where vendorID, productID, and rdesc_quirks are all in"
-               " 0x-prefixed hex");
 /*
  * Input submission and I/O error handler.
  */
@@ -197,31 +189,32 @@ static void hid_irq_in(struct urb *urb)
        int                     status;
 
        switch (urb->status) {
-               case 0:                 /* success */
-                       usbhid->retry_delay = 0;
-                       hid_input_report(urb->context, HID_INPUT_REPORT,
-                                        urb->transfer_buffer,
-                                        urb->actual_length, 1);
-                       break;
-               case -EPIPE:            /* stall */
-                       clear_bit(HID_IN_RUNNING, &usbhid->iofl);
-                       set_bit(HID_CLEAR_HALT, &usbhid->iofl);
-                       schedule_work(&usbhid->reset_work);
-                       return;
-               case -ECONNRESET:       /* unlink */
-               case -ENOENT:
-               case -ESHUTDOWN:        /* unplug */
-                       clear_bit(HID_IN_RUNNING, &usbhid->iofl);
-                       return;
-               case -EILSEQ:           /* protocol error or unplug */
-               case -EPROTO:           /* protocol error or unplug */
-               case -ETIME:            /* protocol error or unplug */
-               case -ETIMEDOUT:        /* Should never happen, but... */
-                       clear_bit(HID_IN_RUNNING, &usbhid->iofl);
-                       hid_io_error(hid);
-                       return;
-               default:                /* error */
-                       warn("input irq status %d received", urb->status);
+       case 0:                 /* success */
+               usbhid->retry_delay = 0;
+               hid_input_report(urb->context, HID_INPUT_REPORT,
+                                urb->transfer_buffer,
+                                urb->actual_length, 1);
+               break;
+       case -EPIPE:            /* stall */
+               clear_bit(HID_IN_RUNNING, &usbhid->iofl);
+               set_bit(HID_CLEAR_HALT, &usbhid->iofl);
+               schedule_work(&usbhid->reset_work);
+               return;
+       case -ECONNRESET:       /* unlink */
+       case -ENOENT:
+       case -ESHUTDOWN:        /* unplug */
+               clear_bit(HID_IN_RUNNING, &usbhid->iofl);
+               return;
+       case -EILSEQ:           /* protocol error or unplug */
+       case -EPROTO:           /* protocol error or unplug */
+       case -ETIME:            /* protocol error or unplug */
+       case -ETIMEDOUT:        /* Should never happen, but... */
+               clear_bit(HID_IN_RUNNING, &usbhid->iofl);
+               hid_io_error(hid);
+               return;
+       default:                /* error */
+               dev_warn(&urb->dev->dev, "input irq status %d  "
+                               "received\n", urb->status);
        }
 
        status = usb_submit_urb(urb, GFP_ATOMIC);
@@ -240,13 +233,16 @@ static void hid_irq_in(struct urb *urb)
 static int hid_submit_out(struct hid_device *hid)
 {
        struct hid_report *report;
+       char *raw_report;
        struct usbhid_device *usbhid = hid->driver_data;
 
-       report = usbhid->out[usbhid->outtail];
+       report = usbhid->out[usbhid->outtail].report;
+       raw_report = usbhid->out[usbhid->outtail].raw_report;
 
-       hid_output_report(report, usbhid->outbuf);
        usbhid->urbout->transfer_buffer_length = ((report->size - 1) >> 3) + 1 + (report->id > 0);
        usbhid->urbout->dev = hid_to_usb_dev(hid);
+       memcpy(usbhid->outbuf, raw_report, usbhid->urbout->transfer_buffer_length);
+       kfree(raw_report);
 
        dbg_hid("submitting out urb\n");
 
@@ -262,17 +258,20 @@ static int hid_submit_ctrl(struct hid_device *hid)
 {
        struct hid_report *report;
        unsigned char dir;
+       char *raw_report;
        int len;
        struct usbhid_device *usbhid = hid->driver_data;
 
        report = usbhid->ctrl[usbhid->ctrltail].report;
+       raw_report = usbhid->ctrl[usbhid->ctrltail].raw_report;
        dir = usbhid->ctrl[usbhid->ctrltail].dir;
 
        len = ((report->size - 1) >> 3) + 1 + (report->id > 0);
        if (dir == USB_DIR_OUT) {
-               hid_output_report(report, usbhid->ctrlbuf);
                usbhid->urbctrl->pipe = usb_sndctrlpipe(hid_to_usb_dev(hid), 0);
                usbhid->urbctrl->transfer_buffer_length = len;
+               memcpy(usbhid->ctrlbuf, raw_report, len);
+               kfree(raw_report);
        } else {
                int maxpacket, padlen;
 
@@ -319,17 +318,18 @@ static void hid_irq_out(struct urb *urb)
        int unplug = 0;
 
        switch (urb->status) {
-               case 0:                 /* success */
-                       break;
-               case -ESHUTDOWN:        /* unplug */
-                       unplug = 1;
-               case -EILSEQ:           /* protocol error or unplug */
-               case -EPROTO:           /* protocol error or unplug */
-               case -ECONNRESET:       /* unlink */
-               case -ENOENT:
-                       break;
-               default:                /* error */
-                       warn("output irq status %d received", urb->status);
+       case 0:                 /* success */
+               break;
+       case -ESHUTDOWN:        /* unplug */
+               unplug = 1;
+       case -EILSEQ:           /* protocol error or unplug */
+       case -EPROTO:           /* protocol error or unplug */
+       case -ECONNRESET:       /* unlink */
+       case -ENOENT:
+               break;
+       default:                /* error */
+               dev_warn(&urb->dev->dev, "output irq status %d "
+                               "received\n", urb->status);
        }
 
        spin_lock_irqsave(&usbhid->outlock, flags);
@@ -367,21 +367,23 @@ static void hid_ctrl(struct urb *urb)
        spin_lock_irqsave(&usbhid->ctrllock, flags);
 
        switch (urb->status) {
-               case 0:                 /* success */
-                       if (usbhid->ctrl[usbhid->ctrltail].dir == USB_DIR_IN)
-                               hid_input_report(urb->context, usbhid->ctrl[usbhid->ctrltail].report->type,
-                                               urb->transfer_buffer, urb->actual_length, 0);
-                       break;
-               case -ESHUTDOWN:        /* unplug */
-                       unplug = 1;
-               case -EILSEQ:           /* protocol error or unplug */
-               case -EPROTO:           /* protocol error or unplug */
-               case -ECONNRESET:       /* unlink */
-               case -ENOENT:
-               case -EPIPE:            /* report not available */
-                       break;
-               default:                /* error */
-                       warn("ctrl urb status %d received", urb->status);
+       case 0:                 /* success */
+               if (usbhid->ctrl[usbhid->ctrltail].dir == USB_DIR_IN)
+                       hid_input_report(urb->context,
+                               usbhid->ctrl[usbhid->ctrltail].report->type,
+                               urb->transfer_buffer, urb->actual_length, 0);
+               break;
+       case -ESHUTDOWN:        /* unplug */
+               unplug = 1;
+       case -EILSEQ:           /* protocol error or unplug */
+       case -EPROTO:           /* protocol error or unplug */
+       case -ECONNRESET:       /* unlink */
+       case -ENOENT:
+       case -EPIPE:            /* report not available */
+               break;
+       default:                /* error */
+               dev_warn(&urb->dev->dev, "ctrl urb status %d "
+                               "received\n", urb->status);
        }
 
        if (unplug)
@@ -408,6 +410,7 @@ void usbhid_submit_report(struct hid_device *hid, struct hid_report *report, uns
        int head;
        unsigned long flags;
        struct usbhid_device *usbhid = hid->driver_data;
+       int len = ((report->size - 1) >> 3) + 1 + (report->id > 0);
 
        if ((hid->quirks & HID_QUIRK_NOGET) && dir == USB_DIR_IN)
                return;
@@ -418,11 +421,18 @@ void usbhid_submit_report(struct hid_device *hid, struct hid_report *report, uns
 
                if ((head = (usbhid->outhead + 1) & (HID_OUTPUT_FIFO_SIZE - 1)) == usbhid->outtail) {
                        spin_unlock_irqrestore(&usbhid->outlock, flags);
-                       warn("output queue full");
+                       dev_warn(&hid->dev, "output queue full\n");
                        return;
                }
 
-               usbhid->out[usbhid->outhead] = report;
+               usbhid->out[usbhid->outhead].raw_report = kmalloc(len, GFP_ATOMIC);
+               if (!usbhid->out[usbhid->outhead].raw_report) {
+                       spin_unlock_irqrestore(&usbhid->outlock, flags);
+                       warn("output queueing failed");
+                       return;
+               }
+               hid_output_report(report, usbhid->out[usbhid->outhead].raw_report);
+               usbhid->out[usbhid->outhead].report = report;
                usbhid->outhead = head;
 
                if (!test_and_set_bit(HID_OUT_RUNNING, &usbhid->iofl))
@@ -437,10 +447,19 @@ void usbhid_submit_report(struct hid_device *hid, struct hid_report *report, uns
 
        if ((head = (usbhid->ctrlhead + 1) & (HID_CONTROL_FIFO_SIZE - 1)) == usbhid->ctrltail) {
                spin_unlock_irqrestore(&usbhid->ctrllock, flags);
-               warn("control queue full");
+               dev_warn(&hid->dev, "control queue full\n");
                return;
        }
 
+       if (dir == USB_DIR_OUT) {
+               usbhid->ctrl[usbhid->ctrlhead].raw_report = kmalloc(len, GFP_ATOMIC);
+               if (!usbhid->ctrl[usbhid->ctrlhead].raw_report) {
+                       spin_unlock_irqrestore(&usbhid->ctrllock, flags);
+                       warn("control queueing failed");
+                       return;
+               }
+               hid_output_report(report, usbhid->ctrl[usbhid->ctrlhead].raw_report);
+       }
        usbhid->ctrl[usbhid->ctrlhead].report = report;
        usbhid->ctrl[usbhid->ctrlhead].dir = dir;
        usbhid->ctrlhead = head;
@@ -451,6 +470,7 @@ void usbhid_submit_report(struct hid_device *hid, struct hid_report *report, uns
 
        spin_unlock_irqrestore(&usbhid->ctrllock, flags);
 }
+EXPORT_SYMBOL_GPL(usbhid_submit_report);
 
 static int usb_hidinput_input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
 {
@@ -465,7 +485,7 @@ static int usb_hidinput_input_event(struct input_dev *dev, unsigned int type, un
                return -1;
 
        if ((offset = hidinput_find_field(hid, type, code, &field)) == -1) {
-               warn("event field not found");
+               dev_warn(&dev->dev, "event field not found\n");
                return -1;
        }
 
@@ -568,7 +588,7 @@ void usbhid_init_reports(struct hid_device *hid)
        }
 
        if (err)
-               warn("timeout initializing reports");
+               dev_warn(&hid->dev, "timeout initializing reports\n");
 }
 
 /*
@@ -598,7 +618,7 @@ static int hid_find_field_early(struct hid_device *hid, unsigned int page,
        return -1;
 }
 
-static void usbhid_set_leds(struct hid_device *hid)
+void usbhid_set_leds(struct hid_device *hid)
 {
        struct hid_field *field;
        int offset;
@@ -608,6 +628,7 @@ static void usbhid_set_leds(struct hid_device *hid)
                usbhid_submit_report(hid, field->report, USB_DIR_OUT);
        }
 }
+EXPORT_SYMBOL_GPL(usbhid_set_leds);
 
 /*
  * Traverse the supplied list of reports and find the longest
@@ -675,43 +696,16 @@ static void hid_free_buffers(struct usb_device *dev, struct hid_device *hid)
        usb_buffer_free(dev, usbhid->bufsize, usbhid->ctrlbuf, usbhid->ctrlbuf_dma);
 }
 
-/*
- * Sending HID_REQ_GET_REPORT changes the operation mode of the ps3 controller
- * to "operational".  Without this, the ps3 controller will not report any
- * events.
- */
-static void hid_fixup_sony_ps3_controller(struct usb_device *dev, int ifnum)
-{
-       int result;
-       char *buf = kmalloc(18, GFP_KERNEL);
-
-       if (!buf)
-               return;
-
-       result = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
-                                HID_REQ_GET_REPORT,
-                                USB_DIR_IN | USB_TYPE_CLASS |
-                                USB_RECIP_INTERFACE,
-                                (3 << 8) | 0xf2, ifnum, buf, 17,
-                                USB_CTRL_GET_TIMEOUT);
-
-       if (result < 0)
-               err_hid("%s failed: %d\n", __func__, result);
-
-       kfree(buf);
-}
-
-static struct hid_device *usb_hid_configure(struct usb_interface *intf)
+static int usbhid_parse(struct hid_device *hid)
 {
+       struct usb_interface *intf = to_usb_interface(hid->dev.parent);
        struct usb_host_interface *interface = intf->cur_altsetting;
        struct usb_device *dev = interface_to_usbdev (intf);
        struct hid_descriptor *hdesc;
-       struct hid_device *hid;
        u32 quirks = 0;
-       unsigned int insize = 0, rsize = 0;
+       unsigned int rsize = 0;
        char *rdesc;
-       int n, len;
-       struct usbhid_device *usbhid;
+       int ret, n;
 
        quirks = usbhid_lookup_quirk(le16_to_cpu(dev->descriptor.idVendor),
                        le16_to_cpu(dev->descriptor.idProduct));
@@ -724,63 +718,75 @@ static struct hid_device *usb_hid_configure(struct usb_interface *intf)
                                quirks |= HID_QUIRK_NOGET;
        }
 
-       if (quirks & HID_QUIRK_IGNORE)
-               return NULL;
-
-       if ((quirks & HID_QUIRK_IGNORE_MOUSE) &&
-               (interface->desc.bInterfaceProtocol == USB_INTERFACE_PROTOCOL_MOUSE))
-                       return NULL;
-
-
        if (usb_get_extra_descriptor(interface, HID_DT_HID, &hdesc) &&
            (!interface->desc.bNumEndpoints ||
             usb_get_extra_descriptor(&interface->endpoint[0], HID_DT_HID, &hdesc))) {
                dbg_hid("class descriptor not present\n");
-               return NULL;
+               return -ENODEV;
        }
 
+       hid->version = le16_to_cpu(hdesc->bcdHID);
+       hid->country = hdesc->bCountryCode;
+
        for (n = 0; n < hdesc->bNumDescriptors; n++)
                if (hdesc->desc[n].bDescriptorType == HID_DT_REPORT)
                        rsize = le16_to_cpu(hdesc->desc[n].wDescriptorLength);
 
        if (!rsize || rsize > HID_MAX_DESCRIPTOR_SIZE) {
                dbg_hid("weird size of report descriptor (%u)\n", rsize);
-               return NULL;
+               return -EINVAL;
        }
 
        if (!(rdesc = kmalloc(rsize, GFP_KERNEL))) {
                dbg_hid("couldn't allocate rdesc memory\n");
-               return NULL;
+               return -ENOMEM;
        }
 
        hid_set_idle(dev, interface->desc.bInterfaceNumber, 0, 0);
 
-       if ((n = hid_get_class_descriptor(dev, interface->desc.bInterfaceNumber, HID_DT_REPORT, rdesc, rsize)) < 0) {
+       ret = hid_get_class_descriptor(dev, interface->desc.bInterfaceNumber,
+                       HID_DT_REPORT, rdesc, rsize);
+       if (ret < 0) {
                dbg_hid("reading report descriptor failed\n");
                kfree(rdesc);
-               return NULL;
+               goto err;
        }
 
-       usbhid_fixup_report_descriptor(le16_to_cpu(dev->descriptor.idVendor),
-                       le16_to_cpu(dev->descriptor.idProduct), rdesc,
-                       rsize, rdesc_quirks_param);
-
        dbg_hid("report descriptor (size %u, read %d) = ", rsize, n);
        for (n = 0; n < rsize; n++)
                dbg_hid_line(" %02x", (unsigned char) rdesc[n]);
        dbg_hid_line("\n");
 
-       if (!(hid = hid_parse_report(rdesc, n))) {
+       ret = hid_parse_report(hid, rdesc, rsize);
+       kfree(rdesc);
+       if (ret) {
                dbg_hid("parsing report descriptor failed\n");
-               kfree(rdesc);
-               return NULL;
+               goto err;
        }
 
-       kfree(rdesc);
        hid->quirks = quirks;
 
-       if (!(usbhid = kzalloc(sizeof(struct usbhid_device), GFP_KERNEL)))
-               goto fail_no_usbhid;
+       return 0;
+err:
+       return ret;
+}
+
+static int usbhid_start(struct hid_device *hid)
+{
+       struct usb_interface *intf = to_usb_interface(hid->dev.parent);
+       struct usb_host_interface *interface = intf->cur_altsetting;
+       struct usb_device *dev = interface_to_usbdev(intf);
+       struct usbhid_device *usbhid;
+       unsigned int n, insize = 0;
+       int ret;
+
+       WARN_ON(hid->driver_data);
+
+       usbhid = kzalloc(sizeof(struct usbhid_device), GFP_KERNEL);
+       if (usbhid == NULL) {
+               ret = -ENOMEM;
+               goto err;
+       }
 
        hid->driver_data = usbhid;
        usbhid->hid = hid;
@@ -799,28 +805,11 @@ static struct hid_device *usb_hid_configure(struct usb_interface *intf)
                insize = HID_MAX_BUFFER_SIZE;
 
        if (hid_alloc_buffers(dev, hid)) {
-               hid_free_buffers(dev, hid);
+               ret = -ENOMEM;
                goto fail;
        }
 
-       hid->name[0] = 0;
-
-       if (dev->manufacturer)
-               strlcpy(hid->name, dev->manufacturer, sizeof(hid->name));
-
-       if (dev->product) {
-               if (dev->manufacturer)
-                       strlcat(hid->name, " ", sizeof(hid->name));
-               strlcat(hid->name, dev->product, sizeof(hid->name));
-       }
-
-       if (!strlen(hid->name))
-               snprintf(hid->name, sizeof(hid->name), "HID %04x:%04x",
-                        le16_to_cpu(dev->descriptor.idVendor),
-                        le16_to_cpu(dev->descriptor.idProduct));
-
        for (n = 0; n < interface->desc.bNumEndpoints; n++) {
-
                struct usb_endpoint_descriptor *endpoint;
                int pipe;
                int interval;
@@ -832,7 +821,7 @@ static struct hid_device *usb_hid_configure(struct usb_interface *intf)
                interval = endpoint->bInterval;
 
                /* Some vendors give fullspeed interval on highspeed devides */
-               if (quirks & HID_QUIRK_FULLSPEED_INTERVAL  &&
+               if (hid->quirks & HID_QUIRK_FULLSPEED_INTERVAL &&
                    dev->speed == USB_SPEED_HIGH) {
                        interval = fls(endpoint->bInterval*8);
                        printk(KERN_INFO "%s: Fixing fullspeed to highspeed interval: %d -> %d\n",
@@ -843,6 +832,7 @@ static struct hid_device *usb_hid_configure(struct usb_interface *intf)
                if (hid->collection->usage == HID_GD_MOUSE && hid_mousepoll_interval > 0)
                        interval = hid_mousepoll_interval;
 
+               ret = -ENOMEM;
                if (usb_endpoint_dir_in(endpoint)) {
                        if (usbhid->urbin)
                                continue;
@@ -868,6 +858,7 @@ static struct hid_device *usb_hid_configure(struct usb_interface *intf)
 
        if (!usbhid->urbin) {
                err_hid("couldn't find an input interrupt endpoint");
+               ret = -ENODEV;
                goto fail;
        }
 
@@ -879,44 +870,25 @@ static struct hid_device *usb_hid_configure(struct usb_interface *intf)
        spin_lock_init(&usbhid->outlock);
        spin_lock_init(&usbhid->ctrllock);
 
-       hid->version = le16_to_cpu(hdesc->bcdHID);
-       hid->country = hdesc->bCountryCode;
-       hid->dev = &intf->dev;
        usbhid->intf = intf;
        usbhid->ifnum = interface->desc.bInterfaceNumber;
 
-       hid->bus = BUS_USB;
-       hid->vendor = le16_to_cpu(dev->descriptor.idVendor);
-       hid->product = le16_to_cpu(dev->descriptor.idProduct);
-
-       usb_make_path(dev, hid->phys, sizeof(hid->phys));
-       strlcat(hid->phys, "/input", sizeof(hid->phys));
-       len = strlen(hid->phys);
-       if (len < sizeof(hid->phys) - 1)
-               snprintf(hid->phys + len, sizeof(hid->phys) - len,
-                        "%d", intf->altsetting[0].desc.bInterfaceNumber);
-
-       if (usb_string(dev, dev->descriptor.iSerialNumber, hid->uniq, 64) <= 0)
-               hid->uniq[0] = 0;
-
        usbhid->urbctrl = usb_alloc_urb(0, GFP_KERNEL);
-       if (!usbhid->urbctrl)
+       if (!usbhid->urbctrl) {
+               ret = -ENOMEM;
                goto fail;
+       }
 
        usb_fill_control_urb(usbhid->urbctrl, dev, 0, (void *) usbhid->cr,
                             usbhid->ctrlbuf, 1, hid_ctrl, hid);
        usbhid->urbctrl->setup_dma = usbhid->cr_dma;
        usbhid->urbctrl->transfer_dma = usbhid->ctrlbuf_dma;
        usbhid->urbctrl->transfer_flags |= (URB_NO_TRANSFER_DMA_MAP | URB_NO_SETUP_DMA_MAP);
-       hid->hidinput_input_event = usb_hidinput_input_event;
-       hid->hid_open = usbhid_open;
-       hid->hid_close = usbhid_close;
-#ifdef CONFIG_USB_HIDDEV
-       hid->hiddev_hid_event = hiddev_hid_event;
-       hid->hiddev_report_event = hiddev_report_event;
-#endif
-       hid->hid_output_raw_report = usbhid_output_raw_report;
-       return hid;
+
+       usbhid_init_reports(hid);
+       hid_dump_device(hid);
+
+       return 0;
 
 fail:
        usb_free_urb(usbhid->urbin);
@@ -924,24 +896,18 @@ fail:
        usb_free_urb(usbhid->urbctrl);
        hid_free_buffers(dev, hid);
        kfree(usbhid);
-fail_no_usbhid:
-       hid_free_device(hid);
-
-       return NULL;
+err:
+       return ret;
 }
 
-static void hid_disconnect(struct usb_interface *intf)
+static void usbhid_stop(struct hid_device *hid)
 {
-       struct hid_device *hid = usb_get_intfdata (intf);
-       struct usbhid_device *usbhid;
+       struct usbhid_device *usbhid = hid->driver_data;
 
-       if (!hid)
+       if (WARN_ON(!usbhid))
                return;
 
-       usbhid = hid->driver_data;
-
        spin_lock_irq(&usbhid->inlock); /* Sync with error handler */
-       usb_set_intfdata(intf, NULL);
        set_bit(HID_DISCONNECTED, &usbhid->iofl);
        spin_unlock_irq(&usbhid->inlock);
        usb_kill_urb(usbhid->urbin);
@@ -958,86 +924,100 @@ static void hid_disconnect(struct usb_interface *intf)
        if (hid->claimed & HID_CLAIMED_HIDRAW)
                hidraw_disconnect(hid);
 
+       hid->claimed = 0;
+
        usb_free_urb(usbhid->urbin);
        usb_free_urb(usbhid->urbctrl);
        usb_free_urb(usbhid->urbout);
 
        hid_free_buffers(hid_to_usb_dev(hid), hid);
        kfree(usbhid);
-       hid_free_device(hid);
+       hid->driver_data = NULL;
 }
 
+static struct hid_ll_driver usb_hid_driver = {
+       .parse = usbhid_parse,
+       .start = usbhid_start,
+       .stop = usbhid_stop,
+       .open = usbhid_open,
+       .close = usbhid_close,
+       .hidinput_input_event = usb_hidinput_input_event,
+};
+
 static int hid_probe(struct usb_interface *intf, const struct usb_device_id *id)
 {
+       struct usb_device *dev = interface_to_usbdev(intf);
        struct hid_device *hid;
-       char path[64];
-       int i;
-       char *c;
+       size_t len;
+       int ret;
 
        dbg_hid("HID probe called for ifnum %d\n",
                        intf->altsetting->desc.bInterfaceNumber);
 
-       if (!(hid = usb_hid_configure(intf)))
-               return -ENODEV;
-
-       usbhid_init_reports(hid);
-       hid_dump_device(hid);
-       if (hid->quirks & HID_QUIRK_RESET_LEDS)
-               usbhid_set_leds(hid);
-
-       if (!hidinput_connect(hid))
-               hid->claimed |= HID_CLAIMED_INPUT;
-       if (!hiddev_connect(hid))
-               hid->claimed |= HID_CLAIMED_HIDDEV;
-       if (!hidraw_connect(hid))
-               hid->claimed |= HID_CLAIMED_HIDRAW;
+       hid = hid_allocate_device();
+       if (IS_ERR(hid))
+               return PTR_ERR(hid);
 
        usb_set_intfdata(intf, hid);
+       hid->ll_driver = &usb_hid_driver;
+       hid->hid_output_raw_report = usbhid_output_raw_report;
+       hid->ff_init = hid_pidff_init;
+#ifdef CONFIG_USB_HIDDEV
+       hid->hiddev_connect = hiddev_connect;
+       hid->hiddev_hid_event = hiddev_hid_event;
+       hid->hiddev_report_event = hiddev_report_event;
+#endif
+       hid->dev.parent = &intf->dev;
+       hid->bus = BUS_USB;
+       hid->vendor = le16_to_cpu(dev->descriptor.idVendor);
+       hid->product = le16_to_cpu(dev->descriptor.idProduct);
+       hid->name[0] = 0;
 
-       if (!hid->claimed) {
-               printk ("HID device claimed by neither input, hiddev nor hidraw\n");
-               hid_disconnect(intf);
-               return -ENODEV;
+       if (dev->manufacturer)
+               strlcpy(hid->name, dev->manufacturer, sizeof(hid->name));
+
+       if (dev->product) {
+               if (dev->manufacturer)
+                       strlcat(hid->name, " ", sizeof(hid->name));
+               strlcat(hid->name, dev->product, sizeof(hid->name));
        }
 
-       if ((hid->claimed & HID_CLAIMED_INPUT))
-               hid_ff_init(hid);
+       if (!strlen(hid->name))
+               snprintf(hid->name, sizeof(hid->name), "HID %04x:%04x",
+                        le16_to_cpu(dev->descriptor.idVendor),
+                        le16_to_cpu(dev->descriptor.idProduct));
 
-       if (hid->quirks & HID_QUIRK_SONY_PS3_CONTROLLER)
-               hid_fixup_sony_ps3_controller(interface_to_usbdev(intf),
-                       intf->cur_altsetting->desc.bInterfaceNumber);
+       usb_make_path(dev, hid->phys, sizeof(hid->phys));
+       strlcat(hid->phys, "/input", sizeof(hid->phys));
+       len = strlen(hid->phys);
+       if (len < sizeof(hid->phys) - 1)
+               snprintf(hid->phys + len, sizeof(hid->phys) - len,
+                        "%d", intf->altsetting[0].desc.bInterfaceNumber);
 
-       printk(KERN_INFO);
+       if (usb_string(dev, dev->descriptor.iSerialNumber, hid->uniq, 64) <= 0)
+               hid->uniq[0] = 0;
 
-       if (hid->claimed & HID_CLAIMED_INPUT)
-               printk("input");
-       if ((hid->claimed & HID_CLAIMED_INPUT) && ((hid->claimed & HID_CLAIMED_HIDDEV) ||
-                               hid->claimed & HID_CLAIMED_HIDRAW))
-               printk(",");
-       if (hid->claimed & HID_CLAIMED_HIDDEV)
-               printk("hiddev%d", hid->minor);
-       if ((hid->claimed & HID_CLAIMED_INPUT) && (hid->claimed & HID_CLAIMED_HIDDEV) &&
-                       (hid->claimed & HID_CLAIMED_HIDRAW))
-               printk(",");
-       if (hid->claimed & HID_CLAIMED_HIDRAW)
-               printk("hidraw%d", ((struct hidraw*)hid->hidraw)->minor);
-
-       c = "Device";
-       for (i = 0; i < hid->maxcollection; i++) {
-               if (hid->collection[i].type == HID_COLLECTION_APPLICATION &&
-                   (hid->collection[i].usage & HID_USAGE_PAGE) == HID_UP_GENDESK &&
-                   (hid->collection[i].usage & 0xffff) < ARRAY_SIZE(hid_types)) {
-                       c = hid_types[hid->collection[i].usage & 0xffff];
-                       break;
-               }
+       ret = hid_add_device(hid);
+       if (ret) {
+               if (ret != -ENODEV)
+                       dev_err(&intf->dev, "can't add hid device: %d\n", ret);
+               goto err;
        }
 
-       usb_make_path(interface_to_usbdev(intf), path, 63);
+       return 0;
+err:
+       hid_destroy_device(hid);
+       return ret;
+}
 
-       printk(": USB HID v%x.%02x %s [%s] on %s\n",
-               hid->version >> 8, hid->version & 0xff, c, hid->name, path);
+static void hid_disconnect(struct usb_interface *intf)
+{
+       struct hid_device *hid = usb_get_intfdata(intf);
 
-       return 0;
+       if (WARN_ON(!hid))
+               return;
+
+       hid_destroy_device(hid);
 }
 
 static int hid_suspend(struct usb_interface *intf, pm_message_t message)
@@ -1107,9 +1087,22 @@ static struct usb_driver hid_driver = {
        .supports_autosuspend = 1,
 };
 
+static const struct hid_device_id hid_usb_table[] = {
+       { HID_USB_DEVICE(HID_ANY_ID, HID_ANY_ID) },
+       { }
+};
+
+static struct hid_driver hid_usb_driver = {
+       .name = "generic-usb",
+       .id_table = hid_usb_table,
+};
+
 static int __init hid_init(void)
 {
        int retval;
+       retval = hid_register_driver(&hid_usb_driver);
+       if (retval)
+               goto hid_register_fail;
        retval = usbhid_quirks_init(quirks_param);
        if (retval)
                goto usbhid_quirks_init_fail;
@@ -1119,7 +1112,8 @@ static int __init hid_init(void)
        retval = usb_register(&hid_driver);
        if (retval)
                goto usb_register_fail;
-       info(DRIVER_VERSION ":" DRIVER_DESC);
+       printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_VERSION ":"
+                       DRIVER_DESC "\n");
 
        return 0;
 usb_register_fail:
@@ -1127,6 +1121,8 @@ usb_register_fail:
 hiddev_init_fail:
        usbhid_quirks_exit();
 usbhid_quirks_init_fail:
+       hid_unregister_driver(&hid_usb_driver);
+hid_register_fail:
        return retval;
 }
 
@@ -1135,6 +1131,7 @@ static void __exit hid_exit(void)
        usb_deregister(&hid_driver);
        hiddev_exit();
        usbhid_quirks_exit();
+       hid_unregister_driver(&hid_usb_driver);
 }
 
 module_init(hid_init);
diff --git a/drivers/hid/usbhid/hid-ff.c b/drivers/hid/usbhid/hid-ff.c
deleted file mode 100644 (file)
index 1d0dac5..0000000
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- *  Force feedback support for hid devices.
- *  Not all hid devices use the same protocol. For example, some use PID,
- *  other use their own proprietary procotol.
- *
- *  Copyright (c) 2002-2004 Johann Deneux
- */
-
-/*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
- * Should you need to contact me, the author, you can do so by
- * e-mail - mail your message to <johann.deneux@it.uu.se>
- */
-
-#include <linux/input.h>
-
-#undef DEBUG
-#include <linux/usb.h>
-
-#include <linux/hid.h>
-#include "usbhid.h"
-
-/*
- * This table contains pointers to initializers. To add support for new
- * devices, you need to add the USB vendor and product ids here.
- */
-struct hid_ff_initializer {
-       u16 idVendor;
-       u16 idProduct;
-       int (*init)(struct hid_device*);
-};
-
-/*
- * We try pidff when no other driver is found because PID is the
- * standards compliant way of implementing force feedback in HID.
- * pidff_init() will quickly abort if the device doesn't appear to
- * be a PID device
- */
-static struct hid_ff_initializer inits[] = {
-#ifdef CONFIG_LOGITECH_FF
-       { 0x46d, 0xc211, hid_lgff_init }, /* Logitech Cordless rumble pad */
-       { 0x46d, 0xc219, hid_lgff_init }, /* Logitech Cordless rumble pad 2 */
-       { 0x46d, 0xc283, hid_lgff_init }, /* Logitech Wingman Force 3d */
-       { 0x46d, 0xc286, hid_lgff_init }, /* Logitech Force 3D Pro Joystick */
-       { 0x46d, 0xc294, hid_lgff_init }, /* Logitech Formula Force EX */
-       { 0x46d, 0xc295, hid_lgff_init }, /* Logitech MOMO force wheel */
-       { 0x46d, 0xca03, hid_lgff_init }, /* Logitech MOMO force wheel */
-#endif
-#ifdef CONFIG_LOGIRUMBLEPAD2_FF
-       { 0x46d, 0xc218, hid_lg2ff_init }, /* Logitech Rumblepad 2 */
-#endif
-#ifdef CONFIG_PANTHERLORD_FF
-       { 0x810, 0x0001, hid_plff_init }, /* "Twin USB Joystick" */
-       { 0xe8f, 0x0003, hid_plff_init }, /* "GreenAsia Inc.    USB Joystick     " */
-#endif
-#ifdef CONFIG_THRUSTMASTER_FF
-       { 0x44f, 0xb300, hid_tmff_init },
-       { 0x44f, 0xb304, hid_tmff_init },
-       { 0x44f, 0xb651, hid_tmff_init }, /* FGT Rumble Force Wheel */
-       { 0x44f, 0xb654, hid_tmff_init }, /* FGT Force Feedback Wheel */
-#endif
-#ifdef CONFIG_ZEROPLUS_FF
-       { 0xc12, 0x0005, hid_zpff_init },
-       { 0xc12, 0x0030, hid_zpff_init },
-#endif
-       { 0,     0,      hid_pidff_init}  /* Matches anything */
-};
-
-int hid_ff_init(struct hid_device* hid)
-{
-       struct hid_ff_initializer *init;
-       int vendor = le16_to_cpu(hid_to_usb_dev(hid)->descriptor.idVendor);
-       int product = le16_to_cpu(hid_to_usb_dev(hid)->descriptor.idProduct);
-
-       for (init = inits; init->idVendor; init++)
-               if (init->idVendor == vendor && init->idProduct == product)
-                       break;
-
-       return init->init(hid);
-}
-EXPORT_SYMBOL_GPL(hid_ff_init);
-
index 011326178c0670c5b5e680a47d741661101a45aa..484e3eec2f88be2b740536fdae06cdc2e60dbc65 100644 (file)
@@ -397,7 +397,6 @@ static void pidff_set_condition_report(struct pidff_device *pidff,
                          effect->u.condition[i].left_saturation);
                pidff_set(&pidff->set_condition[PID_DEAD_BAND],
                          effect->u.condition[i].deadband);
-               usbhid_wait_io(pidff->hid);
                usbhid_submit_report(pidff->hid, pidff->reports[PID_SET_CONDITION],
                                  USB_DIR_OUT);
        }
@@ -512,7 +511,6 @@ static void pidff_playback_pid(struct pidff_device *pidff, int pid_id, int n)
                pidff->effect_operation[PID_LOOP_COUNT].value[0] = n;
        }
 
-       usbhid_wait_io(pidff->hid);
        usbhid_submit_report(pidff->hid, pidff->reports[PID_EFFECT_OPERATION],
                          USB_DIR_OUT);
 }
@@ -548,6 +546,9 @@ static int pidff_erase_effect(struct input_dev *dev, int effect_id)
        int pid_id = pidff->pid_id[effect_id];
 
        debug("starting to erase %d/%d", effect_id, pidff->pid_id[effect_id]);
+       /* Wait for the queue to clear. We do not want a full fifo to
+          prevent the effect removal. */
+       usbhid_wait_io(pidff->hid);
        pidff_playback_pid(pidff, pid_id, 0);
        pidff_erase_pid(pidff, pid_id);
 
index b15f882496394cbf7af43d7b07707e6b4750c54e..47ebe045f9b5a1548010a5d19694e4c91dbee9cc 100644 (file)
 
 #include <linux/hid.h>
 
-#define USB_VENDOR_ID_A4TECH           0x09da
-#define USB_DEVICE_ID_A4TECH_WCP32PU   0x0006
-#define USB_DEVICE_ID_A4TECH_X5_005D   0x000a
-
-#define USB_VENDOR_ID_AASHIMA          0x06d6
-#define USB_DEVICE_ID_AASHIMA_GAMEPAD  0x0025
-#define USB_DEVICE_ID_AASHIMA_PREDATOR 0x0026
-
-#define USB_VENDOR_ID_ACECAD           0x0460
-#define USB_DEVICE_ID_ACECAD_FLAIR     0x0004
-#define USB_DEVICE_ID_ACECAD_302       0x0008
-
-#define USB_VENDOR_ID_ADS_TECH                 0x06e1
-#define USB_DEVICE_ID_ADS_TECH_RADIO_SI470X    0xa155
-
-#define USB_VENDOR_ID_AFATECH          0x15a4
-#define USB_DEVICE_ID_AFATECH_AF9016   0x9016
-
-#define USB_VENDOR_ID_AIPTEK           0x08ca
-#define USB_DEVICE_ID_AIPTEK_01                0x0001
-#define USB_DEVICE_ID_AIPTEK_10                0x0010
-#define USB_DEVICE_ID_AIPTEK_20                0x0020
-#define USB_DEVICE_ID_AIPTEK_21                0x0021
-#define USB_DEVICE_ID_AIPTEK_22                0x0022
-#define USB_DEVICE_ID_AIPTEK_23                0x0023
-#define USB_DEVICE_ID_AIPTEK_24                0x0024
-
-#define USB_VENDOR_ID_AIRCABLE         0x16CA
-#define USB_DEVICE_ID_AIRCABLE1                0x1502
-
-#define USB_VENDOR_ID_ALCOR            0x058f
-#define USB_DEVICE_ID_ALCOR_USBRS232   0x9720
-
-#define USB_VENDOR_ID_ALPS             0x0433
-#define USB_DEVICE_ID_IBM_GAMEPAD      0x1101
-
-#define USB_VENDOR_ID_APPLE            0x05ac
-#define USB_DEVICE_ID_APPLE_MIGHTYMOUSE        0x0304
-#define USB_DEVICE_ID_APPLE_FOUNTAIN_ANSI      0x020e
-#define USB_DEVICE_ID_APPLE_FOUNTAIN_ISO       0x020f
-#define USB_DEVICE_ID_APPLE_GEYSER_ANSI        0x0214
-#define USB_DEVICE_ID_APPLE_GEYSER_ISO 0x0215
-#define USB_DEVICE_ID_APPLE_GEYSER_JIS 0x0216
-#define USB_DEVICE_ID_APPLE_GEYSER3_ANSI       0x0217
-#define USB_DEVICE_ID_APPLE_GEYSER3_ISO        0x0218
-#define USB_DEVICE_ID_APPLE_GEYSER3_JIS        0x0219
-#define USB_DEVICE_ID_APPLE_GEYSER4_ANSI       0x021a
-#define USB_DEVICE_ID_APPLE_GEYSER4_ISO        0x021b
-#define USB_DEVICE_ID_APPLE_GEYSER4_JIS        0x021c
-#define USB_DEVICE_ID_APPLE_ALU_ANSI   0x0220
-#define USB_DEVICE_ID_APPLE_ALU_ISO    0x0221
-#define USB_DEVICE_ID_APPLE_ALU_JIS    0x0222
-#define USB_DEVICE_ID_APPLE_WELLSPRING_ANSI    0x0223
-#define USB_DEVICE_ID_APPLE_WELLSPRING_ISO     0x0224
-#define USB_DEVICE_ID_APPLE_WELLSPRING_JIS     0x0225
-#define USB_DEVICE_ID_APPLE_GEYSER4_HF_ANSI    0x0229
-#define USB_DEVICE_ID_APPLE_GEYSER4_HF_ISO     0x022a
-#define USB_DEVICE_ID_APPLE_GEYSER4_HF_JIS     0x022b
-#define USB_DEVICE_ID_APPLE_ALU_WIRELESS_ANSI  0x022c
-#define USB_DEVICE_ID_APPLE_ALU_WIRELESS_ISO   0x022d
-#define USB_DEVICE_ID_APPLE_ALU_WIRELESS_JIS   0x022e
-#define USB_DEVICE_ID_APPLE_WELLSPRING2_ANSI   0x0230
-#define USB_DEVICE_ID_APPLE_WELLSPRING2_ISO    0x0231
-#define USB_DEVICE_ID_APPLE_WELLSPRING2_JIS    0x0232
-#define USB_DEVICE_ID_APPLE_FOUNTAIN_TP_ONLY   0x030a
-#define USB_DEVICE_ID_APPLE_GEYSER1_TP_ONLY    0x030b
-#define USB_DEVICE_ID_APPLE_IRCONTROL4 0x8242
-
-#define USB_VENDOR_ID_ASUS             0x0b05
-#define USB_DEVICE_ID_ASUS_LCM         0x1726
-
-#define USB_VENDOR_ID_ATEN             0x0557
-#define USB_DEVICE_ID_ATEN_UC100KM     0x2004
-#define USB_DEVICE_ID_ATEN_CS124U      0x2202
-#define USB_DEVICE_ID_ATEN_2PORTKVM    0x2204
-#define USB_DEVICE_ID_ATEN_4PORTKVM    0x2205
-#define USB_DEVICE_ID_ATEN_4PORTKVMC   0x2208
-
-#define USB_VENDOR_ID_BELKIN           0x050d
-#define USB_DEVICE_ID_FLIP_KVM         0x3201
-
-#define USB_VENDOR_ID_BERKSHIRE                0x0c98
-#define USB_DEVICE_ID_BERKSHIRE_PCWD   0x1140
-
-#define USB_VENDOR_ID_CHERRY           0x046a
-#define USB_DEVICE_ID_CHERRY_CYMOTION  0x0023
-
-#define USB_VENDOR_ID_CHIC             0x05fe
-#define USB_DEVICE_ID_CHIC_GAMEPAD     0x0014
-
-#define USB_VENDOR_ID_CIDC             0x1677
-
-#define USB_VENDOR_ID_CMEDIA           0x0d8c
-#define USB_DEVICE_ID_CM109            0x000e
-
-#define USB_VENDOR_ID_CODEMERCS                0x07c0
-#define USB_DEVICE_ID_CODEMERCS_IOW_FIRST      0x1500
-#define USB_DEVICE_ID_CODEMERCS_IOW_LAST       0x15ff
-
-#define USB_VENDOR_ID_CYGNAL           0x10c4
-#define USB_DEVICE_ID_CYGNAL_RADIO_SI470X      0x818a
-
-#define USB_VENDOR_ID_CYPRESS          0x04b4
-#define USB_DEVICE_ID_CYPRESS_MOUSE    0x0001
-#define USB_DEVICE_ID_CYPRESS_HIDCOM   0x5500
-#define USB_DEVICE_ID_CYPRESS_ULTRAMOUSE       0x7417
-#define USB_DEVICE_ID_CYPRESS_BARCODE_1        0xde61
-#define USB_DEVICE_ID_CYPRESS_BARCODE_2        0xde64
-
-#define USB_VENDOR_ID_DELL             0x413c
-#define USB_DEVICE_ID_DELL_W7658       0x2005
-
-#define USB_VENDOR_ID_DELORME          0x1163
-#define USB_DEVICE_ID_DELORME_EARTHMATE 0x0100
-#define USB_DEVICE_ID_DELORME_EM_LT20  0x0200
-
-#define USB_VENDOR_ID_DMI              0x0c0b
-#define USB_DEVICE_ID_DMI_ENC          0x5fab
-
-#define USB_VENDOR_ID_ELO              0x04E7
-#define USB_DEVICE_ID_ELO_TS2700       0x0020
-
-#define USB_VENDOR_ID_ESSENTIAL_REALITY        0x0d7f
-#define USB_DEVICE_ID_ESSENTIAL_REALITY_P5 0x0100
-
-#define USB_VENDOR_ID_EZKEY            0x0518
-#define USB_DEVICE_ID_BTC_8193         0x0002
-
-#define USB_VENDOR_ID_GAMERON          0x0810
-#define USB_DEVICE_ID_GAMERON_DUAL_PSX_ADAPTOR 0x0001
-
-#define USB_VENDOR_ID_GENERAL_TOUCH    0x0dfc
-
-#define USB_VENDOR_ID_GLAB             0x06c2
-#define USB_DEVICE_ID_4_PHIDGETSERVO_30        0x0038
-#define USB_DEVICE_ID_1_PHIDGETSERVO_30        0x0039
-#define USB_DEVICE_ID_0_0_4_IF_KIT     0x0040
-#define USB_DEVICE_ID_0_16_16_IF_KIT   0x0044
-#define USB_DEVICE_ID_8_8_8_IF_KIT     0x0045
-#define USB_DEVICE_ID_0_8_7_IF_KIT     0x0051
-#define USB_DEVICE_ID_0_8_8_IF_KIT     0x0053
-#define USB_DEVICE_ID_PHIDGET_MOTORCONTROL     0x0058
-
-#define USB_VENDOR_ID_GOTOP            0x08f2
-#define USB_DEVICE_ID_SUPER_Q2         0x007f
-#define USB_DEVICE_ID_GOGOPEN          0x00ce
-#define USB_DEVICE_ID_PENPOWER         0x00f4
-
-#define USB_VENDOR_ID_GRETAGMACBETH    0x0971
-#define USB_DEVICE_ID_GRETAGMACBETH_HUEY       0x2005
-
-#define USB_VENDOR_ID_GRIFFIN          0x077d
-#define USB_DEVICE_ID_POWERMATE                0x0410
-#define USB_DEVICE_ID_SOUNDKNOB                0x04AA
-
-#define USB_VENDOR_ID_GTCO             0x078c
-#define USB_DEVICE_ID_GTCO_90          0x0090
-#define USB_DEVICE_ID_GTCO_100         0x0100
-#define USB_DEVICE_ID_GTCO_101         0x0101
-#define USB_DEVICE_ID_GTCO_103         0x0103
-#define USB_DEVICE_ID_GTCO_104         0x0104
-#define USB_DEVICE_ID_GTCO_105         0x0105
-#define USB_DEVICE_ID_GTCO_106         0x0106
-#define USB_DEVICE_ID_GTCO_107         0x0107
-#define USB_DEVICE_ID_GTCO_108         0x0108
-#define USB_DEVICE_ID_GTCO_200         0x0200
-#define USB_DEVICE_ID_GTCO_201         0x0201
-#define USB_DEVICE_ID_GTCO_202         0x0202
-#define USB_DEVICE_ID_GTCO_203         0x0203
-#define USB_DEVICE_ID_GTCO_204         0x0204
-#define USB_DEVICE_ID_GTCO_205         0x0205
-#define USB_DEVICE_ID_GTCO_206         0x0206
-#define USB_DEVICE_ID_GTCO_207         0x0207
-#define USB_DEVICE_ID_GTCO_300         0x0300
-#define USB_DEVICE_ID_GTCO_301         0x0301
-#define USB_DEVICE_ID_GTCO_302         0x0302
-#define USB_DEVICE_ID_GTCO_303         0x0303
-#define USB_DEVICE_ID_GTCO_304         0x0304
-#define USB_DEVICE_ID_GTCO_305         0x0305
-#define USB_DEVICE_ID_GTCO_306         0x0306
-#define USB_DEVICE_ID_GTCO_307         0x0307
-#define USB_DEVICE_ID_GTCO_308         0x0308
-#define USB_DEVICE_ID_GTCO_309         0x0309
-#define USB_DEVICE_ID_GTCO_400         0x0400
-#define USB_DEVICE_ID_GTCO_401         0x0401
-#define USB_DEVICE_ID_GTCO_402         0x0402
-#define USB_DEVICE_ID_GTCO_403         0x0403
-#define USB_DEVICE_ID_GTCO_404         0x0404
-#define USB_DEVICE_ID_GTCO_405         0x0405
-#define USB_DEVICE_ID_GTCO_500         0x0500
-#define USB_DEVICE_ID_GTCO_501         0x0501
-#define USB_DEVICE_ID_GTCO_502         0x0502
-#define USB_DEVICE_ID_GTCO_503         0x0503
-#define USB_DEVICE_ID_GTCO_504         0x0504
-#define USB_DEVICE_ID_GTCO_1000                0x1000
-#define USB_DEVICE_ID_GTCO_1001                0x1001
-#define USB_DEVICE_ID_GTCO_1002                0x1002
-#define USB_DEVICE_ID_GTCO_1003                0x1003
-#define USB_DEVICE_ID_GTCO_1004                0x1004
-#define USB_DEVICE_ID_GTCO_1005                0x1005
-#define USB_DEVICE_ID_GTCO_1006                0x1006
-#define USB_DEVICE_ID_GTCO_1007                0x1007
-#define USB_VENDOR_ID_HAPP             0x078b
-#define USB_DEVICE_ID_UGCI_DRIVING     0x0010
-#define USB_DEVICE_ID_UGCI_FLYING      0x0020
-#define USB_DEVICE_ID_UGCI_FIGHTING    0x0030
-
-#define USB_VENDOR_ID_IMATION          0x0718
-#define USB_DEVICE_ID_DISC_STAKKA      0xd000
-
-#define USB_VENDOR_ID_KBGEAR           0x084e
-#define USB_DEVICE_ID_KBGEAR_JAMSTUDIO 0x1001
-
-#define USB_VENDOR_ID_LD               0x0f11
-#define USB_DEVICE_ID_LD_CASSY         0x1000
-#define USB_DEVICE_ID_LD_POCKETCASSY   0x1010
-#define USB_DEVICE_ID_LD_MOBILECASSY   0x1020
-#define USB_DEVICE_ID_LD_JWM           0x1080
-#define USB_DEVICE_ID_LD_DMMP          0x1081
-#define USB_DEVICE_ID_LD_UMIP          0x1090
-#define USB_DEVICE_ID_LD_XRAY1         0x1100
-#define USB_DEVICE_ID_LD_XRAY2         0x1101
-#define USB_DEVICE_ID_LD_VIDEOCOM      0x1200
-#define USB_DEVICE_ID_LD_COM3LAB       0x2000
-#define USB_DEVICE_ID_LD_TELEPORT      0x2010
-#define USB_DEVICE_ID_LD_NETWORKANALYSER 0x2020
-#define USB_DEVICE_ID_LD_POWERCONTROL  0x2030
-#define USB_DEVICE_ID_LD_MACHINETEST   0x2040
-
-#define USB_VENDOR_ID_LOGITECH         0x046d
-#define USB_DEVICE_ID_LOGITECH_LX3     0xc044
-#define USB_DEVICE_ID_LOGITECH_V150    0xc047
-#define USB_DEVICE_ID_LOGITECH_RECEIVER        0xc101
-#define USB_DEVICE_ID_LOGITECH_HARMONY  0xc110
-#define USB_DEVICE_ID_LOGITECH_HARMONY_2 0xc111
-#define USB_DEVICE_ID_LOGITECH_HARMONY_3 0xc112
-#define USB_DEVICE_ID_LOGITECH_HARMONY_4 0xc113
-#define USB_DEVICE_ID_LOGITECH_HARMONY_5 0xc114
-#define USB_DEVICE_ID_LOGITECH_HARMONY_6 0xc115
-#define USB_DEVICE_ID_LOGITECH_HARMONY_7 0xc116
-#define USB_DEVICE_ID_LOGITECH_HARMONY_8 0xc117
-#define USB_DEVICE_ID_LOGITECH_HARMONY_9 0xc118
-#define USB_DEVICE_ID_LOGITECH_HARMONY_10 0xc119
-#define USB_DEVICE_ID_LOGITECH_HARMONY_11 0xc11a
-#define USB_DEVICE_ID_LOGITECH_HARMONY_12 0xc11b
-#define USB_DEVICE_ID_LOGITECH_HARMONY_13 0xc11c
-#define USB_DEVICE_ID_LOGITECH_HARMONY_14 0xc11d
-#define USB_DEVICE_ID_LOGITECH_HARMONY_15 0xc11e
-#define USB_DEVICE_ID_LOGITECH_HARMONY_16 0xc11f
-#define USB_DEVICE_ID_LOGITECH_HARMONY_17 0xc120
-#define USB_DEVICE_ID_LOGITECH_HARMONY_18 0xc121
-#define USB_DEVICE_ID_LOGITECH_HARMONY_19 0xc122
-#define USB_DEVICE_ID_LOGITECH_HARMONY_20 0xc123
-#define USB_DEVICE_ID_LOGITECH_HARMONY_21 0xc124
-#define USB_DEVICE_ID_LOGITECH_HARMONY_22 0xc125
-#define USB_DEVICE_ID_LOGITECH_HARMONY_23 0xc126
-#define USB_DEVICE_ID_LOGITECH_HARMONY_24 0xc127
-#define USB_DEVICE_ID_LOGITECH_HARMONY_25 0xc128
-#define USB_DEVICE_ID_LOGITECH_HARMONY_26 0xc129
-#define USB_DEVICE_ID_LOGITECH_HARMONY_27 0xc12a
-#define USB_DEVICE_ID_LOGITECH_HARMONY_28 0xc12b
-#define USB_DEVICE_ID_LOGITECH_HARMONY_29 0xc12c
-#define USB_DEVICE_ID_LOGITECH_HARMONY_30 0xc12d
-#define USB_DEVICE_ID_LOGITECH_HARMONY_31 0xc12e
-#define USB_DEVICE_ID_LOGITECH_HARMONY_32 0xc12f
-#define USB_DEVICE_ID_LOGITECH_HARMONY_33 0xc130
-#define USB_DEVICE_ID_LOGITECH_HARMONY_34 0xc131
-#define USB_DEVICE_ID_LOGITECH_HARMONY_35 0xc132
-#define USB_DEVICE_ID_LOGITECH_HARMONY_36 0xc133
-#define USB_DEVICE_ID_LOGITECH_HARMONY_37 0xc134
-#define USB_DEVICE_ID_LOGITECH_HARMONY_38 0xc135
-#define USB_DEVICE_ID_LOGITECH_HARMONY_39 0xc136
-#define USB_DEVICE_ID_LOGITECH_HARMONY_40 0xc137
-#define USB_DEVICE_ID_LOGITECH_HARMONY_41 0xc138
-#define USB_DEVICE_ID_LOGITECH_HARMONY_42 0xc139
-#define USB_DEVICE_ID_LOGITECH_HARMONY_43 0xc13a
-#define USB_DEVICE_ID_LOGITECH_HARMONY_44 0xc13b
-#define USB_DEVICE_ID_LOGITECH_HARMONY_45 0xc13c
-#define USB_DEVICE_ID_LOGITECH_HARMONY_46 0xc13d
-#define USB_DEVICE_ID_LOGITECH_HARMONY_47 0xc13e
-#define USB_DEVICE_ID_LOGITECH_HARMONY_48 0xc13f
-#define USB_DEVICE_ID_LOGITECH_HARMONY_49 0xc140
-#define USB_DEVICE_ID_LOGITECH_HARMONY_50 0xc141
-#define USB_DEVICE_ID_LOGITECH_HARMONY_51 0xc142
-#define USB_DEVICE_ID_LOGITECH_HARMONY_52 0xc143
-#define USB_DEVICE_ID_LOGITECH_HARMONY_53 0xc144
-#define USB_DEVICE_ID_LOGITECH_HARMONY_54 0xc145
-#define USB_DEVICE_ID_LOGITECH_HARMONY_55 0xc146
-#define USB_DEVICE_ID_LOGITECH_HARMONY_56 0xc147
-#define USB_DEVICE_ID_LOGITECH_HARMONY_57 0xc148
-#define USB_DEVICE_ID_LOGITECH_HARMONY_58 0xc149
-#define USB_DEVICE_ID_LOGITECH_HARMONY_59 0xc14a
-#define USB_DEVICE_ID_LOGITECH_HARMONY_60 0xc14b
-#define USB_DEVICE_ID_LOGITECH_HARMONY_61 0xc14c
-#define USB_DEVICE_ID_LOGITECH_HARMONY_62 0xc14d
-#define USB_DEVICE_ID_LOGITECH_HARMONY_63 0xc14e
-#define USB_DEVICE_ID_LOGITECH_HARMONY_64 0xc14f
-#define USB_DEVICE_ID_LOGITECH_EXTREME_3D      0xc215
-#define USB_DEVICE_ID_LOGITECH_WHEEL   0xc294
-#define USB_DEVICE_ID_LOGITECH_ELITE_KBD       0xc30a
-#define USB_DEVICE_ID_LOGITECH_KBD     0xc311
-#define USB_DEVICE_ID_S510_RECEIVER    0xc50c
-#define USB_DEVICE_ID_S510_RECEIVER_2  0xc517
-#define USB_DEVICE_ID_LOGITECH_CORDLESS_DESKTOP_LX500  0xc512
-#define USB_DEVICE_ID_MX3000_RECEIVER  0xc513
-#define USB_DEVICE_ID_DINOVO_DESKTOP   0xc704
-#define USB_DEVICE_ID_DINOVO_EDGE      0xc714
-#define USB_DEVICE_ID_DINOVO_MINI      0xc71f
-
-#define USB_VENDOR_ID_MCC              0x09db
-#define USB_DEVICE_ID_MCC_PMD1024LS    0x0076
-#define USB_DEVICE_ID_MCC_PMD1208LS    0x007a
-
-#define USB_VENDOR_ID_MGE              0x0463
-#define USB_DEVICE_ID_MGE_UPS          0xffff
-#define USB_DEVICE_ID_MGE_UPS1         0x0001
-
-#define USB_VENDOR_ID_MICROCHIP                0x04d8
-#define USB_DEVICE_ID_PICKIT1          0x0032
-#define USB_DEVICE_ID_PICKIT2          0x0033
-
-#define USB_VENDOR_ID_MICROSOFT                0x045e
-#define USB_DEVICE_ID_SIDEWINDER_GV    0x003b
-#define USB_DEVICE_ID_WIRELESS_OPTICAL_DESKTOP_3_0 0x009d
-#define USB_DEVICE_ID_DESKTOP_RECV_1028 0x00f9
-#define USB_DEVICE_ID_MS_NE4K          0x00db
-#define USB_DEVICE_ID_MS_LK6K          0x00f9
-
-#define USB_VENDOR_ID_MONTEREY         0x0566
-#define USB_DEVICE_ID_GENIUS_KB29E     0x3004
-
-#define USB_VENDOR_ID_NCR              0x0404
-#define USB_DEVICE_ID_NCR_FIRST                0x0300
-#define USB_DEVICE_ID_NCR_LAST         0x03ff
-
-#define USB_VENDOR_ID_NATIONAL_SEMICONDUCTOR 0x0400
-#define USB_DEVICE_ID_N_S_HARMONY       0xc359
-
-#define USB_VENDOR_ID_NATSU             0x08b7
-#define USB_DEVICE_ID_NATSU_GAMEPAD     0x0001
-
-#define USB_VENDOR_ID_NEC              0x073e
-#define USB_DEVICE_ID_NEC_USB_GAME_PAD 0x0301
-
-#define USB_VENDOR_ID_ONTRAK           0x0a07
-#define USB_DEVICE_ID_ONTRAK_ADU100    0x0064
-
-#define USB_VENDOR_ID_PANJIT           0x134c
-
-#define USB_VENDOR_ID_PANTHERLORD      0x0810
-#define USB_DEVICE_ID_PANTHERLORD_TWIN_USB_JOYSTICK    0x0001
-
-#define USB_VENDOR_ID_PETALYNX         0x18b1
-#define USB_DEVICE_ID_PETALYNX_MAXTER_REMOTE   0x0037
-
-#define USB_VENDOR_ID_PLAYDOTCOM       0x0b43
-#define USB_DEVICE_ID_PLAYDOTCOM_EMS_USBII     0x0003
-
-#define USB_VENDOR_ID_SAITEK           0x06a3
-#define USB_DEVICE_ID_SAITEK_RUMBLEPAD 0xff17
-
-#define USB_VENDOR_ID_SAMSUNG          0x0419
-#define USB_DEVICE_ID_SAMSUNG_IR_REMOTE        0x0001
-
-#define USB_VENDOR_ID_SONY                     0x054c
-#define USB_DEVICE_ID_SONY_PS3_CONTROLLER      0x0268
-
-#define USB_VENDOR_ID_SOUNDGRAPH       0x15c2
-#define USB_DEVICE_ID_SOUNDGRAPH_IMON_LCD      0x0038
-
-#define USB_VENDOR_ID_SUN              0x0430
-#define USB_DEVICE_ID_RARITAN_KVM_DONGLE       0xcdab
-
-#define USB_VENDOR_ID_SUNPLUS          0x04fc
-#define USB_DEVICE_ID_SUNPLUS_WDESKTOP 0x05d8
-
-#define USB_VENDOR_ID_TOPMAX           0x0663
-#define USB_DEVICE_ID_TOPMAX_COBRAPAD  0x0103
-
-#define USB_VENDOR_ID_TURBOX           0x062a
-#define USB_DEVICE_ID_TURBOX_KEYBOARD  0x0201
-
-#define USB_VENDOR_ID_VERNIER          0x08f7
-#define USB_DEVICE_ID_VERNIER_LABPRO   0x0001
-#define USB_DEVICE_ID_VERNIER_GOTEMP   0x0002
-#define USB_DEVICE_ID_VERNIER_SKIP     0x0003
-#define USB_DEVICE_ID_VERNIER_CYCLOPS  0x0004
-#define USB_DEVICE_ID_VERNIER_LCSPEC   0x0006
-
-#define USB_VENDOR_ID_WACOM            0x056a
-
-#define USB_VENDOR_ID_WISEGROUP                0x0925
-#define USB_DEVICE_ID_1_PHIDGETSERVO_20        0x8101
-#define USB_DEVICE_ID_4_PHIDGETSERVO_20        0x8104
-#define USB_DEVICE_ID_8_8_4_IF_KIT     0x8201
-#define USB_DEVICE_ID_QUAD_USB_JOYPAD  0x8800
-#define USB_DEVICE_ID_DUAL_USB_JOYPAD  0x8866
-
-#define USB_VENDOR_ID_WISEGROUP_LTD    0x6677
-#define USB_DEVICE_ID_SMARTJOY_DUAL_PLUS 0x8802
-
-#define USB_VENDOR_ID_YEALINK          0x6993
-#define USB_DEVICE_ID_YEALINK_P1K_P4K_B2K      0xb001
-
-#define USB_VENDOR_ID_KYE              0x0458
-#define USB_DEVICE_ID_KYE_GPEN_560     0x5003
+#include "../hid-ids.h"
 
 /*
  * Alphabetically sorted blacklist by quirk type.
@@ -433,18 +28,10 @@ static const struct hid_blacklist {
        __u16 idProduct;
        __u32 quirks;
 } hid_blacklist[] = {
-
-       { USB_VENDOR_ID_A4TECH, USB_DEVICE_ID_A4TECH_WCP32PU, HID_QUIRK_2WHEEL_MOUSE_HACK_7 },
-       { USB_VENDOR_ID_A4TECH, USB_DEVICE_ID_A4TECH_X5_005D, HID_QUIRK_2WHEEL_MOUSE_HACK_B8 },
-       { USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_MOUSE, HID_QUIRK_2WHEEL_MOUSE_HACK_5 },
-
-       { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RECEIVER, HID_QUIRK_BAD_RELATIVE_KEYS },
-
        { USB_VENDOR_ID_AASHIMA, USB_DEVICE_ID_AASHIMA_GAMEPAD, HID_QUIRK_BADPAD },
        { USB_VENDOR_ID_AASHIMA, USB_DEVICE_ID_AASHIMA_PREDATOR, HID_QUIRK_BADPAD },
        { USB_VENDOR_ID_ALPS, USB_DEVICE_ID_IBM_GAMEPAD, HID_QUIRK_BADPAD },
        { USB_VENDOR_ID_CHIC, USB_DEVICE_ID_CHIC_GAMEPAD, HID_QUIRK_BADPAD },
-       { USB_VENDOR_ID_GAMERON, USB_DEVICE_ID_GAMERON_DUAL_PSX_ADAPTOR, HID_QUIRK_MULTI_INPUT },
        { USB_VENDOR_ID_HAPP, USB_DEVICE_ID_UGCI_DRIVING, HID_QUIRK_BADPAD | HID_QUIRK_MULTI_INPUT },
        { USB_VENDOR_ID_HAPP, USB_DEVICE_ID_UGCI_FLYING, HID_QUIRK_BADPAD | HID_QUIRK_MULTI_INPUT },
        { USB_VENDOR_ID_HAPP, USB_DEVICE_ID_UGCI_FIGHTING, HID_QUIRK_BADPAD | HID_QUIRK_MULTI_INPUT },
@@ -453,169 +40,11 @@ static const struct hid_blacklist {
        { USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_RUMBLEPAD, HID_QUIRK_BADPAD },
        { USB_VENDOR_ID_TOPMAX, USB_DEVICE_ID_TOPMAX_COBRAPAD, HID_QUIRK_BADPAD },
 
-       { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_DINOVO_DESKTOP, HID_QUIRK_DUPLICATE_USAGES },
-       { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_DINOVO_EDGE, HID_QUIRK_DUPLICATE_USAGES },
-       { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_DINOVO_MINI, HID_QUIRK_DUPLICATE_USAGES },
-
        { USB_VENDOR_ID_AFATECH, USB_DEVICE_ID_AFATECH_AF9016, HID_QUIRK_FULLSPEED_INTERVAL },
 
-       { USB_VENDOR_ID_BELKIN, USB_DEVICE_ID_FLIP_KVM, HID_QUIRK_HIDDEV },
-       { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL4, HID_QUIRK_HIDDEV | HID_QUIRK_IGNORE_HIDINPUT },
-       { USB_VENDOR_ID_SAMSUNG, USB_DEVICE_ID_SAMSUNG_IR_REMOTE, HID_QUIRK_HIDDEV | HID_QUIRK_IGNORE_HIDINPUT },
-       { USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_SIDEWINDER_GV, HID_QUIRK_HIDINPUT },
-
-       { USB_VENDOR_ID_EZKEY, USB_DEVICE_ID_BTC_8193, HID_QUIRK_HWHEEL_WHEEL_INVERT },
-
-       { USB_VENDOR_ID_ADS_TECH, USB_DEVICE_ID_ADS_TECH_RADIO_SI470X, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_01, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_10, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_20, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_21, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_22, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_23, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_24, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_AIRCABLE, USB_DEVICE_ID_AIRCABLE1, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_ALCOR, USB_DEVICE_ID_ALCOR_USBRS232, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_ASUS, USB_DEVICE_ID_ASUS_LCM, HID_QUIRK_IGNORE},
-       { USB_VENDOR_ID_BERKSHIRE, USB_DEVICE_ID_BERKSHIRE_PCWD, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_CIDC, 0x0103, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_CYGNAL, USB_DEVICE_ID_CYGNAL_RADIO_SI470X, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_CMEDIA, USB_DEVICE_ID_CM109, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_HIDCOM, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_ULTRAMOUSE, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_DELORME, USB_DEVICE_ID_DELORME_EARTHMATE, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_DELORME, USB_DEVICE_ID_DELORME_EM_LT20, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_ESSENTIAL_REALITY, USB_DEVICE_ID_ESSENTIAL_REALITY_P5, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_GENERAL_TOUCH, 0x0001, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_GENERAL_TOUCH, 0x0002, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_GENERAL_TOUCH, 0x0003, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_GENERAL_TOUCH, 0x0004, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_GLAB, USB_DEVICE_ID_4_PHIDGETSERVO_30, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_GLAB, USB_DEVICE_ID_1_PHIDGETSERVO_30, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_GLAB, USB_DEVICE_ID_0_0_4_IF_KIT, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_GLAB, USB_DEVICE_ID_0_16_16_IF_KIT, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_GLAB, USB_DEVICE_ID_8_8_8_IF_KIT, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_GLAB, USB_DEVICE_ID_0_8_7_IF_KIT, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_GLAB, USB_DEVICE_ID_0_8_8_IF_KIT, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_GLAB, USB_DEVICE_ID_PHIDGET_MOTORCONTROL, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_GOTOP, USB_DEVICE_ID_SUPER_Q2, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_GOTOP, USB_DEVICE_ID_GOGOPEN, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_GOTOP, USB_DEVICE_ID_PENPOWER, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_GRETAGMACBETH, USB_DEVICE_ID_GRETAGMACBETH_HUEY, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_GRIFFIN, USB_DEVICE_ID_POWERMATE, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_GRIFFIN, USB_DEVICE_ID_SOUNDKNOB, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_90, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_100, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_101, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_103, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_104, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_105, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_106, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_107, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_108, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_200, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_201, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_202, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_203, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_204, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_205, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_206, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_207, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_300, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_301, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_302, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_303, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_304, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_305, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_306, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_307, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_308, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_309, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_400, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_401, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_402, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_403, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_404, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_405, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_500, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_501, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_502, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_503, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_504, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_1000, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_1001, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_1002, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_1003, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_1004, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_1005, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_1006, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_1007, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_IMATION, USB_DEVICE_ID_DISC_STAKKA, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_KBGEAR, USB_DEVICE_ID_KBGEAR_JAMSTUDIO, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_CASSY, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_POCKETCASSY, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_MOBILECASSY, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_JWM, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_DMMP, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_UMIP, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_XRAY1, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_XRAY2, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_VIDEOCOM, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_COM3LAB, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_TELEPORT, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_NETWORKANALYSER, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_POWERCONTROL, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_MACHINETEST, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_MCC, USB_DEVICE_ID_MCC_PMD1024LS, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_MCC, USB_DEVICE_ID_MCC_PMD1208LS, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_MGE, USB_DEVICE_ID_MGE_UPS, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_MGE, USB_DEVICE_ID_MGE_UPS1, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 20, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 30, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 100, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 108, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 118, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 200, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 300, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 400, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 500, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_PANJIT, 0x0001, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_PANJIT, 0x0002, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_PANJIT, 0x0003, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_PANJIT, 0x0004, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_SOUNDGRAPH, USB_DEVICE_ID_SOUNDGRAPH_IMON_LCD, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_LABPRO, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_GOTEMP, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_SKIP, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_CYCLOPS, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_LCSPEC, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_4_PHIDGETSERVO_20, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_1_PHIDGETSERVO_20, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_8_8_4_IF_KIT, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_YEALINK, USB_DEVICE_ID_YEALINK_P1K_P4K_B2K, HID_QUIRK_IGNORE },
-
-       { USB_VENDOR_ID_ACECAD, USB_DEVICE_ID_ACECAD_FLAIR, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_ACECAD, USB_DEVICE_ID_ACECAD_302, HID_QUIRK_IGNORE },
-
-       { USB_VENDOR_ID_MICROCHIP, USB_DEVICE_ID_PICKIT1, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_MICROCHIP, USB_DEVICE_ID_PICKIT2, HID_QUIRK_IGNORE },
-
-       { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_ELITE_KBD, HID_QUIRK_LOGITECH_IGNORE_DOUBLED_WHEEL | HID_QUIRK_LOGITECH_EXPANDED_KEYMAP },
-       { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_CORDLESS_DESKTOP_LX500, HID_QUIRK_LOGITECH_IGNORE_DOUBLED_WHEEL | HID_QUIRK_LOGITECH_EXPANDED_KEYMAP },
-       { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_LX3, HID_QUIRK_INVERT_HWHEEL },
-       { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_V150, HID_QUIRK_INVERT_HWHEEL },
-
-       { USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_NE4K, HID_QUIRK_MICROSOFT_KEYS },
-       { USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_LK6K, HID_QUIRK_MICROSOFT_KEYS },
-
-       { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MIGHTYMOUSE, HID_QUIRK_MIGHTYMOUSE | HID_QUIRK_INVERT_HWHEEL },
-
        { USB_VENDOR_ID_PANTHERLORD, USB_DEVICE_ID_PANTHERLORD_TWIN_USB_JOYSTICK, HID_QUIRK_MULTI_INPUT | HID_QUIRK_SKIP_OUTPUT_REPORTS },
        { USB_VENDOR_ID_PLAYDOTCOM, USB_DEVICE_ID_PLAYDOTCOM_EMS_USBII, HID_QUIRK_MULTI_INPUT },
 
-       { USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS3_CONTROLLER, HID_QUIRK_SONY_PS3_CONTROLLER | HID_QUIRK_HIDDEV },
-
        { USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_UC100KM, HID_QUIRK_NOGET },
        { USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_CS124U, HID_QUIRK_NOGET },
        { USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_2PORTKVM, HID_QUIRK_NOGET },
@@ -623,144 +52,13 @@ static const struct hid_blacklist {
        { USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_4PORTKVMC, HID_QUIRK_NOGET },
        { USB_VENDOR_ID_DMI, USB_DEVICE_ID_DMI_ENC, HID_QUIRK_NOGET },
        { USB_VENDOR_ID_ELO, USB_DEVICE_ID_ELO_TS2700, HID_QUIRK_NOGET },
-       { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_EXTREME_3D, HID_QUIRK_NOGET },
-       { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WHEEL, HID_QUIRK_NOGET },
-       { USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_WIRELESS_OPTICAL_DESKTOP_3_0, HID_QUIRK_NOGET },
-       { USB_VENDOR_ID_PETALYNX, USB_DEVICE_ID_PETALYNX_MAXTER_REMOTE, HID_QUIRK_NOGET },
        { USB_VENDOR_ID_SUN, USB_DEVICE_ID_RARITAN_KVM_DONGLE, HID_QUIRK_NOGET },
        { USB_VENDOR_ID_TURBOX, USB_DEVICE_ID_TURBOX_KEYBOARD, HID_QUIRK_NOGET },
        { USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_DUAL_USB_JOYPAD, HID_QUIRK_NOGET | HID_QUIRK_MULTI_INPUT | HID_QUIRK_SKIP_OUTPUT_REPORTS },
        { USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_QUAD_USB_JOYPAD, HID_QUIRK_NOGET | HID_QUIRK_MULTI_INPUT },
 
        { USB_VENDOR_ID_WISEGROUP_LTD, USB_DEVICE_ID_SMARTJOY_DUAL_PLUS, HID_QUIRK_NOGET | HID_QUIRK_MULTI_INPUT },
-
-       { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_FOUNTAIN_ANSI, HID_QUIRK_APPLE_NUMLOCK_EMULATION | HID_QUIRK_APPLE_HAS_FN | HID_QUIRK_IGNORE_MOUSE },
-       { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_FOUNTAIN_ISO, HID_QUIRK_APPLE_NUMLOCK_EMULATION | HID_QUIRK_APPLE_HAS_FN | HID_QUIRK_IGNORE_MOUSE },
-       { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER_ANSI, HID_QUIRK_APPLE_NUMLOCK_EMULATION | HID_QUIRK_APPLE_HAS_FN | HID_QUIRK_IGNORE_MOUSE },
-       { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER_ISO, HID_QUIRK_APPLE_NUMLOCK_EMULATION | HID_QUIRK_APPLE_HAS_FN | HID_QUIRK_IGNORE_MOUSE | HID_QUIRK_APPLE_ISO_KEYBOARD},
-       { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER_JIS, HID_QUIRK_APPLE_NUMLOCK_EMULATION | HID_QUIRK_APPLE_HAS_FN | HID_QUIRK_IGNORE_MOUSE },
-       { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER3_ANSI, HID_QUIRK_APPLE_NUMLOCK_EMULATION | HID_QUIRK_APPLE_HAS_FN | HID_QUIRK_IGNORE_MOUSE },
-       { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER3_ISO, HID_QUIRK_APPLE_NUMLOCK_EMULATION | HID_QUIRK_APPLE_HAS_FN | HID_QUIRK_IGNORE_MOUSE | HID_QUIRK_APPLE_ISO_KEYBOARD},
-       { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER3_JIS, HID_QUIRK_APPLE_NUMLOCK_EMULATION | HID_QUIRK_APPLE_HAS_FN | HID_QUIRK_IGNORE_MOUSE },
-       { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_ANSI, HID_QUIRK_APPLE_NUMLOCK_EMULATION | HID_QUIRK_APPLE_HAS_FN | HID_QUIRK_IGNORE_MOUSE },
-       { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_ISO, HID_QUIRK_APPLE_NUMLOCK_EMULATION | HID_QUIRK_APPLE_HAS_FN | HID_QUIRK_IGNORE_MOUSE | HID_QUIRK_APPLE_ISO_KEYBOARD},
-       { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_JIS, HID_QUIRK_APPLE_NUMLOCK_EMULATION | HID_QUIRK_APPLE_HAS_FN | HID_QUIRK_IGNORE_MOUSE },
-       { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_ANSI, HID_QUIRK_APPLE_HAS_FN },
-       { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_ISO, HID_QUIRK_APPLE_HAS_FN | HID_QUIRK_APPLE_ISO_KEYBOARD },
-       { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_JIS, HID_QUIRK_APPLE_HAS_FN },
-       { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_HF_ANSI, HID_QUIRK_APPLE_NUMLOCK_EMULATION | HID_QUIRK_APPLE_HAS_FN | HID_QUIRK_IGNORE_MOUSE },
-       { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_HF_ISO, HID_QUIRK_APPLE_NUMLOCK_EMULATION | HID_QUIRK_APPLE_HAS_FN | HID_QUIRK_IGNORE_MOUSE },
-       { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_HF_JIS, HID_QUIRK_APPLE_NUMLOCK_EMULATION | HID_QUIRK_APPLE_HAS_FN | HID_QUIRK_IGNORE_MOUSE },
-       { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_ANSI, HID_QUIRK_APPLE_NUMLOCK_EMULATION | HID_QUIRK_APPLE_HAS_FN },
-       { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_ISO, HID_QUIRK_APPLE_NUMLOCK_EMULATION | HID_QUIRK_APPLE_HAS_FN | HID_QUIRK_APPLE_ISO_KEYBOARD },
-       { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_JIS, HID_QUIRK_APPLE_NUMLOCK_EMULATION | HID_QUIRK_APPLE_HAS_FN },
-       { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING_ANSI, HID_QUIRK_APPLE_HAS_FN | HID_QUIRK_IGNORE_MOUSE },
-       { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING_ISO, HID_QUIRK_APPLE_HAS_FN | HID_QUIRK_APPLE_ISO_KEYBOARD | HID_QUIRK_IGNORE_MOUSE},
-       { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING_JIS, HID_QUIRK_APPLE_HAS_FN | HID_QUIRK_IGNORE_MOUSE},
-       { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING2_ANSI, HID_QUIRK_APPLE_HAS_FN | HID_QUIRK_IGNORE_MOUSE},
-       { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING2_ISO, HID_QUIRK_APPLE_HAS_FN | HID_QUIRK_APPLE_ISO_KEYBOARD | HID_QUIRK_IGNORE_MOUSE },
-       { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING2_JIS, HID_QUIRK_APPLE_HAS_FN  | HID_QUIRK_IGNORE_MOUSE },
-       { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_FOUNTAIN_TP_ONLY, HID_QUIRK_APPLE_NUMLOCK_EMULATION | HID_QUIRK_APPLE_HAS_FN | HID_QUIRK_IGNORE_MOUSE },
-       { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER1_TP_ONLY, HID_QUIRK_APPLE_NUMLOCK_EMULATION | HID_QUIRK_APPLE_HAS_FN | HID_QUIRK_IGNORE_MOUSE },
-
-       { USB_VENDOR_ID_DELL, USB_DEVICE_ID_DELL_W7658, HID_QUIRK_RESET_LEDS },
-       { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_KBD, HID_QUIRK_RESET_LEDS },
-       { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_2, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_3, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_4, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_5, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_6, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_7, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_8, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_9, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_10, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_11, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_12, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_13, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_14, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_15, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_16, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_17, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_18, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_19, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_20, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_21, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_22, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_23, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_24, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_25, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_26, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_27, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_28, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_29, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_30, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_31, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_32, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_33, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_34, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_35, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_36, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_37, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_38, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_39, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_40, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_41, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_42, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_43, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_44, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_45, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_46, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_47, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_48, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_49, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_50, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_51, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_52, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_53, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_54, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_55, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_56, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_57, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_58, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_59, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_60, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_61, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_62, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_63, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_64, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_NATIONAL_SEMICONDUCTOR, USB_DEVICE_ID_N_S_HARMONY, HID_QUIRK_IGNORE },
-       { USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_GPEN_560, HID_QUIRK_IGNORE },
-
-       { 0, 0 }
-};
-
-/* Quirks for devices which require report descriptor fixup go here */
-static const struct hid_rdesc_blacklist {
-       __u16 idVendor;
-       __u16 idProduct;
-       __u32 quirks;
-} hid_rdesc_blacklist[] = {
-
-       { USB_VENDOR_ID_CHERRY, USB_DEVICE_ID_CHERRY_CYMOTION, HID_QUIRK_RDESC_CYMOTION },
-
-       { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_MX3000_RECEIVER, HID_QUIRK_RDESC_LOGITECH },
-       { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_S510_RECEIVER, HID_QUIRK_RDESC_LOGITECH },
-       { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_S510_RECEIVER_2, HID_QUIRK_RDESC_LOGITECH },
-       { USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_DESKTOP_RECV_1028, HID_QUIRK_RDESC_MICROSOFT_RECV_1028 },
-
-       { USB_VENDOR_ID_MONTEREY, USB_DEVICE_ID_GENIUS_KB29E, HID_QUIRK_RDESC_BUTTON_CONSUMER },
-
-       { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_JIS, HID_QUIRK_RDESC_MACBOOK_JIS },
-
-       { USB_VENDOR_ID_PETALYNX, USB_DEVICE_ID_PETALYNX_MAXTER_REMOTE, HID_QUIRK_RDESC_PETALYNX },
-
-       { USB_VENDOR_ID_SAMSUNG, USB_DEVICE_ID_SAMSUNG_IR_REMOTE, HID_QUIRK_RDESC_SAMSUNG_REMOTE },
-
-       { USB_VENDOR_ID_SUNPLUS, USB_DEVICE_ID_SUNPLUS_WDESKTOP, HID_QUIRK_RDESC_SUNPLUS_WDESKTOP },
-
-       { USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_BARCODE_1, HID_QUIRK_RDESC_SWAPPED_MIN_MAX },
-       { USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_BARCODE_2, HID_QUIRK_RDESC_SWAPPED_MIN_MAX },
+       { USB_VENDOR_ID_WISEGROUP_LTD2, USB_DEVICE_ID_SMARTJOY_DUAL_PLUS, HID_QUIRK_NOGET | HID_QUIRK_MULTI_INPUT },
 
        { 0, 0 }
 };
@@ -974,16 +272,6 @@ u32 usbhid_lookup_quirk(const u16 idVendor, const u16 idProduct)
        u32 quirks = 0;
        const struct hid_blacklist *bl_entry = NULL;
 
-       /* Ignore all Wacom devices */
-       if (idVendor == USB_VENDOR_ID_WACOM)
-               return HID_QUIRK_IGNORE;
-
-       /* ignore all Code Mercenaries IOWarrior devices */
-       if (idVendor == USB_VENDOR_ID_CODEMERCS)
-               if (idProduct >= USB_DEVICE_ID_CODEMERCS_IOW_FIRST &&
-                               idProduct <= USB_DEVICE_ID_CODEMERCS_IOW_LAST)
-                       return HID_QUIRK_IGNORE;
-
        /* NCR devices must not be queried for reports */
        if (idVendor == USB_VENDOR_ID_NCR &&
                        idProduct >= USB_DEVICE_ID_NCR_FIRST &&
@@ -1002,221 +290,3 @@ u32 usbhid_lookup_quirk(const u16 idVendor, const u16 idProduct)
 }
 
 EXPORT_SYMBOL_GPL(usbhid_lookup_quirk);
-
-/*
- * Cherry Cymotion keyboard have an invalid HID report descriptor,
- * that needs fixing before we can parse it.
- */
-static void usbhid_fixup_cymotion_descriptor(char *rdesc, int rsize)
-{
-       if (rsize >= 17 && rdesc[11] == 0x3c && rdesc[12] == 0x02) {
-               printk(KERN_INFO "Fixing up Cherry Cymotion report descriptor\n");
-               rdesc[11] = rdesc[16] = 0xff;
-               rdesc[12] = rdesc[17] = 0x03;
-       }
-}
-
-
-/*
- * Certain Logitech keyboards send in report #3 keys which are far
- * above the logical maximum described in descriptor. This extends
- * the original value of 0x28c of logical maximum to 0x104d
- */
-static void usbhid_fixup_logitech_descriptor(unsigned char *rdesc, int rsize)
-{
-       if (rsize >= 90 && rdesc[83] == 0x26
-                       && rdesc[84] == 0x8c
-                       && rdesc[85] == 0x02) {
-               printk(KERN_INFO "Fixing up Logitech keyboard report descriptor\n");
-               rdesc[84] = rdesc[89] = 0x4d;
-               rdesc[85] = rdesc[90] = 0x10;
-       }
-}
-
-static void usbhid_fixup_sunplus_wdesktop(unsigned char *rdesc, int rsize)
-{
-       if (rsize >= 107 && rdesc[104] == 0x26
-                        && rdesc[105] == 0x80
-                        && rdesc[106] == 0x03) {
-               printk(KERN_INFO "Fixing up Sunplus Wireless Desktop report descriptor\n");
-               rdesc[105] = rdesc[110] = 0x03;
-               rdesc[106] = rdesc[111] = 0x21;
-       }
-}
-
-/*
- * Samsung IrDA remote controller (reports as Cypress USB Mouse).
- *
- * Vendor specific report #4 has a size of 48 bit,
- * and therefore is not accepted when inspecting the descriptors.
- * As a workaround we reinterpret the report as:
- *   Variable type, count 6, size 8 bit, log. maximum 255
- * The burden to reconstruct the data is moved into user space.
- */
-static void usbhid_fixup_samsung_irda_descriptor(unsigned char *rdesc,
-                                                 int rsize)
-{
-       if (rsize >= 182 && rdesc[175] == 0x25
-                        && rdesc[176] == 0x40
-                        && rdesc[177] == 0x75
-                        && rdesc[178] == 0x30
-                        && rdesc[179] == 0x95
-                        && rdesc[180] == 0x01
-                        && rdesc[182] == 0x40) {
-               printk(KERN_INFO "Fixing up Samsung IrDA report descriptor\n");
-               rdesc[176] = 0xff;
-               rdesc[178] = 0x08;
-               rdesc[180] = 0x06;
-               rdesc[182] = 0x42;
-       }
-}
-
-/* Petalynx Maxter Remote has maximum for consumer page set too low */
-static void usbhid_fixup_petalynx_descriptor(unsigned char *rdesc, int rsize)
-{
-       if (rsize >= 60 && rdesc[39] == 0x2a
-                       && rdesc[40] == 0xf5
-                       && rdesc[41] == 0x00
-                       && rdesc[59] == 0x26
-                       && rdesc[60] == 0xf9
-                       && rdesc[61] == 0x00) {
-               printk(KERN_INFO "Fixing up Petalynx Maxter Remote report descriptor\n");
-               rdesc[60] = 0xfa;
-               rdesc[40] = 0xfa;
-       }
-}
-
-/*
- * Some USB barcode readers from cypress have usage min and usage max in
- * the wrong order
- */
-static void usbhid_fixup_cypress_descriptor(unsigned char *rdesc, int rsize)
-{
-       short fixed = 0;
-       int i;
-
-       for (i = 0; i < rsize - 4; i++) {
-               if (rdesc[i] == 0x29 && rdesc [i+2] == 0x19) {
-                       unsigned char tmp;
-
-                       rdesc[i] = 0x19; rdesc[i+2] = 0x29;
-                       tmp = rdesc[i+3];
-                       rdesc[i+3] = rdesc[i+1];
-                       rdesc[i+1] = tmp;
-               }
-       }
-
-       if (fixed)
-               printk(KERN_INFO "Fixing up Cypress report descriptor\n");
-}
-
-/*
- * MacBook JIS keyboard has wrong logical maximum
- */
-static void usbhid_fixup_macbook_descriptor(unsigned char *rdesc, int rsize)
-{
-       if (rsize >= 60 && rdesc[53] == 0x65
-                       && rdesc[59] == 0x65) {
-               printk(KERN_INFO "Fixing up MacBook JIS keyboard report descriptor\n");
-               rdesc[53] = rdesc[59] = 0xe7;
-       }
-}
-
-static void usbhid_fixup_button_consumer_descriptor(unsigned char *rdesc, int rsize)
-{
-       if (rsize >= 30 && rdesc[29] == 0x05
-                       && rdesc[30] == 0x09) {
-               printk(KERN_INFO "Fixing up button/consumer in HID report descriptor\n");
-               rdesc[30] = 0x0c;
-       }
-}
-
-/*
- * Microsoft Wireless Desktop Receiver (Model 1028) has several
- * 'Usage Min/Max' where it ought to have 'Physical Min/Max'
- */
-static void usbhid_fixup_microsoft_descriptor(unsigned char *rdesc, int rsize)
-{
-       if (rsize == 571 && rdesc[284] == 0x19
-                        && rdesc[286] == 0x2a
-                        && rdesc[304] == 0x19
-                        && rdesc[306] == 0x29
-                        && rdesc[352] == 0x1a
-                        && rdesc[355] == 0x2a
-                        && rdesc[557] == 0x19
-                        && rdesc[559] == 0x29) {
-               printk(KERN_INFO "Fixing up Microsoft Wireless Receiver Model 1028 report descriptor\n");
-               rdesc[284] = rdesc[304] = rdesc[557] = 0x35;
-               rdesc[352] = 0x36;
-               rdesc[286] = rdesc[355] = 0x46;
-               rdesc[306] = rdesc[559] = 0x45;
-       }
-}
-
-static void __usbhid_fixup_report_descriptor(__u32 quirks, char *rdesc, unsigned rsize)
-{
-       if ((quirks & HID_QUIRK_RDESC_CYMOTION))
-               usbhid_fixup_cymotion_descriptor(rdesc, rsize);
-
-       if (quirks & HID_QUIRK_RDESC_LOGITECH)
-               usbhid_fixup_logitech_descriptor(rdesc, rsize);
-
-       if (quirks & HID_QUIRK_RDESC_SWAPPED_MIN_MAX)
-               usbhid_fixup_cypress_descriptor(rdesc, rsize);
-
-       if (quirks & HID_QUIRK_RDESC_PETALYNX)
-               usbhid_fixup_petalynx_descriptor(rdesc, rsize);
-
-       if (quirks & HID_QUIRK_RDESC_MACBOOK_JIS)
-               usbhid_fixup_macbook_descriptor(rdesc, rsize);
-
-       if (quirks & HID_QUIRK_RDESC_BUTTON_CONSUMER)
-               usbhid_fixup_button_consumer_descriptor(rdesc, rsize);
-
-       if (quirks & HID_QUIRK_RDESC_SAMSUNG_REMOTE)
-               usbhid_fixup_samsung_irda_descriptor(rdesc, rsize);
-
-       if (quirks & HID_QUIRK_RDESC_MICROSOFT_RECV_1028)
-               usbhid_fixup_microsoft_descriptor(rdesc, rsize);
-
-       if (quirks & HID_QUIRK_RDESC_SUNPLUS_WDESKTOP)
-               usbhid_fixup_sunplus_wdesktop(rdesc, rsize);
-}
-
-/**
- * usbhid_fixup_report_descriptor: check if report descriptor needs fixup
- *
- * Description:
- *     Walks the hid_rdesc_blacklist[] array and checks whether the device
- *     is known to have broken report descriptor that needs to be fixed up
- *     prior to entering the HID parser
- *
- * Returns: nothing
- */
-void usbhid_fixup_report_descriptor(const u16 idVendor, const u16 idProduct,
-                                   char *rdesc, unsigned rsize, char **quirks_param)
-{
-       int n, m;
-       u16 paramVendor, paramProduct;
-       u32 quirks;
-
-       /* static rdesc quirk entries */
-       for (n = 0; hid_rdesc_blacklist[n].idVendor; n++)
-               if (hid_rdesc_blacklist[n].idVendor == idVendor &&
-                               hid_rdesc_blacklist[n].idProduct == idProduct)
-                       __usbhid_fixup_report_descriptor(hid_rdesc_blacklist[n].quirks,
-                                       rdesc, rsize);
-
-       /* runtime rdesc quirk entries handling */
-       for (n = 0; quirks_param[n] && n < MAX_USBHID_BOOT_QUIRKS; n++) {
-               m = sscanf(quirks_param[n], "0x%hx:0x%hx:0x%x",
-                               &paramVendor, &paramProduct, &quirks);
-
-               if (m != 3)
-                       printk(KERN_WARNING
-                               "Could not parse HID quirk module param %s\n",
-                               quirks_param[n]);
-               else if (paramVendor == idVendor && paramProduct == idProduct)
-                       __usbhid_fixup_report_descriptor(quirks, rdesc, rsize);
-       }
-}
index 842e9edb888ed889c27c19fd2ca705c3da38010f..babd65dd46ad4eefa066c4e842288144f2941ab8 100644 (file)
@@ -790,21 +790,23 @@ static struct usb_class_driver hiddev_class = {
 /*
  * This is where hid.c calls us to connect a hid device to the hiddev driver
  */
-int hiddev_connect(struct hid_device *hid)
+int hiddev_connect(struct hid_device *hid, unsigned int force)
 {
        struct hiddev *hiddev;
        struct usbhid_device *usbhid = hid->driver_data;
-       int i;
        int retval;
 
-       for (i = 0; i < hid->maxcollection; i++)
-               if (hid->collection[i].type ==
-                   HID_COLLECTION_APPLICATION &&
-                   !IS_INPUT_APPLICATION(hid->collection[i].usage))
-                       break;
+       if (!force) {
+               unsigned int i;
+               for (i = 0; i < hid->maxcollection; i++)
+                       if (hid->collection[i].type ==
+                           HID_COLLECTION_APPLICATION &&
+                           !IS_INPUT_APPLICATION(hid->collection[i].usage))
+                               break;
 
-       if (i == hid->maxcollection && (hid->quirks & HID_QUIRK_HIDDEV) == 0)
-               return -1;
+               if (i == hid->maxcollection)
+                       return -1;
+       }
 
        if (!(hiddev = kzalloc(sizeof(struct hiddev), GFP_KERNEL)))
                return -1;
index 62d2d7c925bd3d494c6dd4be8a3f6b66c4aa556c..abedb13c623e419b12b8c3cc02cef4a8a977f520 100644 (file)
@@ -67,7 +67,7 @@ struct usbhid_device {
        spinlock_t ctrllock;                                            /* Control fifo spinlock */
 
        struct urb *urbout;                                             /* Output URB */
-       struct hid_report *out[HID_CONTROL_FIFO_SIZE];                  /* Output pipe fifo */
+       struct hid_output_fifo out[HID_CONTROL_FIFO_SIZE];              /* Output pipe fifo */
        unsigned char outhead, outtail;                                 /* Output pipe fifo head & tail */
        char *outbuf;                                                   /* Output buffer */
        dma_addr_t outbuf_dma;                                          /* Output buffer dma */
@@ -82,7 +82,7 @@ struct usbhid_device {
 };
 
 #define        hid_to_usb_dev(hid_dev) \
-       container_of(hid_dev->dev->parent, struct usb_device, dev)
+       container_of(hid_dev->dev.parent->parent, struct usb_device, dev)
 
 #endif
 
index 0caaafe018438db68b33acc4ac0d695c717d4058..b342926dd7fcbc91d697e4c058a5c0889a265d4b 100644 (file)
@@ -105,14 +105,16 @@ static void usb_kbd_irq(struct urb *urb)
                        if (usb_kbd_keycode[kbd->old[i]])
                                input_report_key(kbd->dev, usb_kbd_keycode[kbd->old[i]], 0);
                        else
-                               info("Unknown key (scancode %#x) released.", kbd->old[i]);
+                               dev_info(&urb->dev->dev,
+                                               "Unknown key (scancode %#x) released.\n", kbd->old[i]);
                }
 
                if (kbd->new[i] > 3 && memscan(kbd->old + 2, kbd->new[i], 6) == kbd->old + 8) {
                        if (usb_kbd_keycode[kbd->new[i]])
                                input_report_key(kbd->dev, usb_kbd_keycode[kbd->new[i]], 1);
                        else
-                               info("Unknown key (scancode %#x) pressed.", kbd->new[i]);
+                               dev_info(&urb->dev->dev,
+                                               "Unknown key (scancode %#x) released.\n", kbd->new[i]);
                }
        }
 
@@ -159,7 +161,8 @@ static void usb_kbd_led(struct urb *urb)
        struct usb_kbd *kbd = urb->context;
 
        if (urb->status)
-               warn("led urb status %d received", urb->status);
+               dev_warn(&urb->dev->dev, "led urb status %d received\n",
+                        urb->status);
 
        if (*(kbd->leds) == kbd->newleds)
                return;
@@ -352,7 +355,8 @@ static int __init usb_kbd_init(void)
 {
        int result = usb_register(&usb_kbd_driver);
        if (result == 0)
-               info(DRIVER_VERSION ":" DRIVER_DESC);
+               printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_VERSION ":"
+                               DRIVER_DESC "\n");
        return result;
 }
 
index 35689ef172cc5240fdb4a7882ccaf13fbe3913df..72ab4b2680963ca9721a47d6d5dcd86f5dcc4246 100644 (file)
 #include <linux/usb/input.h>
 #include <linux/hid.h>
 
+/* for apple IDs */
+#ifdef CONFIG_USB_HID_MODULE
+#include "../hid-ids.h"
+#endif
+
 /*
  * Version Information
  */
@@ -240,7 +245,8 @@ static int __init usb_mouse_init(void)
 {
        int retval = usb_register(&usb_mouse_driver);
        if (retval == 0)
-               info(DRIVER_VERSION ":" DRIVER_DESC);
+               printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_VERSION ":"
+                               DRIVER_DESC "\n");
        return retval;
 }
 
index cdb8311e4ef730c503d814de22442917595e9c82..27a5d397f9a127f40b4d9eedbac8f3a205444414 100644 (file)
@@ -175,11 +175,11 @@ static const u8 DME1737_BIT_ALARM_FAN[] = {10, 11, 12, 13, 22, 23};
  * Data structures and manipulation thereof
  * --------------------------------------------------------------------- */
 
-/* For ISA chips, we abuse the i2c_client addr and name fields. We also use
-   the driver field to differentiate between I2C and ISA chips. */
 struct dme1737_data {
-       struct i2c_client client;
+       struct i2c_client *client;      /* for I2C devices only */
        struct device *hwmon_dev;
+       const char *name;
+       unsigned int addr;              /* for ISA devices only */
 
        struct mutex update_lock;
        int valid;                      /* !=0 if following fields are valid */
@@ -512,11 +512,12 @@ static inline int PWM_OFF_TO_REG(int val, int ix, int reg)
  * before calling dme1737_read or dme1737_write.
  * --------------------------------------------------------------------- */
 
-static u8 dme1737_read(struct i2c_client *client, u8 reg)
+static u8 dme1737_read(const struct dme1737_data *data, u8 reg)
 {
+       struct i2c_client *client = data->client;
        s32 val;
 
-       if (client->driver) { /* I2C device */
+       if (client) { /* I2C device */
                val = i2c_smbus_read_byte_data(client, reg);
 
                if (val < 0) {
@@ -525,18 +526,19 @@ static u8 dme1737_read(struct i2c_client *client, u8 reg)
                                 "maintainer.\n", reg);
                }
        } else { /* ISA device */
-               outb(reg, client->addr);
-               val = inb(client->addr + 1);
+               outb(reg, data->addr);
+               val = inb(data->addr + 1);
        }
 
        return val;
 }
 
-static s32 dme1737_write(struct i2c_client *client, u8 reg, u8 val)
+static s32 dme1737_write(const struct dme1737_data *data, u8 reg, u8 val)
 {
+       struct i2c_client *client = data->client;
        s32 res = 0;
 
-       if (client->driver) { /* I2C device */
+       if (client) { /* I2C device */
                res = i2c_smbus_write_byte_data(client, reg, val);
 
                if (res < 0) {
@@ -545,8 +547,8 @@ static s32 dme1737_write(struct i2c_client *client, u8 reg, u8 val)
                                 "maintainer.\n", reg);
                }
        } else { /* ISA device */
-               outb(reg, client->addr);
-               outb(val, client->addr + 1);
+               outb(reg, data->addr);
+               outb(val, data->addr + 1);
        }
 
        return res;
@@ -555,7 +557,6 @@ static s32 dme1737_write(struct i2c_client *client, u8 reg, u8 val)
 static struct dme1737_data *dme1737_update_device(struct device *dev)
 {
        struct dme1737_data *data = dev_get_drvdata(dev);
-       struct i2c_client *client = &data->client;
        int ix;
        u8 lsb[5];
 
@@ -563,7 +564,7 @@ static struct dme1737_data *dme1737_update_device(struct device *dev)
 
        /* Enable a Vbat monitoring cycle every 10 mins */
        if (time_after(jiffies, data->last_vbat + 600 * HZ) || !data->valid) {
-               dme1737_write(client, DME1737_REG_CONFIG, dme1737_read(client,
+               dme1737_write(data, DME1737_REG_CONFIG, dme1737_read(data,
                                                DME1737_REG_CONFIG) | 0x10);
                data->last_vbat = jiffies;
        }
@@ -571,7 +572,7 @@ static struct dme1737_data *dme1737_update_device(struct device *dev)
        /* Sample register contents every 1 sec */
        if (time_after(jiffies, data->last_update + HZ) || !data->valid) {
                if (data->type != sch5027) {
-                       data->vid = dme1737_read(client, DME1737_REG_VID) &
+                       data->vid = dme1737_read(data, DME1737_REG_VID) &
                                0x3f;
                }
 
@@ -580,11 +581,11 @@ static struct dme1737_data *dme1737_update_device(struct device *dev)
                        /* Voltage inputs are stored as 16 bit values even
                         * though they have only 12 bits resolution. This is
                         * to make it consistent with the temp inputs. */
-                       data->in[ix] = dme1737_read(client,
+                       data->in[ix] = dme1737_read(data,
                                        DME1737_REG_IN(ix)) << 8;
-                       data->in_min[ix] = dme1737_read(client,
+                       data->in_min[ix] = dme1737_read(data,
                                        DME1737_REG_IN_MIN(ix));
-                       data->in_max[ix] = dme1737_read(client,
+                       data->in_max[ix] = dme1737_read(data,
                                        DME1737_REG_IN_MAX(ix));
                }
 
@@ -595,14 +596,14 @@ static struct dme1737_data *dme1737_update_device(struct device *dev)
                         * to take advantage of implicit conversions between
                         * register values (2's complement) and temp values
                         * (signed decimal). */
-                       data->temp[ix] = dme1737_read(client,
+                       data->temp[ix] = dme1737_read(data,
                                        DME1737_REG_TEMP(ix)) << 8;
-                       data->temp_min[ix] = dme1737_read(client,
+                       data->temp_min[ix] = dme1737_read(data,
                                        DME1737_REG_TEMP_MIN(ix));
-                       data->temp_max[ix] = dme1737_read(client,
+                       data->temp_max[ix] = dme1737_read(data,
                                        DME1737_REG_TEMP_MAX(ix));
                        if (data->type != sch5027) {
-                               data->temp_offset[ix] = dme1737_read(client,
+                               data->temp_offset[ix] = dme1737_read(data,
                                                DME1737_REG_TEMP_OFFSET(ix));
                        }
                }
@@ -612,7 +613,7 @@ static struct dme1737_data *dme1737_update_device(struct device *dev)
                 * which the registers are read (MSB first, then LSB) is
                 * important! */
                for (ix = 0; ix < ARRAY_SIZE(lsb); ix++) {
-                       lsb[ix] = dme1737_read(client,
+                       lsb[ix] = dme1737_read(data,
                                        DME1737_REG_IN_TEMP_LSB(ix));
                }
                for (ix = 0; ix < ARRAY_SIZE(data->in); ix++) {
@@ -631,19 +632,19 @@ static struct dme1737_data *dme1737_update_device(struct device *dev)
                        if (!(data->has_fan & (1 << ix))) {
                                continue;
                        }
-                       data->fan[ix] = dme1737_read(client,
+                       data->fan[ix] = dme1737_read(data,
                                        DME1737_REG_FAN(ix));
-                       data->fan[ix] |= dme1737_read(client,
+                       data->fan[ix] |= dme1737_read(data,
                                        DME1737_REG_FAN(ix) + 1) << 8;
-                       data->fan_min[ix] = dme1737_read(client,
+                       data->fan_min[ix] = dme1737_read(data,
                                        DME1737_REG_FAN_MIN(ix));
-                       data->fan_min[ix] |= dme1737_read(client,
+                       data->fan_min[ix] |= dme1737_read(data,
                                        DME1737_REG_FAN_MIN(ix) + 1) << 8;
-                       data->fan_opt[ix] = dme1737_read(client,
+                       data->fan_opt[ix] = dme1737_read(data,
                                        DME1737_REG_FAN_OPT(ix));
                        /* fan_max exists only for fan[5-6] */
                        if (ix > 3) {
-                               data->fan_max[ix - 4] = dme1737_read(client,
+                               data->fan_max[ix - 4] = dme1737_read(data,
                                        DME1737_REG_FAN_MAX(ix));
                        }
                }
@@ -655,63 +656,63 @@ static struct dme1737_data *dme1737_update_device(struct device *dev)
                        if (!(data->has_pwm & (1 << ix))) {
                                continue;
                        }
-                       data->pwm[ix] = dme1737_read(client,
+                       data->pwm[ix] = dme1737_read(data,
                                        DME1737_REG_PWM(ix));
-                       data->pwm_freq[ix] = dme1737_read(client,
+                       data->pwm_freq[ix] = dme1737_read(data,
                                        DME1737_REG_PWM_FREQ(ix));
                        /* pwm_config and pwm_min exist only for pwm[1-3] */
                        if (ix < 3) {
-                               data->pwm_config[ix] = dme1737_read(client,
+                               data->pwm_config[ix] = dme1737_read(data,
                                                DME1737_REG_PWM_CONFIG(ix));
-                               data->pwm_min[ix] = dme1737_read(client,
+                               data->pwm_min[ix] = dme1737_read(data,
                                                DME1737_REG_PWM_MIN(ix));
                        }
                }
                for (ix = 0; ix < ARRAY_SIZE(data->pwm_rr); ix++) {
-                       data->pwm_rr[ix] = dme1737_read(client,
+                       data->pwm_rr[ix] = dme1737_read(data,
                                                DME1737_REG_PWM_RR(ix));
                }
 
                /* Thermal zone registers */
                for (ix = 0; ix < ARRAY_SIZE(data->zone_low); ix++) {
-                       data->zone_low[ix] = dme1737_read(client,
+                       data->zone_low[ix] = dme1737_read(data,
                                        DME1737_REG_ZONE_LOW(ix));
-                       data->zone_abs[ix] = dme1737_read(client,
+                       data->zone_abs[ix] = dme1737_read(data,
                                        DME1737_REG_ZONE_ABS(ix));
                }
                if (data->type != sch5027) {
                        for (ix = 0; ix < ARRAY_SIZE(data->zone_hyst); ix++) {
-                               data->zone_hyst[ix] = dme1737_read(client,
+                               data->zone_hyst[ix] = dme1737_read(data,
                                                DME1737_REG_ZONE_HYST(ix));
                        }
                }
 
                /* Alarm registers */
-               data->alarms = dme1737_read(client,
+               data->alarms = dme1737_read(data,
                                                DME1737_REG_ALARM1);
                /* Bit 7 tells us if the other alarm registers are non-zero and
                 * therefore also need to be read */
                if (data->alarms & 0x80) {
-                       data->alarms |= dme1737_read(client,
+                       data->alarms |= dme1737_read(data,
                                                DME1737_REG_ALARM2) << 8;
-                       data->alarms |= dme1737_read(client,
+                       data->alarms |= dme1737_read(data,
                                                DME1737_REG_ALARM3) << 16;
                }
 
                /* The ISA chips require explicit clearing of alarm bits.
                 * Don't worry, an alarm will come back if the condition
                 * that causes it still exists */
-               if (!client->driver) {
+               if (!data->client) {
                        if (data->alarms & 0xff0000) {
-                               dme1737_write(client, DME1737_REG_ALARM3,
+                               dme1737_write(data, DME1737_REG_ALARM3,
                                              0xff);
                        }
                        if (data->alarms & 0xff00) {
-                               dme1737_write(client, DME1737_REG_ALARM2,
+                               dme1737_write(data, DME1737_REG_ALARM2,
                                              0xff);
                        }
                        if (data->alarms & 0xff) {
-                               dme1737_write(client, DME1737_REG_ALARM1,
+                               dme1737_write(data, DME1737_REG_ALARM1,
                                              0xff);
                        }
                }
@@ -770,7 +771,6 @@ static ssize_t set_in(struct device *dev, struct device_attribute *attr,
                      const char *buf, size_t count)
 {
        struct dme1737_data *data = dev_get_drvdata(dev);
-       struct i2c_client *client = &data->client;
        struct sensor_device_attribute_2
                *sensor_attr_2 = to_sensor_dev_attr_2(attr);
        int ix = sensor_attr_2->index;
@@ -781,12 +781,12 @@ static ssize_t set_in(struct device *dev, struct device_attribute *attr,
        switch (fn) {
        case SYS_IN_MIN:
                data->in_min[ix] = IN_TO_REG(val, data->in_nominal[ix]);
-               dme1737_write(client, DME1737_REG_IN_MIN(ix),
+               dme1737_write(data, DME1737_REG_IN_MIN(ix),
                              data->in_min[ix]);
                break;
        case SYS_IN_MAX:
                data->in_max[ix] = IN_TO_REG(val, data->in_nominal[ix]);
-               dme1737_write(client, DME1737_REG_IN_MAX(ix),
+               dme1737_write(data, DME1737_REG_IN_MAX(ix),
                              data->in_max[ix]);
                break;
        default:
@@ -850,7 +850,6 @@ static ssize_t set_temp(struct device *dev, struct device_attribute *attr,
                        const char *buf, size_t count)
 {
        struct dme1737_data *data = dev_get_drvdata(dev);
-       struct i2c_client *client = &data->client;
        struct sensor_device_attribute_2
                *sensor_attr_2 = to_sensor_dev_attr_2(attr);
        int ix = sensor_attr_2->index;
@@ -861,17 +860,17 @@ static ssize_t set_temp(struct device *dev, struct device_attribute *attr,
        switch (fn) {
        case SYS_TEMP_MIN:
                data->temp_min[ix] = TEMP_TO_REG(val);
-               dme1737_write(client, DME1737_REG_TEMP_MIN(ix),
+               dme1737_write(data, DME1737_REG_TEMP_MIN(ix),
                              data->temp_min[ix]);
                break;
        case SYS_TEMP_MAX:
                data->temp_max[ix] = TEMP_TO_REG(val);
-               dme1737_write(client, DME1737_REG_TEMP_MAX(ix),
+               dme1737_write(data, DME1737_REG_TEMP_MAX(ix),
                              data->temp_max[ix]);
                break;
        case SYS_TEMP_OFFSET:
                data->temp_offset[ix] = TEMP_TO_REG(val);
-               dme1737_write(client, DME1737_REG_TEMP_OFFSET(ix),
+               dme1737_write(data, DME1737_REG_TEMP_OFFSET(ix),
                              data->temp_offset[ix]);
                break;
        default:
@@ -939,7 +938,6 @@ static ssize_t set_zone(struct device *dev, struct device_attribute *attr,
                        const char *buf, size_t count)
 {
        struct dme1737_data *data = dev_get_drvdata(dev);
-       struct i2c_client *client = &data->client;
        struct sensor_device_attribute_2
                *sensor_attr_2 = to_sensor_dev_attr_2(attr);
        int ix = sensor_attr_2->index;
@@ -950,37 +948,37 @@ static ssize_t set_zone(struct device *dev, struct device_attribute *attr,
        switch (fn) {
        case SYS_ZONE_AUTO_POINT1_TEMP_HYST:
                /* Refresh the cache */
-               data->zone_low[ix] = dme1737_read(client,
+               data->zone_low[ix] = dme1737_read(data,
                                                  DME1737_REG_ZONE_LOW(ix));
                /* Modify the temp hyst value */
                data->zone_hyst[ix == 2] = TEMP_HYST_TO_REG(
                                        TEMP_FROM_REG(data->zone_low[ix], 8) -
-                                       val, ix, dme1737_read(client,
+                                       val, ix, dme1737_read(data,
                                        DME1737_REG_ZONE_HYST(ix == 2)));
-               dme1737_write(client, DME1737_REG_ZONE_HYST(ix == 2),
+               dme1737_write(data, DME1737_REG_ZONE_HYST(ix == 2),
                              data->zone_hyst[ix == 2]);
                break;
        case SYS_ZONE_AUTO_POINT1_TEMP:
                data->zone_low[ix] = TEMP_TO_REG(val);
-               dme1737_write(client, DME1737_REG_ZONE_LOW(ix),
+               dme1737_write(data, DME1737_REG_ZONE_LOW(ix),
                              data->zone_low[ix]);
                break;
        case SYS_ZONE_AUTO_POINT2_TEMP:
                /* Refresh the cache */
-               data->zone_low[ix] = dme1737_read(client,
+               data->zone_low[ix] = dme1737_read(data,
                                                  DME1737_REG_ZONE_LOW(ix));
                /* Modify the temp range value (which is stored in the upper
                 * nibble of the pwm_freq register) */
                data->pwm_freq[ix] = TEMP_RANGE_TO_REG(val -
                                        TEMP_FROM_REG(data->zone_low[ix], 8),
-                                       dme1737_read(client,
+                                       dme1737_read(data,
                                        DME1737_REG_PWM_FREQ(ix)));
-               dme1737_write(client, DME1737_REG_PWM_FREQ(ix),
+               dme1737_write(data, DME1737_REG_PWM_FREQ(ix),
                              data->pwm_freq[ix]);
                break;
        case SYS_ZONE_AUTO_POINT3_TEMP:
                data->zone_abs[ix] = TEMP_TO_REG(val);
-               dme1737_write(client, DME1737_REG_ZONE_ABS(ix),
+               dme1737_write(data, DME1737_REG_ZONE_ABS(ix),
                              data->zone_abs[ix]);
                break;
        default:
@@ -1046,7 +1044,6 @@ static ssize_t set_fan(struct device *dev, struct device_attribute *attr,
                       const char *buf, size_t count)
 {
        struct dme1737_data *data = dev_get_drvdata(dev);
-       struct i2c_client *client = &data->client;
        struct sensor_device_attribute_2
                *sensor_attr_2 = to_sensor_dev_attr_2(attr);
        int ix = sensor_attr_2->index;
@@ -1060,21 +1057,21 @@ static ssize_t set_fan(struct device *dev, struct device_attribute *attr,
                        data->fan_min[ix] = FAN_TO_REG(val, 0);
                } else {
                        /* Refresh the cache */
-                       data->fan_opt[ix] = dme1737_read(client,
+                       data->fan_opt[ix] = dme1737_read(data,
                                                DME1737_REG_FAN_OPT(ix));
                        /* Modify the fan min value */
                        data->fan_min[ix] = FAN_TO_REG(val,
                                        FAN_TPC_FROM_REG(data->fan_opt[ix]));
                }
-               dme1737_write(client, DME1737_REG_FAN_MIN(ix),
+               dme1737_write(data, DME1737_REG_FAN_MIN(ix),
                              data->fan_min[ix] & 0xff);
-               dme1737_write(client, DME1737_REG_FAN_MIN(ix) + 1,
+               dme1737_write(data, DME1737_REG_FAN_MIN(ix) + 1,
                              data->fan_min[ix] >> 8);
                break;
        case SYS_FAN_MAX:
                /* Only valid for fan[5-6] */
                data->fan_max[ix - 4] = FAN_MAX_TO_REG(val);
-               dme1737_write(client, DME1737_REG_FAN_MAX(ix),
+               dme1737_write(data, DME1737_REG_FAN_MAX(ix),
                              data->fan_max[ix - 4]);
                break;
        case SYS_FAN_TYPE:
@@ -1086,9 +1083,9 @@ static ssize_t set_fan(struct device *dev, struct device_attribute *attr,
                                 val);
                        goto exit;
                }
-               data->fan_opt[ix] = FAN_TYPE_TO_REG(val, dme1737_read(client,
+               data->fan_opt[ix] = FAN_TYPE_TO_REG(val, dme1737_read(data,
                                        DME1737_REG_FAN_OPT(ix)));
-               dme1737_write(client, DME1737_REG_FAN_OPT(ix),
+               dme1737_write(data, DME1737_REG_FAN_OPT(ix),
                              data->fan_opt[ix]);
                break;
        default:
@@ -1185,7 +1182,6 @@ static ssize_t set_pwm(struct device *dev, struct device_attribute *attr,
                       const char *buf, size_t count)
 {
        struct dme1737_data *data = dev_get_drvdata(dev);
-       struct i2c_client *client = &data->client;
        struct sensor_device_attribute_2
                *sensor_attr_2 = to_sensor_dev_attr_2(attr);
        int ix = sensor_attr_2->index;
@@ -1196,12 +1192,12 @@ static ssize_t set_pwm(struct device *dev, struct device_attribute *attr,
        switch (fn) {
        case SYS_PWM:
                data->pwm[ix] = SENSORS_LIMIT(val, 0, 255);
-               dme1737_write(client, DME1737_REG_PWM(ix), data->pwm[ix]);
+               dme1737_write(data, DME1737_REG_PWM(ix), data->pwm[ix]);
                break;
        case SYS_PWM_FREQ:
-               data->pwm_freq[ix] = PWM_FREQ_TO_REG(val, dme1737_read(client,
+               data->pwm_freq[ix] = PWM_FREQ_TO_REG(val, dme1737_read(data,
                                                DME1737_REG_PWM_FREQ(ix)));
-               dme1737_write(client, DME1737_REG_PWM_FREQ(ix),
+               dme1737_write(data, DME1737_REG_PWM_FREQ(ix),
                              data->pwm_freq[ix]);
                break;
        case SYS_PWM_ENABLE:
@@ -1214,7 +1210,7 @@ static ssize_t set_pwm(struct device *dev, struct device_attribute *attr,
                        goto exit;
                }
                /* Refresh the cache */
-               data->pwm_config[ix] = dme1737_read(client,
+               data->pwm_config[ix] = dme1737_read(data,
                                                DME1737_REG_PWM_CONFIG(ix));
                if (val == PWM_EN_FROM_REG(data->pwm_config[ix])) {
                        /* Bail out if no change */
@@ -1226,14 +1222,14 @@ static ssize_t set_pwm(struct device *dev, struct device_attribute *attr,
                        data->pwm_acz[ix] = PWM_ACZ_FROM_REG(
                                                        data->pwm_config[ix]);
                        /* Save the current ramp rate state and disable it */
-                       data->pwm_rr[ix > 0] = dme1737_read(client,
+                       data->pwm_rr[ix > 0] = dme1737_read(data,
                                                DME1737_REG_PWM_RR(ix > 0));
                        data->pwm_rr_en &= ~(1 << ix);
                        if (PWM_RR_EN_FROM_REG(data->pwm_rr[ix > 0], ix)) {
                                data->pwm_rr_en |= (1 << ix);
                                data->pwm_rr[ix > 0] = PWM_RR_EN_TO_REG(0, ix,
                                                        data->pwm_rr[ix > 0]);
-                               dme1737_write(client,
+                               dme1737_write(data,
                                              DME1737_REG_PWM_RR(ix > 0),
                                              data->pwm_rr[ix > 0]);
                        }
@@ -1247,14 +1243,14 @@ static ssize_t set_pwm(struct device *dev, struct device_attribute *attr,
                        /* Turn fan fully on */
                        data->pwm_config[ix] = PWM_EN_TO_REG(0,
                                                        data->pwm_config[ix]);
-                       dme1737_write(client, DME1737_REG_PWM_CONFIG(ix),
+                       dme1737_write(data, DME1737_REG_PWM_CONFIG(ix),
                                      data->pwm_config[ix]);
                        break;
                case 1:
                        /* Turn on manual mode */
                        data->pwm_config[ix] = PWM_EN_TO_REG(1,
                                                        data->pwm_config[ix]);
-                       dme1737_write(client, DME1737_REG_PWM_CONFIG(ix),
+                       dme1737_write(data, DME1737_REG_PWM_CONFIG(ix),
                                      data->pwm_config[ix]);
                        /* Change permissions of pwm[ix] to read-writeable */
                        dme1737_chmod_file(dev, dme1737_pwm_chmod_attr[ix],
@@ -1269,14 +1265,14 @@ static ssize_t set_pwm(struct device *dev, struct device_attribute *attr,
                        data->pwm_config[ix] = PWM_ACZ_TO_REG(
                                                        data->pwm_acz[ix],
                                                        data->pwm_config[ix]);
-                       dme1737_write(client, DME1737_REG_PWM_CONFIG(ix),
+                       dme1737_write(data, DME1737_REG_PWM_CONFIG(ix),
                                      data->pwm_config[ix]);
                        /* Enable PWM ramp rate if previously enabled */
                        if (data->pwm_rr_en & (1 << ix)) {
                                data->pwm_rr[ix > 0] = PWM_RR_EN_TO_REG(1, ix,
-                                               dme1737_read(client,
+                                               dme1737_read(data,
                                                DME1737_REG_PWM_RR(ix > 0)));
-                               dme1737_write(client,
+                               dme1737_write(data,
                                              DME1737_REG_PWM_RR(ix > 0),
                                              data->pwm_rr[ix > 0]);
                        }
@@ -1286,9 +1282,9 @@ static ssize_t set_pwm(struct device *dev, struct device_attribute *attr,
        case SYS_PWM_RAMP_RATE:
                /* Only valid for pwm[1-3] */
                /* Refresh the cache */
-               data->pwm_config[ix] = dme1737_read(client,
+               data->pwm_config[ix] = dme1737_read(data,
                                                DME1737_REG_PWM_CONFIG(ix));
-               data->pwm_rr[ix > 0] = dme1737_read(client,
+               data->pwm_rr[ix > 0] = dme1737_read(data,
                                                DME1737_REG_PWM_RR(ix > 0));
                /* Set the ramp rate value */
                if (val > 0) {
@@ -1301,7 +1297,7 @@ static ssize_t set_pwm(struct device *dev, struct device_attribute *attr,
                        data->pwm_rr[ix > 0] = PWM_RR_EN_TO_REG(val > 0, ix,
                                                        data->pwm_rr[ix > 0]);
                }
-               dme1737_write(client, DME1737_REG_PWM_RR(ix > 0),
+               dme1737_write(data, DME1737_REG_PWM_RR(ix > 0),
                              data->pwm_rr[ix > 0]);
                break;
        case SYS_PWM_AUTO_CHANNELS_ZONE:
@@ -1315,14 +1311,14 @@ static ssize_t set_pwm(struct device *dev, struct device_attribute *attr,
                        goto exit;
                }
                /* Refresh the cache */
-               data->pwm_config[ix] = dme1737_read(client,
+               data->pwm_config[ix] = dme1737_read(data,
                                                DME1737_REG_PWM_CONFIG(ix));
                if (PWM_EN_FROM_REG(data->pwm_config[ix]) == 2) {
                        /* PWM is already in auto mode so update the temp
                         * channel assignment */
                        data->pwm_config[ix] = PWM_ACZ_TO_REG(val,
                                                data->pwm_config[ix]);
-                       dme1737_write(client, DME1737_REG_PWM_CONFIG(ix),
+                       dme1737_write(data, DME1737_REG_PWM_CONFIG(ix),
                                      data->pwm_config[ix]);
                } else {
                        /* PWM is not in auto mode so we save the temp
@@ -1333,7 +1329,7 @@ static ssize_t set_pwm(struct device *dev, struct device_attribute *attr,
        case SYS_PWM_AUTO_PWM_MIN:
                /* Only valid for pwm[1-3] */
                /* Refresh the cache */
-               data->pwm_min[ix] = dme1737_read(client,
+               data->pwm_min[ix] = dme1737_read(data,
                                                DME1737_REG_PWM_MIN(ix));
                /* There are only 2 values supported for the auto_pwm_min
                 * value: 0 or auto_point1_pwm. So if the temperature drops
@@ -1341,20 +1337,20 @@ static ssize_t set_pwm(struct device *dev, struct device_attribute *attr,
                 * off or runs at auto_point1_pwm duty-cycle. */
                if (val > ((data->pwm_min[ix] + 1) / 2)) {
                        data->pwm_rr[0] = PWM_OFF_TO_REG(1, ix,
-                                               dme1737_read(client,
+                                               dme1737_read(data,
                                                DME1737_REG_PWM_RR(0)));
                } else {
                        data->pwm_rr[0] = PWM_OFF_TO_REG(0, ix,
-                                               dme1737_read(client,
+                                               dme1737_read(data,
                                                DME1737_REG_PWM_RR(0)));
                }
-               dme1737_write(client, DME1737_REG_PWM_RR(0),
+               dme1737_write(data, DME1737_REG_PWM_RR(0),
                              data->pwm_rr[0]);
                break;
        case SYS_PWM_AUTO_POINT1_PWM:
                /* Only valid for pwm[1-3] */
                data->pwm_min[ix] = SENSORS_LIMIT(val, 0, 255);
-               dme1737_write(client, DME1737_REG_PWM_MIN(ix),
+               dme1737_write(data, DME1737_REG_PWM_MIN(ix),
                              data->pwm_min[ix]);
                break;
        default:
@@ -1402,7 +1398,7 @@ static ssize_t show_name(struct device *dev, struct device_attribute *attr,
 {
        struct dme1737_data *data = dev_get_drvdata(dev);
 
-       return sprintf(buf, "%s\n", data->client.name);
+       return sprintf(buf, "%s\n", data->name);
 }
 
 /* ---------------------------------------------------------------------
@@ -1908,7 +1904,7 @@ static void dme1737_remove_files(struct device *dev)
 
        sysfs_remove_group(&dev->kobj, &dme1737_group);
 
-       if (!data->client.driver) {
+       if (!data->client) {
                sysfs_remove_file(&dev->kobj, &dev_attr_name.attr);
        }
 }
@@ -1919,7 +1915,7 @@ static int dme1737_create_files(struct device *dev)
        int err, ix;
 
        /* Create a name attribute for ISA devices */
-       if (!data->client.driver &&
+       if (!data->client &&
            (err = sysfs_create_file(&dev->kobj, &dev_attr_name.attr))) {
                goto exit;
        }
@@ -2013,14 +2009,14 @@ exit:
 static int dme1737_init_device(struct device *dev)
 {
        struct dme1737_data *data = dev_get_drvdata(dev);
-       struct i2c_client *client = &data->client;
+       struct i2c_client *client = data->client;
        int ix;
        u8 reg;
 
        /* Point to the right nominal voltages array */
        data->in_nominal = IN_NOMINAL(data->type);
 
-       data->config = dme1737_read(client, DME1737_REG_CONFIG);
+       data->config = dme1737_read(data, DME1737_REG_CONFIG);
        /* Inform if part is not monitoring/started */
        if (!(data->config & 0x01)) {
                if (!force_start) {
@@ -2032,7 +2028,7 @@ static int dme1737_init_device(struct device *dev)
 
                /* Force monitoring */
                data->config |= 0x01;
-               dme1737_write(client, DME1737_REG_CONFIG, data->config);
+               dme1737_write(data, DME1737_REG_CONFIG, data->config);
        }
        /* Inform if part is not ready */
        if (!(data->config & 0x04)) {
@@ -2041,8 +2037,8 @@ static int dme1737_init_device(struct device *dev)
        }
 
        /* Determine which optional fan and pwm features are enabled/present */
-       if (client->driver) {   /* I2C chip */
-               data->config2 = dme1737_read(client, DME1737_REG_CONFIG2);
+       if (client) {   /* I2C chip */
+               data->config2 = dme1737_read(data, DME1737_REG_CONFIG2);
                /* Check if optional fan3 input is enabled */
                if (data->config2 & 0x04) {
                        data->has_fan |= (1 << 2);
@@ -2051,7 +2047,7 @@ static int dme1737_init_device(struct device *dev)
                /* Fan4 and pwm3 are only available if the client's I2C address
                 * is the default 0x2e. Otherwise the I/Os associated with
                 * these functions are used for addr enable/select. */
-               if (data->client.addr == 0x2e) {
+               if (client->addr == 0x2e) {
                        data->has_fan |= (1 << 3);
                        data->has_pwm |= (1 << 2);
                }
@@ -2086,16 +2082,16 @@ static int dme1737_init_device(struct device *dev)
                 (data->has_fan & (1 << 4)) ? "yes" : "no",
                 (data->has_fan & (1 << 5)) ? "yes" : "no");
 
-       reg = dme1737_read(client, DME1737_REG_TACH_PWM);
+       reg = dme1737_read(data, DME1737_REG_TACH_PWM);
        /* Inform if fan-to-pwm mapping differs from the default */
-       if (client->driver && reg != 0xa4) {   /* I2C chip */
+       if (client && reg != 0xa4) {   /* I2C chip */
                dev_warn(dev, "Non-standard fan to pwm mapping: "
                         "fan1->pwm%d, fan2->pwm%d, fan3->pwm%d, "
                         "fan4->pwm%d. Please report to the driver "
                         "maintainer.\n",
                         (reg & 0x03) + 1, ((reg >> 2) & 0x03) + 1,
                         ((reg >> 4) & 0x03) + 1, ((reg >> 6) & 0x03) + 1);
-       } else if (!client->driver && reg != 0x24) {   /* ISA chip */
+       } else if (!client && reg != 0x24) {   /* ISA chip */
                dev_warn(dev, "Non-standard fan to pwm mapping: "
                         "fan1->pwm%d, fan2->pwm%d, fan3->pwm%d. "
                         "Please report to the driver maintainer.\n",
@@ -2108,7 +2104,7 @@ static int dme1737_init_device(struct device *dev)
         * disabled). */
        if (!(data->config & 0x02)) {
                for (ix = 0; ix < 3; ix++) {
-                       data->pwm_config[ix] = dme1737_read(client,
+                       data->pwm_config[ix] = dme1737_read(data,
                                                DME1737_REG_PWM_CONFIG(ix));
                        if ((data->has_pwm & (1 << ix)) &&
                            (PWM_EN_FROM_REG(data->pwm_config[ix]) == -1)) {
@@ -2116,8 +2112,8 @@ static int dme1737_init_device(struct device *dev)
                                         "manual mode.\n", ix + 1);
                                data->pwm_config[ix] = PWM_EN_TO_REG(1,
                                                        data->pwm_config[ix]);
-                               dme1737_write(client, DME1737_REG_PWM(ix), 0);
-                               dme1737_write(client,
+                               dme1737_write(data, DME1737_REG_PWM(ix), 0);
+                               dme1737_write(data,
                                              DME1737_REG_PWM_CONFIG(ix),
                                              data->pwm_config[ix]);
                        }
@@ -2191,37 +2187,24 @@ exit:
        return err;
 }
 
-static int dme1737_i2c_detect(struct i2c_adapter *adapter, int address,
-                             int kind)
+/* Return 0 if detection is successful, -ENODEV otherwise */
+static int dme1737_i2c_detect(struct i2c_client *client, int kind,
+                             struct i2c_board_info *info)
 {
+       struct i2c_adapter *adapter = client->adapter;
+       struct device *dev = &adapter->dev;
        u8 company, verstep = 0;
-       struct i2c_client *client;
-       struct dme1737_data *data;
-       struct device *dev;
-       int err = 0;
        const char *name;
 
        if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
-               goto exit;
-       }
-
-       if (!(data = kzalloc(sizeof(struct dme1737_data), GFP_KERNEL))) {
-               err = -ENOMEM;
-               goto exit;
+               return -ENODEV;
        }
 
-       client = &data->client;
-       i2c_set_clientdata(client, data);
-       client->addr = address;
-       client->adapter = adapter;
-       client->driver = &dme1737_i2c_driver;
-       dev = &client->dev;
-
        /* A negative kind means that the driver was loaded with no force
         * parameter (default), so we must identify the chip. */
        if (kind < 0) {
-               company = dme1737_read(client, DME1737_REG_COMPANY);
-               verstep = dme1737_read(client, DME1737_REG_VERSTEP);
+               company = i2c_smbus_read_byte_data(client, DME1737_REG_COMPANY);
+               verstep = i2c_smbus_read_byte_data(client, DME1737_REG_VERSTEP);
 
                if (company == DME1737_COMPANY_SMSC &&
                    (verstep & DME1737_VERSTEP_MASK) == DME1737_VERSTEP) {
@@ -2230,8 +2213,7 @@ static int dme1737_i2c_detect(struct i2c_adapter *adapter, int address,
                           verstep == SCH5027_VERSTEP) {
                        kind = sch5027;
                } else {
-                       err = -ENODEV;
-                       goto exit_kfree;
+                       return -ENODEV;
                }
        }
 
@@ -2241,32 +2223,44 @@ static int dme1737_i2c_detect(struct i2c_adapter *adapter, int address,
                kind = dme1737;
                name = "dme1737";
        }
-       data->type = kind;
-
-       /* Fill in the remaining client fields and put it into the global
-        * list */
-       strlcpy(client->name, name, I2C_NAME_SIZE);
-       mutex_init(&data->update_lock);
-
-       /* Tell the I2C layer a new client has arrived */
-       if ((err = i2c_attach_client(client))) {
-               goto exit_kfree;
-       }
 
        dev_info(dev, "Found a %s chip at 0x%02x (rev 0x%02x).\n",
                 kind == sch5027 ? "SCH5027" : "DME1737", client->addr,
                 verstep);
+       strlcpy(info->type, name, I2C_NAME_SIZE);
+
+       return 0;
+}
+
+static int dme1737_i2c_probe(struct i2c_client *client,
+                            const struct i2c_device_id *id)
+{
+       struct dme1737_data *data;
+       struct device *dev = &client->dev;
+       int err;
+
+       data = kzalloc(sizeof(struct dme1737_data), GFP_KERNEL);
+       if (!data) {
+               err = -ENOMEM;
+               goto exit;
+       }
+
+       i2c_set_clientdata(client, data);
+       data->type = id->driver_data;
+       data->client = client;
+       data->name = client->name;
+       mutex_init(&data->update_lock);
 
        /* Initialize the DME1737 chip */
        if ((err = dme1737_init_device(dev))) {
                dev_err(dev, "Failed to initialize device.\n");
-               goto exit_detach;
+               goto exit_kfree;
        }
 
        /* Create sysfs files */
        if ((err = dme1737_create_files(dev))) {
                dev_err(dev, "Failed to create sysfs files.\n");
-               goto exit_detach;
+               goto exit_kfree;
        }
 
        /* Register device */
@@ -2281,45 +2275,40 @@ static int dme1737_i2c_detect(struct i2c_adapter *adapter, int address,
 
 exit_remove:
        dme1737_remove_files(dev);
-exit_detach:
-       i2c_detach_client(client);
 exit_kfree:
        kfree(data);
 exit:
        return err;
 }
 
-static int dme1737_i2c_attach_adapter(struct i2c_adapter *adapter)
-{
-       if (!(adapter->class & I2C_CLASS_HWMON)) {
-               return 0;
-       }
-
-       return i2c_probe(adapter, &addr_data, dme1737_i2c_detect);
-}
-
-static int dme1737_i2c_detach_client(struct i2c_client *client)
+static int dme1737_i2c_remove(struct i2c_client *client)
 {
        struct dme1737_data *data = i2c_get_clientdata(client);
-       int err;
 
        hwmon_device_unregister(data->hwmon_dev);
        dme1737_remove_files(&client->dev);
 
-       if ((err = i2c_detach_client(client))) {
-               return err;
-       }
-
        kfree(data);
        return 0;
 }
 
+static const struct i2c_device_id dme1737_id[] = {
+       { "dme1737", dme1737 },
+       { "sch5027", sch5027 },
+       { }
+};
+MODULE_DEVICE_TABLE(i2c, dme1737_id);
+
 static struct i2c_driver dme1737_i2c_driver = {
+       .class = I2C_CLASS_HWMON,
        .driver = {
                .name = "dme1737",
        },
-       .attach_adapter = dme1737_i2c_attach_adapter,
-       .detach_client = dme1737_i2c_detach_client,
+       .probe = dme1737_i2c_probe,
+       .remove = dme1737_i2c_remove,
+       .id_table = dme1737_id,
+       .detect = dme1737_i2c_detect,
+       .address_data = &addr_data,
 };
 
 /* ---------------------------------------------------------------------
@@ -2403,7 +2392,6 @@ static int __devinit dme1737_isa_probe(struct platform_device *pdev)
 {
        u8 company, device;
        struct resource *res;
-       struct i2c_client *client;
        struct dme1737_data *data;
        struct device *dev = &pdev->dev;
        int err;
@@ -2422,15 +2410,13 @@ static int __devinit dme1737_isa_probe(struct platform_device *pdev)
                goto exit_release_region;
        }
 
-       client = &data->client;
-       i2c_set_clientdata(client, data);
-       client->addr = res->start;
+       data->addr = res->start;
        platform_set_drvdata(pdev, data);
 
        /* Skip chip detection if module is loaded with force_id parameter */
        if (!force_id) {
-               company = dme1737_read(client, DME1737_REG_COMPANY);
-               device = dme1737_read(client, DME1737_REG_DEVICE);
+               company = dme1737_read(data, DME1737_REG_COMPANY);
+               device = dme1737_read(data, DME1737_REG_DEVICE);
 
                if (!((company == DME1737_COMPANY_SMSC) &&
                      (device == SCH311X_DEVICE))) {
@@ -2441,10 +2427,10 @@ static int __devinit dme1737_isa_probe(struct platform_device *pdev)
        data->type = sch311x;
 
        /* Fill in the remaining client fields and initialize the mutex */
-       strlcpy(client->name, "sch311x", I2C_NAME_SIZE);
+       data->name = "sch311x";
        mutex_init(&data->update_lock);
 
-       dev_info(dev, "Found a SCH311x chip at 0x%04x\n", client->addr);
+       dev_info(dev, "Found a SCH311x chip at 0x%04x\n", data->addr);
 
        /* Initialize the chip */
        if ((err = dme1737_init_device(dev))) {
@@ -2485,7 +2471,7 @@ static int __devexit dme1737_isa_remove(struct platform_device *pdev)
 
        hwmon_device_unregister(data->hwmon_dev);
        dme1737_remove_files(&pdev->dev);
-       release_region(data->client.addr, DME1737_EXTENT);
+       release_region(data->addr, DME1737_EXTENT);
        platform_set_drvdata(pdev, NULL);
        kfree(data);
 
index 6ee997b2817c450b3ac2fae2bf32e5ad035ff5d3..acadbc51fc0f35f6f1b9f261fc909cfffcef5c3a 100644 (file)
@@ -55,7 +55,7 @@ config I2C_AMD756
 
 config I2C_AMD756_S4882
        tristate "SMBus multiplexing on the Tyan S4882"
-       depends on I2C_AMD756 && EXPERIMENTAL
+       depends on I2C_AMD756 && X86 && EXPERIMENTAL
        help
          Enabling this option will add specific SMBus support for the Tyan
          S4882 motherboard.  On this 4-CPU board, the SMBus is multiplexed
@@ -148,7 +148,7 @@ config I2C_NFORCE2
 
 config I2C_NFORCE2_S4985
        tristate "SMBus multiplexing on the Tyan S4985"
-       depends on I2C_NFORCE2 && EXPERIMENTAL
+       depends on I2C_NFORCE2 && X86 && EXPERIMENTAL
        help
          Enabling this option will add specific SMBus support for the Tyan
          S4985 motherboard.  On this 4-CPU board, the SMBus is multiplexed
@@ -209,7 +209,7 @@ config I2C_VIA
          will be called i2c-via.
 
 config I2C_VIAPRO
-       tristate "VIA VT82C596/82C686/82xx and CX700"
+       tristate "VIA VT82C596/82C686/82xx and CX700/VX800/VX820"
        depends on PCI
        help
          If you say yes to this option, support will be included for the VIA
@@ -223,6 +223,8 @@ config I2C_VIAPRO
            VT8237R/A/S
            VT8251
            CX700
+           VX800
+           VX820
 
          This driver can also be built as a module.  If so, the module
          will be called i2c-viapro.
@@ -330,6 +332,18 @@ config I2C_GPIO
          This is a very simple bitbanging I2C driver utilizing the
          arch-neutral GPIO API to control the SCL and SDA lines.
 
+config I2C_HIGHLANDER
+       tristate "Highlander FPGA SMBus interface"
+       depends on SH_HIGHLANDER
+       help
+         If you say yes to this option, support will be included for
+         the SMBus interface located in the FPGA on various Highlander
+         boards, particularly the R0P7780LC0011RL and R0P7785LC0011RL
+         FPGAs. This is wholly unrelated to the SoC I2C.
+
+         This driver can also be built as a module.  If so, the module
+         will be called i2c-highlander.
+
 config I2C_IBM_IIC
        tristate "IBM PPC 4xx on-chip I2C interface"
        depends on 4xx
index 97dbfa2107fee2fe7b7170219c9aebf552b8381b..0c2c4b26cdf1ff4d5bac686a40a4a306bf0e730f 100644 (file)
@@ -31,6 +31,7 @@ obj-$(CONFIG_I2C_BLACKFIN_TWI)        += i2c-bfin-twi.o
 obj-$(CONFIG_I2C_CPM)          += i2c-cpm.o
 obj-$(CONFIG_I2C_DAVINCI)      += i2c-davinci.o
 obj-$(CONFIG_I2C_GPIO)         += i2c-gpio.o
+obj-$(CONFIG_I2C_HIGHLANDER)   += i2c-highlander.o
 obj-$(CONFIG_I2C_IBM_IIC)      += i2c-ibm_iic.o
 obj-$(CONFIG_I2C_IOP3XX)       += i2c-iop3xx.o
 obj-$(CONFIG_I2C_IXP2000)      += i2c-ixp2000.o
diff --git a/drivers/i2c/busses/i2c-highlander.c b/drivers/i2c/busses/i2c-highlander.c
new file mode 100644 (file)
index 0000000..f4d22ae
--- /dev/null
@@ -0,0 +1,498 @@
+/*
+ * Renesas Solutions Highlander FPGA I2C/SMBus support.
+ *
+ * Supported devices: R0P7780LC0011RL, R0P7785LC0011RL
+ *
+ * Copyright (C) 2008  Paul Mundt
+ * Copyright (C) 2008  Renesas Solutions Corp.
+ * Copyright (C) 2008  Atom Create Engineering Co., Ltd.
+ *
+ * This file is subject to the terms and conditions of the GNU General
+ * Public License version 2. See the file "COPYING" in the main directory
+ * of this archive for more details.
+ */
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <linux/completion.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+
+#define SMCR           0x00
+#define SMCR_START     (1 << 0)
+#define SMCR_IRIC      (1 << 1)
+#define SMCR_BBSY      (1 << 2)
+#define SMCR_ACKE      (1 << 3)
+#define SMCR_RST       (1 << 4)
+#define SMCR_IEIC      (1 << 6)
+
+#define SMSMADR                0x02
+
+#define SMMR           0x04
+#define SMMR_MODE0     (1 << 0)
+#define SMMR_MODE1     (1 << 1)
+#define SMMR_CAP       (1 << 3)
+#define SMMR_TMMD      (1 << 4)
+#define SMMR_SP                (1 << 7)
+
+#define SMSADR         0x06
+#define SMTRDR         0x46
+
+struct highlander_i2c_dev {
+       struct device           *dev;
+       void __iomem            *base;
+       struct i2c_adapter      adapter;
+       struct completion       cmd_complete;
+       unsigned long           last_read_time;
+       int                     irq;
+       u8                      *buf;
+       size_t                  buf_len;
+};
+
+static int iic_force_poll, iic_force_normal;
+static int iic_timeout = 1000, iic_read_delay;
+
+static inline void highlander_i2c_irq_enable(struct highlander_i2c_dev *dev)
+{
+       iowrite16(ioread16(dev->base + SMCR) | SMCR_IEIC, dev->base + SMCR);
+}
+
+static inline void highlander_i2c_irq_disable(struct highlander_i2c_dev *dev)
+{
+       iowrite16(ioread16(dev->base + SMCR) & ~SMCR_IEIC, dev->base + SMCR);
+}
+
+static inline void highlander_i2c_start(struct highlander_i2c_dev *dev)
+{
+       iowrite16(ioread16(dev->base + SMCR) | SMCR_START, dev->base + SMCR);
+}
+
+static inline void highlander_i2c_done(struct highlander_i2c_dev *dev)
+{
+       iowrite16(ioread16(dev->base + SMCR) | SMCR_IRIC, dev->base + SMCR);
+}
+
+static void highlander_i2c_setup(struct highlander_i2c_dev *dev)
+{
+       u16 smmr;
+
+       smmr = ioread16(dev->base + SMMR);
+       smmr |= SMMR_TMMD;
+
+       if (iic_force_normal)
+               smmr &= ~SMMR_SP;
+       else
+               smmr |= SMMR_SP;
+
+       iowrite16(smmr, dev->base + SMMR);
+}
+
+static void smbus_write_data(u8 *src, u16 *dst, int len)
+{
+       for (; len > 1; len -= 2) {
+               *dst++ = be16_to_cpup((u16 *)src);
+               src += 2;
+       }
+
+       if (len)
+               *dst = *src << 8;
+}
+
+static void smbus_read_data(u16 *src, u8 *dst, int len)
+{
+       for (; len > 1; len -= 2) {
+               *(u16 *)dst = cpu_to_be16p(src++);
+               dst += 2;
+       }
+
+       if (len)
+               *dst = *src >> 8;
+}
+
+static void highlander_i2c_command(struct highlander_i2c_dev *dev,
+                                  u8 command, int len)
+{
+       unsigned int i;
+       u16 cmd = (command << 8) | command;
+
+       for (i = 0; i < len; i += 2) {
+               if (len - i == 1)
+                       cmd = command << 8;
+               iowrite16(cmd, dev->base + SMSADR + i);
+               dev_dbg(dev->dev, "command data[%x] 0x%04x\n", i/2, cmd);
+       }
+}
+
+static int highlander_i2c_wait_for_bbsy(struct highlander_i2c_dev *dev)
+{
+       unsigned long timeout;
+
+       timeout = jiffies + msecs_to_jiffies(iic_timeout);
+       while (ioread16(dev->base + SMCR) & SMCR_BBSY) {
+               if (time_after(jiffies, timeout)) {
+                       dev_warn(dev->dev, "timeout waiting for bus ready\n");
+                       return -ETIMEDOUT;
+               }
+
+               msleep(1);
+       }
+
+       return 0;
+}
+
+static int highlander_i2c_reset(struct highlander_i2c_dev *dev)
+{
+       iowrite16(ioread16(dev->base + SMCR) | SMCR_RST, dev->base + SMCR);
+       return highlander_i2c_wait_for_bbsy(dev);
+}
+
+static int highlander_i2c_wait_for_ack(struct highlander_i2c_dev *dev)
+{
+       u16 tmp = ioread16(dev->base + SMCR);
+
+       if ((tmp & (SMCR_IRIC | SMCR_ACKE)) == SMCR_ACKE) {
+               dev_warn(dev->dev, "ack abnormality\n");
+               return highlander_i2c_reset(dev);
+       }
+
+       return 0;
+}
+
+static irqreturn_t highlander_i2c_irq(int irq, void *dev_id)
+{
+       struct highlander_i2c_dev *dev = dev_id;
+
+       highlander_i2c_done(dev);
+       complete(&dev->cmd_complete);
+
+       return IRQ_HANDLED;
+}
+
+static void highlander_i2c_poll(struct highlander_i2c_dev *dev)
+{
+       unsigned long timeout;
+       u16 smcr;
+
+       timeout = jiffies + msecs_to_jiffies(iic_timeout);
+       for (;;) {
+               smcr = ioread16(dev->base + SMCR);
+
+               /*
+                * Don't bother checking ACKE here, this and the reset
+                * are handled in highlander_i2c_wait_xfer_done() when
+                * waiting for the ACK.
+                */
+
+               if (smcr & SMCR_IRIC)
+                       return;
+               if (time_after(jiffies, timeout))
+                       break;
+
+               cpu_relax();
+               cond_resched();
+       }
+
+       dev_err(dev->dev, "polling timed out\n");
+}
+
+static inline int highlander_i2c_wait_xfer_done(struct highlander_i2c_dev *dev)
+{
+       if (dev->irq)
+               wait_for_completion_timeout(&dev->cmd_complete,
+                                         msecs_to_jiffies(iic_timeout));
+       else
+               /* busy looping, the IRQ of champions */
+               highlander_i2c_poll(dev);
+
+       return highlander_i2c_wait_for_ack(dev);
+}
+
+static int highlander_i2c_read(struct highlander_i2c_dev *dev)
+{
+       int i, cnt;
+       u16 data[16];
+
+       if (highlander_i2c_wait_for_bbsy(dev))
+               return -EAGAIN;
+
+       highlander_i2c_start(dev);
+
+       if (highlander_i2c_wait_xfer_done(dev)) {
+               dev_err(dev->dev, "Arbitration loss\n");
+               return -EAGAIN;
+       }
+
+       /*
+        * The R0P7780LC0011RL FPGA needs a significant delay between
+        * data read cycles, otherwise the transciever gets confused and
+        * garbage is returned when the read is subsequently aborted.
+        *
+        * It is not sufficient to wait for BBSY.
+        *
+        * While this generally only applies to the older SH7780-based
+        * Highlanders, the same issue can be observed on SH7785 ones,
+        * albeit less frequently. SH7780-based Highlanders may need
+        * this to be as high as 1000 ms.
+        */
+       if (iic_read_delay && time_before(jiffies, dev->last_read_time +
+                                msecs_to_jiffies(iic_read_delay)))
+               msleep(jiffies_to_msecs((dev->last_read_time +
+                               msecs_to_jiffies(iic_read_delay)) - jiffies));
+
+       cnt = (dev->buf_len + 1) >> 1;
+       for (i = 0; i < cnt; i++) {
+               data[i] = ioread16(dev->base + SMTRDR + (i * sizeof(u16)));
+               dev_dbg(dev->dev, "read data[%x] 0x%04x\n", i, data[i]);
+       }
+
+       smbus_read_data(data, dev->buf, dev->buf_len);
+
+       dev->last_read_time = jiffies;
+
+       return 0;
+}
+
+static int highlander_i2c_write(struct highlander_i2c_dev *dev)
+{
+       int i, cnt;
+       u16 data[16];
+
+       smbus_write_data(dev->buf, data, dev->buf_len);
+
+       cnt = (dev->buf_len + 1) >> 1;
+       for (i = 0; i < cnt; i++) {
+               iowrite16(data[i], dev->base + SMTRDR + (i * sizeof(u16)));
+               dev_dbg(dev->dev, "write data[%x] 0x%04x\n", i, data[i]);
+       }
+
+       if (highlander_i2c_wait_for_bbsy(dev))
+               return -EAGAIN;
+
+       highlander_i2c_start(dev);
+
+       return highlander_i2c_wait_xfer_done(dev);
+}
+
+static int highlander_i2c_smbus_xfer(struct i2c_adapter *adap, u16 addr,
+                                 unsigned short flags, char read_write,
+                                 u8 command, int size,
+                                 union i2c_smbus_data *data)
+{
+       struct highlander_i2c_dev *dev = i2c_get_adapdata(adap);
+       int read = read_write & I2C_SMBUS_READ;
+       u16 tmp;
+
+       init_completion(&dev->cmd_complete);
+
+       dev_dbg(dev->dev, "addr %04x, command %02x, read_write %d, size %d\n",
+               addr, command, read_write, size);
+
+       /*
+        * Set up the buffer and transfer size
+        */
+       switch (size) {
+       case I2C_SMBUS_BYTE_DATA:
+               dev->buf = &data->byte;
+               dev->buf_len = 1;
+               break;
+       case I2C_SMBUS_I2C_BLOCK_DATA:
+               dev->buf = &data->block[1];
+               dev->buf_len = data->block[0];
+               break;
+       default:
+               dev_err(dev->dev, "unsupported command %d\n", size);
+               return -EINVAL;
+       }
+
+       /*
+        * Encode the mode setting
+        */
+       tmp = ioread16(dev->base + SMMR);
+       tmp &= ~(SMMR_MODE0 | SMMR_MODE1);
+
+       switch (dev->buf_len) {
+       case 1:
+               /* default */
+               break;
+       case 8:
+               tmp |= SMMR_MODE0;
+               break;
+       case 16:
+               tmp |= SMMR_MODE1;
+               break;
+       case 32:
+               tmp |= (SMMR_MODE0 | SMMR_MODE1);
+               break;
+       default:
+               dev_err(dev->dev, "unsupported xfer size %d\n", dev->buf_len);
+               return -EINVAL;
+       }
+
+       iowrite16(tmp, dev->base + SMMR);
+
+       /* Ensure we're in a sane state */
+       highlander_i2c_done(dev);
+
+       /* Set slave address */
+       iowrite16((addr << 1) | read, dev->base + SMSMADR);
+
+       highlander_i2c_command(dev, command, dev->buf_len);
+
+       if (read)
+               return highlander_i2c_read(dev);
+       else
+               return highlander_i2c_write(dev);
+}
+
+static u32 highlander_i2c_func(struct i2c_adapter *adapter)
+{
+       return I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_I2C_BLOCK;
+}
+
+static const struct i2c_algorithm highlander_i2c_algo = {
+       .smbus_xfer     = highlander_i2c_smbus_xfer,
+       .functionality  = highlander_i2c_func,
+};
+
+static int __devinit highlander_i2c_probe(struct platform_device *pdev)
+{
+       struct highlander_i2c_dev *dev;
+       struct i2c_adapter *adap;
+       struct resource *res;
+       int ret;
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (unlikely(!res)) {
+               dev_err(&pdev->dev, "no mem resource\n");
+               return -ENODEV;
+       }
+
+       dev = kzalloc(sizeof(struct highlander_i2c_dev), GFP_KERNEL);
+       if (unlikely(!dev))
+               return -ENOMEM;
+
+       dev->base = ioremap_nocache(res->start, res->end - res->start + 1);
+       if (unlikely(!dev->base)) {
+               ret = -ENXIO;
+               goto err;
+       }
+
+       dev->dev = &pdev->dev;
+       platform_set_drvdata(pdev, dev);
+
+       dev->irq = platform_get_irq(pdev, 0);
+       if (iic_force_poll)
+               dev->irq = 0;
+
+       if (dev->irq) {
+               ret = request_irq(dev->irq, highlander_i2c_irq, IRQF_DISABLED,
+                                 pdev->name, dev);
+               if (unlikely(ret))
+                       goto err_unmap;
+
+               highlander_i2c_irq_enable(dev);
+       } else {
+               dev_notice(&pdev->dev, "no IRQ, using polling mode\n");
+               highlander_i2c_irq_disable(dev);
+       }
+
+       dev->last_read_time = jiffies;  /* initial read jiffies */
+
+       highlander_i2c_setup(dev);
+
+       adap = &dev->adapter;
+       i2c_set_adapdata(adap, dev);
+       adap->owner = THIS_MODULE;
+       adap->class = I2C_CLASS_HWMON;
+       strlcpy(adap->name, "HL FPGA I2C adapter", sizeof(adap->name));
+       adap->algo = &highlander_i2c_algo;
+       adap->dev.parent = &pdev->dev;
+       adap->nr = pdev->id;
+
+       /*
+        * Reset the adapter
+        */
+       ret = highlander_i2c_reset(dev);
+       if (unlikely(ret)) {
+               dev_err(&pdev->dev, "controller didn't come up\n");
+               goto err_free_irq;
+       }
+
+       ret = i2c_add_numbered_adapter(adap);
+       if (unlikely(ret)) {
+               dev_err(&pdev->dev, "failure adding adapter\n");
+               goto err_free_irq;
+       }
+
+       return 0;
+
+err_free_irq:
+       if (dev->irq)
+               free_irq(dev->irq, dev);
+err_unmap:
+       iounmap(dev->base);
+err:
+       kfree(dev);
+
+       platform_set_drvdata(pdev, NULL);
+
+       return ret;
+}
+
+static int __devexit highlander_i2c_remove(struct platform_device *pdev)
+{
+       struct highlander_i2c_dev *dev = platform_get_drvdata(pdev);
+
+       i2c_del_adapter(&dev->adapter);
+
+       if (dev->irq)
+               free_irq(dev->irq, dev);
+
+       iounmap(dev->base);
+       kfree(dev);
+
+       platform_set_drvdata(pdev, NULL);
+
+       return 0;
+}
+
+static struct platform_driver highlander_i2c_driver = {
+       .driver         = {
+               .name   = "i2c-highlander",
+               .owner  = THIS_MODULE,
+       },
+
+       .probe          = highlander_i2c_probe,
+       .remove         = __devexit_p(highlander_i2c_remove),
+};
+
+static int __init highlander_i2c_init(void)
+{
+       return platform_driver_register(&highlander_i2c_driver);
+}
+
+static void __exit highlander_i2c_exit(void)
+{
+       platform_driver_unregister(&highlander_i2c_driver);
+}
+
+module_init(highlander_i2c_init);
+module_exit(highlander_i2c_exit);
+
+MODULE_AUTHOR("Paul Mundt");
+MODULE_DESCRIPTION("Renesas Highlander FPGA I2C/SMBus adapter");
+MODULE_LICENSE("GPL v2");
+
+module_param(iic_force_poll, bool, 0);
+module_param(iic_force_normal, bool, 0);
+module_param(iic_timeout, int, 0);
+module_param(iic_read_delay, int, 0);
+
+MODULE_PARM_DESC(iic_force_poll, "Force polling mode");
+MODULE_PARM_DESC(iic_force_normal,
+                "Force normal mode (100 kHz), default is fast mode (400 kHz)");
+MODULE_PARM_DESC(iic_timeout, "Set timeout value in msecs (default 1000 ms)");
+MODULE_PARM_DESC(iic_read_delay,
+                "Delay between data read cycles (default 0 ms)");
index c6faf9bdad18d1ddda022243a4de7cc29d83f185..b2b8380f66029f23f238cef943c8006a35aa95dc 100644 (file)
@@ -123,11 +123,6 @@ static struct i2c_adapter parport_adapter = {
 static int __devinit i2c_parport_probe(struct platform_device *pdev)
 {
        int err;
-       struct resource *res;
-
-       res = platform_get_resource(pdev, IORESOURCE_IO, 0);
-       if (!request_region(res->start, res->end - res->start + 1, DRVNAME))
-               return -EBUSY;
 
        /* Reset hardware to a sane state (SCL and SDA high) */
        parport_setsda(NULL, 1);
@@ -138,29 +133,19 @@ static int __devinit i2c_parport_probe(struct platform_device *pdev)
 
        parport_adapter.dev.parent = &pdev->dev;
        err = i2c_bit_add_bus(&parport_adapter);
-       if (err) {
+       if (err)
                dev_err(&pdev->dev, "Unable to register with I2C\n");
-               goto exit_region;
-       }
-       return 0;
-
-exit_region:
-       release_region(res->start, res->end - res->start + 1);
        return err;
 }
 
 static int __devexit i2c_parport_remove(struct platform_device *pdev)
 {
-       struct resource *res;
-
        i2c_del_adapter(&parport_adapter);
 
        /* Un-init if needed (power off...) */
        if (adapter_parm[type].init.val)
                line_set(0, &adapter_parm[type].init);
 
-       res = platform_get_resource(pdev, IORESOURCE_IO, 0);
-       release_region(res->start, res->end - res->start + 1);
        return 0;
 }
 
@@ -175,12 +160,6 @@ static struct platform_driver i2c_parport_driver = {
 
 static int __init i2c_parport_device_add(u16 address)
 {
-       struct resource res = {
-               .start  = address,
-               .end    = address + 2,
-               .name   = DRVNAME,
-               .flags  = IORESOURCE_IO,
-       };
        int err;
 
        pdev = platform_device_alloc(DRVNAME, -1);
@@ -190,13 +169,6 @@ static int __init i2c_parport_device_add(u16 address)
                goto exit;
        }
 
-       err = platform_device_add_resources(pdev, &res, 1);
-       if (err) {
-               printk(KERN_ERR DRVNAME ": Device resource addition failed "
-                      "(%d)\n", err);
-               goto exit_device_put;
-       }
-
        err = platform_device_add(pdev);
        if (err) {
                printk(KERN_ERR DRVNAME ": Device addition failed (%d)\n",
@@ -231,13 +203,16 @@ static int __init i2c_parport_init(void)
                base = DEFAULT_BASE;
        }
 
+       if (!request_region(base, 3, DRVNAME))
+               return -EBUSY;
+
         if (!adapter_parm[type].getscl.val)
                parport_algo_data.getscl = NULL;
 
        /* Sets global pdev as a side effect */
        err = i2c_parport_device_add(base);
        if (err)
-               goto exit;
+               goto exit_release;
 
        err = platform_driver_register(&i2c_parport_driver);
        if (err)
@@ -247,7 +222,8 @@ static int __init i2c_parport_init(void)
 
 exit_device:
        platform_device_unregister(pdev);
-exit:
+exit_release:
+       release_region(base, 3);
        return err;
 }
 
@@ -255,6 +231,7 @@ static void __exit i2c_parport_exit(void)
 {
        platform_driver_unregister(&i2c_parport_driver);
        platform_device_unregister(pdev);
+       release_region(base, 3);
 }
 
 MODULE_AUTHOR("Jean Delvare <khali@linux-fr.org>");
index a119784bae1069bee133d2b5f5f76ba1dd805f63..f80df9ae50541936925752804e632f901dcd4d3f 100644 (file)
@@ -36,8 +36,8 @@
 #define DRIVER "i2c-pca-isa"
 #define IO_SIZE 4
 
-static unsigned long base   = 0x330;
-static int irq           = 10;
+static unsigned long base;
+static int irq = -1;
 
 /* Data sheet recommends 59kHz for 100kHz operation due to variation
  * in the actual clock rate */
@@ -107,6 +107,19 @@ static struct i2c_adapter pca_isa_ops = {
        .timeout        = 100,
 };
 
+static int __devinit pca_isa_match(struct device *dev, unsigned int id)
+{
+       int match = base != 0;
+
+       if (match) {
+               if (irq <= -1)
+                       dev_warn(dev, "Using polling mode (specify irq)\n");
+       } else
+               dev_err(dev, "Please specify I/O base\n");
+
+       return match;
+}
+
 static int __devinit pca_isa_probe(struct device *dev, unsigned int id)
 {
        init_waitqueue_head(&pca_wait);
@@ -153,7 +166,7 @@ static int __devexit pca_isa_remove(struct device *dev, unsigned int id)
 {
        i2c_del_adapter(&pca_isa_ops);
 
-       if (irq > 0) {
+       if (irq > -1) {
                disable_irq(irq);
                free_irq(irq, &pca_isa_ops);
        }
@@ -163,6 +176,7 @@ static int __devexit pca_isa_remove(struct device *dev, unsigned int id)
 }
 
 static struct isa_driver pca_isa_driver = {
+       .match          = pca_isa_match,
        .probe          = pca_isa_probe,
        .remove         = __devexit_p(pca_isa_remove),
        .driver = {
index 862eb352a2d92b9708cdad16adfec687fdc4dece..73dc52e114eb00009d2b87e43250ce03d233ac17 100644 (file)
@@ -36,6 +36,7 @@
    VT8237S            0x3372             yes
    VT8251             0x3287             yes
    CX700              0x8324             yes
+   VX800/VX820        0x8353             yes
 
    Note: we assume there can only be one device, with one SMBus interface.
 */
@@ -82,6 +83,7 @@ static unsigned short SMBHSTCFG = 0xD2;
 #define VT596_BYTE             0x04
 #define VT596_BYTE_DATA                0x08
 #define VT596_WORD_DATA                0x0C
+#define VT596_PROC_CALL                0x10
 #define VT596_BLOCK_DATA       0x14
 #define VT596_I2C_BLOCK_DATA   0x34
 
@@ -232,6 +234,12 @@ static s32 vt596_access(struct i2c_adapter *adap, u16 addr,
                }
                size = VT596_WORD_DATA;
                break;
+       case I2C_SMBUS_PROC_CALL:
+               outb_p(command, SMBHSTCMD);
+               outb_p(data->word & 0xff, SMBHSTDAT0);
+               outb_p((data->word & 0xff00) >> 8, SMBHSTDAT1);
+               size = VT596_PROC_CALL;
+               break;
        case I2C_SMBUS_I2C_BLOCK_DATA:
                if (!(vt596_features & FEATURE_I2CBLOCK))
                        goto exit_unsupported;
@@ -262,6 +270,9 @@ static s32 vt596_access(struct i2c_adapter *adap, u16 addr,
        if (status)
                return status;
 
+       if (size == VT596_PROC_CALL)
+               read_write = I2C_SMBUS_READ;
+
        if ((read_write == I2C_SMBUS_WRITE) || (size == VT596_QUICK))
                return 0;
 
@@ -271,6 +282,7 @@ static s32 vt596_access(struct i2c_adapter *adap, u16 addr,
                data->byte = inb_p(SMBHSTDAT0);
                break;
        case VT596_WORD_DATA:
+       case VT596_PROC_CALL:
                data->word = inb_p(SMBHSTDAT0) + (inb_p(SMBHSTDAT1) << 8);
                break;
        case VT596_I2C_BLOCK_DATA:
@@ -295,7 +307,7 @@ static u32 vt596_func(struct i2c_adapter *adapter)
 {
        u32 func = I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE |
            I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA |
-           I2C_FUNC_SMBUS_BLOCK_DATA;
+           I2C_SMBUS_PROC_CALL | I2C_FUNC_SMBUS_BLOCK_DATA;
 
        if (vt596_features & FEATURE_I2CBLOCK)
                func |= I2C_FUNC_SMBUS_I2C_BLOCK;
@@ -396,6 +408,7 @@ found:
 
        switch (pdev->device) {
        case PCI_DEVICE_ID_VIA_CX700:
+       case PCI_DEVICE_ID_VIA_VX800:
        case PCI_DEVICE_ID_VIA_8251:
        case PCI_DEVICE_ID_VIA_8237:
        case PCI_DEVICE_ID_VIA_8237A:
@@ -459,6 +472,8 @@ static struct pci_device_id vt596_ids[] = {
          .driver_data = SMBBA3 },
        { PCI_DEVICE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_CX700),
          .driver_data = SMBBA3 },
+       { PCI_DEVICE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_VX800),
+         .driver_data = SMBBA3 },
        { 0, }
 };
 
index 4655b794ebe3c7d06ad911cf898c25a5061b03d0..28902ebd553905aedd2507d32870d97ea7023ade 100644 (file)
@@ -49,10 +49,9 @@ MODULE_LICENSE("GPL");
 
 struct isp1301 {
        struct otg_transceiver  otg;
-       struct i2c_client       client;
+       struct i2c_client       *client;
        void                    (*i2c_release)(struct device *dev);
 
-       int                     irq;
        int                     irq_type;
 
        u32                     last_otg_ctrl;
@@ -138,14 +137,6 @@ static inline void notresponding(struct isp1301 *isp)
 
 /*-------------------------------------------------------------------------*/
 
-/* only two addresses possible */
-#define        ISP_BASE                0x2c
-static unsigned short normal_i2c[] = {
-       ISP_BASE, ISP_BASE + 1,
-       I2C_CLIENT_END };
-
-I2C_CLIENT_INSMOD;
-
 static struct i2c_driver isp1301_driver;
 
 /* smbus apis are used for portability */
@@ -153,25 +144,25 @@ static struct i2c_driver isp1301_driver;
 static inline u8
 isp1301_get_u8(struct isp1301 *isp, u8 reg)
 {
-       return i2c_smbus_read_byte_data(&isp->client, reg + 0);
+       return i2c_smbus_read_byte_data(isp->client, reg + 0);
 }
 
 static inline int
 isp1301_get_u16(struct isp1301 *isp, u8 reg)
 {
-       return i2c_smbus_read_word_data(&isp->client, reg);
+       return i2c_smbus_read_word_data(isp->client, reg);
 }
 
 static inline int
 isp1301_set_bits(struct isp1301 *isp, u8 reg, u8 bits)
 {
-       return i2c_smbus_write_byte_data(&isp->client, reg + 0, bits);
+       return i2c_smbus_write_byte_data(isp->client, reg + 0, bits);
 }
 
 static inline int
 isp1301_clear_bits(struct isp1301 *isp, u8 reg, u8 bits)
 {
-       return i2c_smbus_write_byte_data(&isp->client, reg + 1, bits);
+       return i2c_smbus_write_byte_data(isp->client, reg + 1, bits);
 }
 
 /*-------------------------------------------------------------------------*/
@@ -349,10 +340,10 @@ isp1301_defer_work(struct isp1301 *isp, int work)
        int status;
 
        if (isp && !test_and_set_bit(work, &isp->todo)) {
-               (void) get_device(&isp->client.dev);
+               (void) get_device(&isp->client->dev);
                status = schedule_work(&isp->work);
                if (!status && !isp->working)
-                       dev_vdbg(&isp->client.dev,
+                       dev_vdbg(&isp->client->dev,
                                "work item %d may be lost\n", work);
        }
 }
@@ -1135,7 +1126,7 @@ isp1301_work(struct work_struct *work)
                /* transfer state from otg engine to isp1301 */
                if (test_and_clear_bit(WORK_UPDATE_ISP, &isp->todo)) {
                        otg_update_isp(isp);
-                       put_device(&isp->client.dev);
+                       put_device(&isp->client->dev);
                }
 #endif
                /* transfer state from isp1301 to otg engine */
@@ -1143,7 +1134,7 @@ isp1301_work(struct work_struct *work)
                        u8              stat = isp1301_clear_latch(isp);
 
                        isp_update_otg(isp, stat);
-                       put_device(&isp->client.dev);
+                       put_device(&isp->client->dev);
                }
 
                if (test_and_clear_bit(WORK_HOST_RESUME, &isp->todo)) {
@@ -1178,7 +1169,7 @@ isp1301_work(struct work_struct *work)
                        }
                        host_resume(isp);
                        // mdelay(10);
-                       put_device(&isp->client.dev);
+                       put_device(&isp->client->dev);
                }
 
                if (test_and_clear_bit(WORK_TIMER, &isp->todo)) {
@@ -1187,15 +1178,15 @@ isp1301_work(struct work_struct *work)
                        if (!stop)
                                mod_timer(&isp->timer, jiffies + TIMER_JIFFIES);
 #endif
-                       put_device(&isp->client.dev);
+                       put_device(&isp->client->dev);
                }
 
                if (isp->todo)
-                       dev_vdbg(&isp->client.dev,
+                       dev_vdbg(&isp->client->dev,
                                "work done, todo = 0x%lx\n",
                                isp->todo);
                if (stop) {
-                       dev_dbg(&isp->client.dev, "stop\n");
+                       dev_dbg(&isp->client->dev, "stop\n");
                        break;
                }
        } while (isp->todo);
@@ -1219,7 +1210,7 @@ static void isp1301_release(struct device *dev)
 {
        struct isp1301  *isp;
 
-       isp = container_of(dev, struct isp1301, client.dev);
+       isp = dev_get_drvdata(dev);
 
        /* ugly -- i2c hijacks our memory hook to wait_for_completion() */
        if (isp->i2c_release)
@@ -1229,15 +1220,15 @@ static void isp1301_release(struct device *dev)
 
 static struct isp1301 *the_transceiver;
 
-static int isp1301_detach_client(struct i2c_client *i2c)
+static int __exit isp1301_remove(struct i2c_client *i2c)
 {
        struct isp1301  *isp;
 
-       isp = container_of(i2c, struct isp1301, client);
+       isp = i2c_get_clientdata(i2c);
 
        isp1301_clear_bits(isp, ISP1301_INTERRUPT_FALLING, ~0);
        isp1301_clear_bits(isp, ISP1301_INTERRUPT_RISING, ~0);
-       free_irq(isp->irq, isp);
+       free_irq(i2c->irq, isp);
 #ifdef CONFIG_USB_OTG
        otg_unbind(isp);
 #endif
@@ -1252,7 +1243,7 @@ static int isp1301_detach_client(struct i2c_client *i2c)
        put_device(&i2c->dev);
        the_transceiver = 0;
 
-       return i2c_detach_client(i2c);
+       return 0;
 }
 
 /*-------------------------------------------------------------------------*/
@@ -1285,7 +1276,7 @@ static int isp1301_otg_enable(struct isp1301 *isp)
        isp1301_set_bits(isp, ISP1301_INTERRUPT_FALLING,
                INTR_VBUS_VLD | INTR_SESS_VLD | INTR_ID_GND);
 
-       dev_info(&isp->client.dev, "ready for dual-role USB ...\n");
+       dev_info(&isp->client->dev, "ready for dual-role USB ...\n");
 
        return 0;
 }
@@ -1310,7 +1301,7 @@ isp1301_set_host(struct otg_transceiver *otg, struct usb_bus *host)
 
 #ifdef CONFIG_USB_OTG
        isp->otg.host = host;
-       dev_dbg(&isp->client.dev, "registered host\n");
+       dev_dbg(&isp->client->dev, "registered host\n");
        host_suspend(isp);
        if (isp->otg.gadget)
                return isp1301_otg_enable(isp);
@@ -1325,7 +1316,7 @@ isp1301_set_host(struct otg_transceiver *otg, struct usb_bus *host)
        if (machine_is_omap_h2())
                isp1301_set_bits(isp, ISP1301_MODE_CONTROL_1, MC1_DAT_SE0);
 
-       dev_info(&isp->client.dev, "A-Host sessions ok\n");
+       dev_info(&isp->client->dev, "A-Host sessions ok\n");
        isp1301_set_bits(isp, ISP1301_INTERRUPT_RISING,
                INTR_ID_GND);
        isp1301_set_bits(isp, ISP1301_INTERRUPT_FALLING,
@@ -1343,7 +1334,7 @@ isp1301_set_host(struct otg_transceiver *otg, struct usb_bus *host)
        return 0;
 
 #else
-       dev_dbg(&isp->client.dev, "host sessions not allowed\n");
+       dev_dbg(&isp->client->dev, "host sessions not allowed\n");
        return -EINVAL;
 #endif
 
@@ -1370,7 +1361,7 @@ isp1301_set_peripheral(struct otg_transceiver *otg, struct usb_gadget *gadget)
 
 #ifdef CONFIG_USB_OTG
        isp->otg.gadget = gadget;
-       dev_dbg(&isp->client.dev, "registered gadget\n");
+       dev_dbg(&isp->client->dev, "registered gadget\n");
        /* gadget driver may be suspended until vbus_connect () */
        if (isp->otg.host)
                return isp1301_otg_enable(isp);
@@ -1395,7 +1386,7 @@ isp1301_set_peripheral(struct otg_transceiver *otg, struct usb_gadget *gadget)
                INTR_SESS_VLD);
        isp1301_set_bits(isp, ISP1301_INTERRUPT_FALLING,
                INTR_VBUS_VLD);
-       dev_info(&isp->client.dev, "B-Peripheral sessions ok\n");
+       dev_info(&isp->client->dev, "B-Peripheral sessions ok\n");
        dump_regs(isp, __func__);
 
        /* If this has a Mini-AB connector, this mode is highly
@@ -1408,7 +1399,7 @@ isp1301_set_peripheral(struct otg_transceiver *otg, struct usb_gadget *gadget)
        return 0;
 
 #else
-       dev_dbg(&isp->client.dev, "peripheral sessions not allowed\n");
+       dev_dbg(&isp->client->dev, "peripheral sessions not allowed\n");
        return -EINVAL;
 #endif
 }
@@ -1508,12 +1499,10 @@ isp1301_start_hnp(struct otg_transceiver *dev)
 
 /*-------------------------------------------------------------------------*/
 
-/* no error returns, they'd just make bus scanning stop */
-static int isp1301_probe(struct i2c_adapter *bus, int address, int kind)
+static int __init isp1301_probe(struct i2c_client *i2c)
 {
        int                     status;
        struct isp1301          *isp;
-       struct i2c_client       *i2c;
 
        if (the_transceiver)
                return 0;
@@ -1527,37 +1516,19 @@ static int isp1301_probe(struct i2c_adapter *bus, int address, int kind)
        isp->timer.function = isp1301_timer;
        isp->timer.data = (unsigned long) isp;
 
-       isp->irq = -1;
-       isp->client.addr = address;
-       i2c_set_clientdata(&isp->client, isp);
-       isp->client.adapter = bus;
-       isp->client.driver = &isp1301_driver;
-       strlcpy(isp->client.name, DRIVER_NAME, I2C_NAME_SIZE);
-       i2c = &isp->client;
-
-       /* if this is a true probe, verify the chip ... */
-       if (kind < 0) {
-               status = isp1301_get_u16(isp, ISP1301_VENDOR_ID);
-               if (status != I2C_VENDOR_ID_PHILIPS) {
-                       dev_dbg(&bus->dev, "addr %d not philips id: %d\n",
-                               address, status);
-                       goto fail1;
-               }
-               status = isp1301_get_u16(isp, ISP1301_PRODUCT_ID);
-               if (status != I2C_PRODUCT_ID_PHILIPS_1301) {
-                       dev_dbg(&bus->dev, "%d not isp1301, %d\n",
-                               address, status);
-                       goto fail1;
-               }
-       }
+       i2c_set_clientdata(i2c, isp);
+       isp->client = i2c;
 
-       status = i2c_attach_client(i2c);
-       if (status < 0) {
-               dev_dbg(&bus->dev, "can't attach %s to device %d, err %d\n",
-                               DRIVER_NAME, address, status);
-fail1:
-               kfree(isp);
-               return 0;
+       /* verify the chip (shouldn't be necesary) */
+       status = isp1301_get_u16(isp, ISP1301_VENDOR_ID);
+       if (status != I2C_VENDOR_ID_PHILIPS) {
+               dev_dbg(&i2c->dev, "not philips id: %d\n", status);
+               goto fail;
+       }
+       status = isp1301_get_u16(isp, ISP1301_PRODUCT_ID);
+       if (status != I2C_PRODUCT_ID_PHILIPS_1301) {
+               dev_dbg(&i2c->dev, "not isp1301, %d\n", status);
+               goto fail;
        }
        isp->i2c_release = i2c->dev.release;
        i2c->dev.release = isp1301_release;
@@ -1586,7 +1557,7 @@ fail1:
        status = otg_bind(isp);
        if (status < 0) {
                dev_dbg(&i2c->dev, "can't bind OTG\n");
-               goto fail2;
+               goto fail;
        }
 #endif
 
@@ -1599,26 +1570,21 @@ fail1:
 
                /* IRQ wired at M14 */
                omap_cfg_reg(M14_1510_GPIO2);
-               isp->irq = OMAP_GPIO_IRQ(2);
                if (gpio_request(2, "isp1301") == 0)
                        gpio_direction_input(2);
                isp->irq_type = IRQF_TRIGGER_FALLING;
        }
 
        isp->irq_type |= IRQF_SAMPLE_RANDOM;
-       status = request_irq(isp->irq, isp1301_irq,
+       status = request_irq(i2c->irq, isp1301_irq,
                        isp->irq_type, DRIVER_NAME, isp);
        if (status < 0) {
                dev_dbg(&i2c->dev, "can't get IRQ %d, err %d\n",
-                               isp->irq, status);
-#ifdef CONFIG_USB_OTG
-fail2:
-#endif
-               i2c_detach_client(i2c);
-               goto fail1;
+                               i2c->irq, status);
+               goto fail;
        }
 
-       isp->otg.dev = &isp->client.dev;
+       isp->otg.dev = &i2c->dev;
        isp->otg.label = DRIVER_NAME;
 
        isp->otg.set_host = isp1301_set_host,
@@ -1649,22 +1615,25 @@ fail2:
                        status);
 
        return 0;
-}
 
-static int isp1301_scan_bus(struct i2c_adapter *bus)
-{
-       if (!i2c_check_functionality(bus, I2C_FUNC_SMBUS_BYTE_DATA
-                       | I2C_FUNC_SMBUS_READ_WORD_DATA))
-               return -EINVAL;
-       return i2c_probe(bus, &addr_data, isp1301_probe);
+fail:
+       kfree(isp);
+       return -ENODEV;
 }
 
+static const struct i2c_device_id isp1301_id[] = {
+       { "isp1301_omap", 0 },
+       { }
+};
+MODULE_DEVICE_TABLE(i2c, isp1301_id);
+
 static struct i2c_driver isp1301_driver = {
        .driver = {
                .name   = "isp1301_omap",
        },
-       .attach_adapter = isp1301_scan_bus,
-       .detach_client  = isp1301_detach_client,
+       .probe          = isp1301_probe,
+       .remove         = __exit_p(isp1301_remove),
+       .id_table       = isp1301_id,
 };
 
 /*-------------------------------------------------------------------------*/
index cf02e8fceb42a7a1ef4ee91c42d0fb8cfdd14ecb..acf8b9d5f575fec8cfe3d2578ab909c0956ce178 100644 (file)
@@ -456,14 +456,17 @@ static irqreturn_t tps65010_irq(int irq, void *_tps)
 
 /* offsets 0..3 == GPIO1..GPIO4
  * offsets 4..5 == LED1/nPG, LED2 (we set one of the non-BLINK modes)
+ * offset 6 == vibrator motor driver
  */
 static void
 tps65010_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
 {
        if (offset < 4)
                tps65010_set_gpio_out_value(offset + 1, value);
-       else
+       else if (offset < 6)
                tps65010_set_led(offset - 3, value ? ON : OFF);
+       else
+               tps65010_set_vib(value);
 }
 
 static int
@@ -477,8 +480,10 @@ tps65010_output(struct gpio_chip *chip, unsigned offset, int value)
                if (!(tps->outmask & (1 << offset)))
                        return -EINVAL;
                tps65010_set_gpio_out_value(offset + 1, value);
-       } else
+       } else if (offset < 6)
                tps65010_set_led(offset - 3, value ? ON : OFF);
+       else
+               tps65010_set_vib(value);
 
        return 0;
 }
@@ -646,7 +651,7 @@ static int tps65010_probe(struct i2c_client *client,
                tps->chip.get = tps65010_gpio_get;
 
                tps->chip.base = board->base;
-               tps->chip.ngpio = 6;
+               tps->chip.ngpio = 7;
                tps->chip.can_sleep = 1;
 
                status = gpiochip_add(&tps->chip);
@@ -675,6 +680,7 @@ static const struct i2c_device_id tps65010_id[] = {
        { "tps65011", TPS65011 },
        { "tps65012", TPS65012 },
        { "tps65013", TPS65013 },
+       { "tps65014", TPS65011 },       /* tps65011 charging at 6.5V max */
        { }
 };
 MODULE_DEVICE_TABLE(i2c, tps65010_id);
index b346a687ab593a89834270e64b91f2b8b1c4c66f..42e852d79ffaa5b001dda2c44219cad064a537e7 100644 (file)
@@ -437,6 +437,10 @@ static int i2c_register_adapter(struct i2c_adapter *adap)
 {
        int res = 0, dummy;
 
+       /* Can't register until after driver model init */
+       if (unlikely(WARN_ON(!i2c_bus_type.p)))
+               return -EAGAIN;
+
        mutex_init(&adap->bus_lock);
        mutex_init(&adap->clist_lock);
        INIT_LIST_HEAD(&adap->clients);
@@ -696,6 +700,10 @@ int i2c_register_driver(struct module *owner, struct i2c_driver *driver)
 {
        int res;
 
+       /* Can't register until after driver model init */
+       if (unlikely(WARN_ON(!i2c_bus_type.p)))
+               return -EAGAIN;
+
        /* new style driver methods can't mix with legacy ones */
        if (is_newstyle_driver(driver)) {
                if (driver->attach_adapter || driver->detach_adapter
@@ -978,7 +986,10 @@ static void __exit i2c_exit(void)
        bus_unregister(&i2c_bus_type);
 }
 
-subsys_initcall(i2c_init);
+/* We must initialize early, because some subsystems register i2c drivers
+ * in subsys_initcall() code, but are linked (and initialized) before i2c.
+ */
+postcore_initcall(i2c_init);
 module_exit(i2c_exit);
 
 /* ----------------------------------------------------
@@ -1676,6 +1687,28 @@ s32 i2c_smbus_write_word_data(struct i2c_client *client, u8 command, u16 value)
 }
 EXPORT_SYMBOL(i2c_smbus_write_word_data);
 
+/**
+ * i2c_smbus_process_call - SMBus "process call" protocol
+ * @client: Handle to slave device
+ * @command: Byte interpreted by slave
+ * @value: 16-bit "word" being written
+ *
+ * This executes the SMBus "process call" protocol, returning negative errno
+ * else a 16-bit unsigned "word" received from the device.
+ */
+s32 i2c_smbus_process_call(struct i2c_client *client, u8 command, u16 value)
+{
+       union i2c_smbus_data data;
+       int status;
+       data.word = value;
+
+       status = i2c_smbus_xfer(client->adapter, client->addr, client->flags,
+                               I2C_SMBUS_WRITE, command,
+                               I2C_SMBUS_PROC_CALL, &data);
+       return (status < 0) ? status : data.word;
+}
+EXPORT_SYMBOL(i2c_smbus_process_call);
+
 /**
  * i2c_smbus_read_block_data - SMBus "block read" protocol
  * @client: Handle to slave device
index daa9d4220331b06e68b9100caad9c5e98065a48f..82ec6b1b646782276363f769bc73e96226237459 100644 (file)
@@ -458,35 +458,35 @@ static int hp_sdc_rtc_proc_output (char *buf)
                p += sprintf(p, "i8042 rtc\t: READ FAILED!\n");
        } else {
                p += sprintf(p, "i8042 rtc\t: %ld.%02d seconds\n", 
-                            tv.tv_sec, tv.tv_usec/1000);
+                            tv.tv_sec, (int)tv.tv_usec/1000);
        }
 
        if (hp_sdc_rtc_read_fhs(&tv)) {
                p += sprintf(p, "handshake\t: READ FAILED!\n");
        } else {
                p += sprintf(p, "handshake\t: %ld.%02d seconds\n", 
-                            tv.tv_sec, tv.tv_usec/1000);
+                            tv.tv_sec, (int)tv.tv_usec/1000);
        }
 
        if (hp_sdc_rtc_read_mt(&tv)) {
                p += sprintf(p, "alarm\t\t: READ FAILED!\n");
        } else {
                p += sprintf(p, "alarm\t\t: %ld.%02d seconds\n", 
-                            tv.tv_sec, tv.tv_usec/1000);
+                            tv.tv_sec, (int)tv.tv_usec/1000);
        }
 
        if (hp_sdc_rtc_read_dt(&tv)) {
                p += sprintf(p, "delay\t\t: READ FAILED!\n");
        } else {
                p += sprintf(p, "delay\t\t: %ld.%02d seconds\n", 
-                            tv.tv_sec, tv.tv_usec/1000);
+                            tv.tv_sec, (int)tv.tv_usec/1000);
        }
 
        if (hp_sdc_rtc_read_ct(&tv)) {
                p += sprintf(p, "periodic\t: READ FAILED!\n");
        } else {
                p += sprintf(p, "periodic\t: %ld.%02d seconds\n", 
-                            tv.tv_sec, tv.tv_usec/1000);
+                            tv.tv_sec, (int)tv.tv_usec/1000);
        }
 
         p += sprintf(p,
index 0d395979b2d19c2a35ddb31e6d8c915c363fcfd9..bfe49243f38bf571d5d99e2f36c3d42b569ea6f3 100644 (file)
@@ -323,7 +323,7 @@ static void hp_sdc_tasklet(unsigned long foo)
                         * it back to the application. and be less verbose.
                         */
                        printk(KERN_WARNING PREFIX "read timeout (%ius)!\n",
-                              tv.tv_usec - hp_sdc.rtv.tv_usec);
+                              (int)(tv.tv_usec - hp_sdc.rtv.tv_usec));
                        curr->idx += hp_sdc.rqty;
                        hp_sdc.rqty = 0;
                        tmp = curr->seq[curr->actidx];
index e92b1ba4b45eb836d83ef101564c76311644ed2e..c2f51cc507608bada189220168178d05b34f908a 100644 (file)
@@ -452,10 +452,10 @@ one_member:
                        if (finddsp->features.pcm_id == dsp->features.pcm_id) {
                                if (finddsp->pcm_slot_rx >= 0 &&
                                    finddsp->pcm_slot_rx < sizeof(freeslots))
-                                       freeslots[finddsp->pcm_slot_tx] = 0;
+                                       freeslots[finddsp->pcm_slot_rx] = 0;
                                if (finddsp->pcm_slot_tx >= 0 &&
                                    finddsp->pcm_slot_tx < sizeof(freeslots))
-                                       freeslots[finddsp->pcm_slot_rx] = 0;
+                                       freeslots[finddsp->pcm_slot_tx] = 0;
                        }
                }
                i = 0;
index e7462924b5050fb70cad3ddfd9f607368d61e774..875fabe16e368c32b87f1a2e697accf45369eac6 100644 (file)
@@ -61,7 +61,7 @@ mISDN_open(struct inode *ino, struct file *filep)
        init_waitqueue_head(&dev->wait);
        filep->private_data = dev;
        __module_get(THIS_MODULE);
-       return 0;
+       return nonseekable_open(ino, filep);
 }
 
 static int
index 07d92c11b5d8d935887f0b8361626faaf1abfd46..2281b5098e95455cd1a6d60af7354f7728888185 100644 (file)
@@ -30,6 +30,20 @@ config BLK_DEV_MD
 
          If unsure, say N.
 
+config MD_AUTODETECT
+       bool "Autodetect RAID arrays during kernel boot"
+       depends on BLK_DEV_MD=y
+       default y
+       ---help---
+         If you say Y here, then the kernel will try to autodetect raid
+         arrays as part of its boot process. 
+
+         If you don't use raid and say Y, this autodetection can cause 
+         a several-second delay in the boot time due to various
+         synchronisation steps that are part of this step.
+
+         If unsure, say Y.
+
 config MD_LINEAR
        tristate "Linear (append) mode"
        depends on BLK_DEV_MD
index 0dae245c625953fa2194f643d580db06f00a3aac..5eff8ad834d6f6bae7da3e97a89a3a6c87a336fa 100644 (file)
@@ -87,6 +87,44 @@ config MFD_TC6393XB
        help
          Support for Toshiba Mobile IO Controller TC6393XB
 
+config MFD_WM8400
+       tristate "Support Wolfson Microelectronics WM8400"
+       help
+         Support for the Wolfson Microelecronics WM8400 PMIC and audio
+         CODEC.  This driver adds provides common support for accessing
+         the device, additional drivers must be enabled in order to use
+         the functionality of the device.
+
+config MFD_WM8350
+       tristate
+
+config MFD_WM8350_CONFIG_MODE_0
+       bool
+       depends on MFD_WM8350
+
+config MFD_WM8350_CONFIG_MODE_1
+       bool
+       depends on MFD_WM8350
+
+config MFD_WM8350_CONFIG_MODE_2
+       bool
+       depends on MFD_WM8350
+
+config MFD_WM8350_CONFIG_MODE_3
+       bool
+       depends on MFD_WM8350
+
+config MFD_WM8350_I2C
+       tristate "Support Wolfson Microelectronics WM8350 with I2C"
+       select MFD_WM8350
+       depends on I2C
+       help
+         The WM8350 is an integrated audio and power management
+         subsystem with watchdog and RTC functionality for embedded
+         systems.  This option enables core support for the WM8350 with
+         I2C as the control interface.  Additional options must be
+         selected to enable support for the functionality of the chip.
+
 endmenu
 
 menu "Multimedia Capabilities Port drivers"
index 6abebe364419ed9668f0e08ea68abf0c362a7733..759b1fe1c891a44bac2c5486ea51da90d1de0985 100644 (file)
@@ -12,6 +12,11 @@ obj-$(CONFIG_MFD_T7L66XB)    += t7l66xb.o
 obj-$(CONFIG_MFD_TC6387XB)     += tc6387xb.o
 obj-$(CONFIG_MFD_TC6393XB)     += tc6393xb.o
 
+obj-$(CONFIG_MFD_WM8400)       += wm8400-core.o
+wm8350-objs                    := wm8350-core.o wm8350-regmap.o wm8350-gpio.o
+obj-$(CONFIG_MFD_WM8350)       += wm8350.o
+obj-$(CONFIG_MFD_WM8350_I2C)   += wm8350-i2c.o
+
 obj-$(CONFIG_MFD_CORE)         += mfd-core.o
 
 obj-$(CONFIG_MCP)              += mcp-core.o
diff --git a/drivers/mfd/wm8350-core.c b/drivers/mfd/wm8350-core.c
new file mode 100644 (file)
index 0000000..25a7a5d
--- /dev/null
@@ -0,0 +1,1273 @@
+/*
+ * wm8350-core.c  --  Device access for Wolfson WM8350
+ *
+ * Copyright 2007, 2008 Wolfson Microelectronics PLC.
+ *
+ * Author: Liam Girdwood, Mark Brown
+ *
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/bug.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/workqueue.h>
+
+#include <linux/mfd/wm8350/core.h>
+#include <linux/mfd/wm8350/audio.h>
+#include <linux/mfd/wm8350/comparator.h>
+#include <linux/mfd/wm8350/gpio.h>
+#include <linux/mfd/wm8350/pmic.h>
+#include <linux/mfd/wm8350/rtc.h>
+#include <linux/mfd/wm8350/supply.h>
+#include <linux/mfd/wm8350/wdt.h>
+
+#define WM8350_UNLOCK_KEY              0x0013
+#define WM8350_LOCK_KEY                        0x0000
+
+#define WM8350_CLOCK_CONTROL_1         0x28
+#define WM8350_AIF_TEST                        0x74
+
+/* debug */
+#define WM8350_BUS_DEBUG 0
+#if WM8350_BUS_DEBUG
+#define dump(regs, src) do { \
+       int i_; \
+       u16 *src_ = src; \
+       printk(KERN_DEBUG); \
+       for (i_ = 0; i_ < regs; i_++) \
+               printk(" 0x%4.4x", *src_++); \
+       printk("\n"); \
+} while (0);
+#else
+#define dump(bytes, src)
+#endif
+
+#define WM8350_LOCK_DEBUG 0
+#if WM8350_LOCK_DEBUG
+#define ldbg(format, arg...) printk(format, ## arg)
+#else
+#define ldbg(format, arg...)
+#endif
+
+/*
+ * WM8350 Device IO
+ */
+static DEFINE_MUTEX(io_mutex);
+static DEFINE_MUTEX(reg_lock_mutex);
+static DEFINE_MUTEX(auxadc_mutex);
+
+/* Perform a physical read from the device.
+ */
+static int wm8350_phys_read(struct wm8350 *wm8350, u8 reg, int num_regs,
+                           u16 *dest)
+{
+       int i, ret;
+       int bytes = num_regs * 2;
+
+       dev_dbg(wm8350->dev, "volatile read\n");
+       ret = wm8350->read_dev(wm8350, reg, bytes, (char *)dest);
+
+       for (i = reg; i < reg + num_regs; i++) {
+               /* Cache is CPU endian */
+               dest[i - reg] = be16_to_cpu(dest[i - reg]);
+
+               /* Satisfy non-volatile bits from cache */
+               dest[i - reg] &= wm8350_reg_io_map[i].vol;
+               dest[i - reg] |= wm8350->reg_cache[i];
+
+               /* Mask out non-readable bits */
+               dest[i - reg] &= wm8350_reg_io_map[i].readable;
+       }
+
+       dump(num_regs, dest);
+
+       return ret;
+}
+
+static int wm8350_read(struct wm8350 *wm8350, u8 reg, int num_regs, u16 *dest)
+{
+       int i;
+       int end = reg + num_regs;
+       int ret = 0;
+       int bytes = num_regs * 2;
+
+       if (wm8350->read_dev == NULL)
+               return -ENODEV;
+
+       if ((reg + num_regs - 1) > WM8350_MAX_REGISTER) {
+               dev_err(wm8350->dev, "invalid reg %x\n",
+                       reg + num_regs - 1);
+               return -EINVAL;
+       }
+
+       dev_dbg(wm8350->dev,
+               "%s R%d(0x%2.2x) %d regs\n", __func__, reg, reg, num_regs);
+
+#if WM8350_BUS_DEBUG
+       /* we can _safely_ read any register, but warn if read not supported */
+       for (i = reg; i < end; i++) {
+               if (!wm8350_reg_io_map[i].readable)
+                       dev_warn(wm8350->dev,
+                               "reg R%d is not readable\n", i);
+       }
+#endif
+
+       /* if any volatile registers are required, then read back all */
+       for (i = reg; i < end; i++)
+               if (wm8350_reg_io_map[i].vol)
+                       return wm8350_phys_read(wm8350, reg, num_regs, dest);
+
+       /* no volatiles, then cache is good */
+       dev_dbg(wm8350->dev, "cache read\n");
+       memcpy(dest, &wm8350->reg_cache[reg], bytes);
+       dump(num_regs, dest);
+       return ret;
+}
+
+static inline int is_reg_locked(struct wm8350 *wm8350, u8 reg)
+{
+       if (reg == WM8350_SECURITY ||
+           wm8350->reg_cache[WM8350_SECURITY] == WM8350_UNLOCK_KEY)
+               return 0;
+
+       if ((reg == WM8350_GPIO_CONFIGURATION_I_O) ||
+           (reg >= WM8350_GPIO_FUNCTION_SELECT_1 &&
+            reg <= WM8350_GPIO_FUNCTION_SELECT_4) ||
+           (reg >= WM8350_BATTERY_CHARGER_CONTROL_1 &&
+            reg <= WM8350_BATTERY_CHARGER_CONTROL_3))
+               return 1;
+       return 0;
+}
+
+static int wm8350_write(struct wm8350 *wm8350, u8 reg, int num_regs, u16 *src)
+{
+       int i;
+       int end = reg + num_regs;
+       int bytes = num_regs * 2;
+
+       if (wm8350->write_dev == NULL)
+               return -ENODEV;
+
+       if ((reg + num_regs - 1) > WM8350_MAX_REGISTER) {
+               dev_err(wm8350->dev, "invalid reg %x\n",
+                       reg + num_regs - 1);
+               return -EINVAL;
+       }
+
+       /* it's generally not a good idea to write to RO or locked registers */
+       for (i = reg; i < end; i++) {
+               if (!wm8350_reg_io_map[i].writable) {
+                       dev_err(wm8350->dev,
+                               "attempted write to read only reg R%d\n", i);
+                       return -EINVAL;
+               }
+
+               if (is_reg_locked(wm8350, i)) {
+                       dev_err(wm8350->dev,
+                              "attempted write to locked reg R%d\n", i);
+                       return -EINVAL;
+               }
+
+               src[i - reg] &= wm8350_reg_io_map[i].writable;
+
+               wm8350->reg_cache[i] =
+                       (wm8350->reg_cache[i] & ~wm8350_reg_io_map[i].writable)
+                       | src[i - reg];
+
+               src[i - reg] = cpu_to_be16(src[i - reg]);
+       }
+
+       /* Actually write it out */
+       return wm8350->write_dev(wm8350, reg, bytes, (char *)src);
+}
+
+/*
+ * Safe read, modify, write methods
+ */
+int wm8350_clear_bits(struct wm8350 *wm8350, u16 reg, u16 mask)
+{
+       u16 data;
+       int err;
+
+       mutex_lock(&io_mutex);
+       err = wm8350_read(wm8350, reg, 1, &data);
+       if (err) {
+               dev_err(wm8350->dev, "read from reg R%d failed\n", reg);
+               goto out;
+       }
+
+       data &= ~mask;
+       err = wm8350_write(wm8350, reg, 1, &data);
+       if (err)
+               dev_err(wm8350->dev, "write to reg R%d failed\n", reg);
+out:
+       mutex_unlock(&io_mutex);
+       return err;
+}
+EXPORT_SYMBOL_GPL(wm8350_clear_bits);
+
+int wm8350_set_bits(struct wm8350 *wm8350, u16 reg, u16 mask)
+{
+       u16 data;
+       int err;
+
+       mutex_lock(&io_mutex);
+       err = wm8350_read(wm8350, reg, 1, &data);
+       if (err) {
+               dev_err(wm8350->dev, "read from reg R%d failed\n", reg);
+               goto out;
+       }
+
+       data |= mask;
+       err = wm8350_write(wm8350, reg, 1, &data);
+       if (err)
+               dev_err(wm8350->dev, "write to reg R%d failed\n", reg);
+out:
+       mutex_unlock(&io_mutex);
+       return err;
+}
+EXPORT_SYMBOL_GPL(wm8350_set_bits);
+
+u16 wm8350_reg_read(struct wm8350 *wm8350, int reg)
+{
+       u16 data;
+       int err;
+
+       mutex_lock(&io_mutex);
+       err = wm8350_read(wm8350, reg, 1, &data);
+       if (err)
+               dev_err(wm8350->dev, "read from reg R%d failed\n", reg);
+
+       mutex_unlock(&io_mutex);
+       return data;
+}
+EXPORT_SYMBOL_GPL(wm8350_reg_read);
+
+int wm8350_reg_write(struct wm8350 *wm8350, int reg, u16 val)
+{
+       int ret;
+       u16 data = val;
+
+       mutex_lock(&io_mutex);
+       ret = wm8350_write(wm8350, reg, 1, &data);
+       if (ret)
+               dev_err(wm8350->dev, "write to reg R%d failed\n", reg);
+       mutex_unlock(&io_mutex);
+       return ret;
+}
+EXPORT_SYMBOL_GPL(wm8350_reg_write);
+
+int wm8350_block_read(struct wm8350 *wm8350, int start_reg, int regs,
+                     u16 *dest)
+{
+       int err = 0;
+
+       mutex_lock(&io_mutex);
+       err = wm8350_read(wm8350, start_reg, regs, dest);
+       if (err)
+               dev_err(wm8350->dev, "block read starting from R%d failed\n",
+                       start_reg);
+       mutex_unlock(&io_mutex);
+       return err;
+}
+EXPORT_SYMBOL_GPL(wm8350_block_read);
+
+int wm8350_block_write(struct wm8350 *wm8350, int start_reg, int regs,
+                      u16 *src)
+{
+       int ret = 0;
+
+       mutex_lock(&io_mutex);
+       ret = wm8350_write(wm8350, start_reg, regs, src);
+       if (ret)
+               dev_err(wm8350->dev, "block write starting at R%d failed\n",
+                       start_reg);
+       mutex_unlock(&io_mutex);
+       return ret;
+}
+EXPORT_SYMBOL_GPL(wm8350_block_write);
+
+int wm8350_reg_lock(struct wm8350 *wm8350)
+{
+       u16 key = WM8350_LOCK_KEY;
+       int ret;
+
+       ldbg(__func__);
+       mutex_lock(&io_mutex);
+       ret = wm8350_write(wm8350, WM8350_SECURITY, 1, &key);
+       if (ret)
+               dev_err(wm8350->dev, "lock failed\n");
+       mutex_unlock(&io_mutex);
+       return ret;
+}
+EXPORT_SYMBOL_GPL(wm8350_reg_lock);
+
+int wm8350_reg_unlock(struct wm8350 *wm8350)
+{
+       u16 key = WM8350_UNLOCK_KEY;
+       int ret;
+
+       ldbg(__func__);
+       mutex_lock(&io_mutex);
+       ret = wm8350_write(wm8350, WM8350_SECURITY, 1, &key);
+       if (ret)
+               dev_err(wm8350->dev, "unlock failed\n");
+       mutex_unlock(&io_mutex);
+       return ret;
+}
+EXPORT_SYMBOL_GPL(wm8350_reg_unlock);
+
+static void wm8350_irq_call_handler(struct wm8350 *wm8350, int irq)
+{
+       mutex_lock(&wm8350->irq_mutex);
+
+       if (wm8350->irq[irq].handler)
+               wm8350->irq[irq].handler(wm8350, irq, wm8350->irq[irq].data);
+       else {
+               dev_err(wm8350->dev, "irq %d nobody cared. now masked.\n",
+                       irq);
+               wm8350_mask_irq(wm8350, irq);
+       }
+
+       mutex_unlock(&wm8350->irq_mutex);
+}
+
+/*
+ * wm8350_irq_worker actually handles the interrupts.  Since all
+ * interrupts are clear on read the IRQ line will be reasserted and
+ * the physical IRQ will be handled again if another interrupt is
+ * asserted while we run - in the normal course of events this is a
+ * rare occurrence so we save I2C/SPI reads.
+ */
+static void wm8350_irq_worker(struct work_struct *work)
+{
+       struct wm8350 *wm8350 = container_of(work, struct wm8350, irq_work);
+       u16 level_one, status1, status2, comp;
+
+       /* TODO: Use block reads to improve performance? */
+       level_one = wm8350_reg_read(wm8350, WM8350_SYSTEM_INTERRUPTS)
+               & ~wm8350_reg_read(wm8350, WM8350_SYSTEM_INTERRUPTS_MASK);
+       status1 = wm8350_reg_read(wm8350, WM8350_INT_STATUS_1)
+               & ~wm8350_reg_read(wm8350, WM8350_INT_STATUS_1_MASK);
+       status2 = wm8350_reg_read(wm8350, WM8350_INT_STATUS_2)
+               & ~wm8350_reg_read(wm8350, WM8350_INT_STATUS_2_MASK);
+       comp = wm8350_reg_read(wm8350, WM8350_COMPARATOR_INT_STATUS)
+               & ~wm8350_reg_read(wm8350, WM8350_COMPARATOR_INT_STATUS_MASK);
+
+       /* over current */
+       if (level_one & WM8350_OC_INT) {
+               u16 oc;
+
+               oc = wm8350_reg_read(wm8350, WM8350_OVER_CURRENT_INT_STATUS);
+               oc &= ~wm8350_reg_read(wm8350,
+                                      WM8350_OVER_CURRENT_INT_STATUS_MASK);
+
+               if (oc & WM8350_OC_LS_EINT)     /* limit switch */
+                       wm8350_irq_call_handler(wm8350, WM8350_IRQ_OC_LS);
+       }
+
+       /* under voltage */
+       if (level_one & WM8350_UV_INT) {
+               u16 uv;
+
+               uv = wm8350_reg_read(wm8350, WM8350_UNDER_VOLTAGE_INT_STATUS);
+               uv &= ~wm8350_reg_read(wm8350,
+                                      WM8350_UNDER_VOLTAGE_INT_STATUS_MASK);
+
+               if (uv & WM8350_UV_DC1_EINT)
+                       wm8350_irq_call_handler(wm8350, WM8350_IRQ_UV_DC1);
+               if (uv & WM8350_UV_DC2_EINT)
+                       wm8350_irq_call_handler(wm8350, WM8350_IRQ_UV_DC2);
+               if (uv & WM8350_UV_DC3_EINT)
+                       wm8350_irq_call_handler(wm8350, WM8350_IRQ_UV_DC3);
+               if (uv & WM8350_UV_DC4_EINT)
+                       wm8350_irq_call_handler(wm8350, WM8350_IRQ_UV_DC4);
+               if (uv & WM8350_UV_DC5_EINT)
+                       wm8350_irq_call_handler(wm8350, WM8350_IRQ_UV_DC5);
+               if (uv & WM8350_UV_DC6_EINT)
+                       wm8350_irq_call_handler(wm8350, WM8350_IRQ_UV_DC6);
+               if (uv & WM8350_UV_LDO1_EINT)
+                       wm8350_irq_call_handler(wm8350, WM8350_IRQ_UV_LDO1);
+               if (uv & WM8350_UV_LDO2_EINT)
+                       wm8350_irq_call_handler(wm8350, WM8350_IRQ_UV_LDO2);
+               if (uv & WM8350_UV_LDO3_EINT)
+                       wm8350_irq_call_handler(wm8350, WM8350_IRQ_UV_LDO3);
+               if (uv & WM8350_UV_LDO4_EINT)
+                       wm8350_irq_call_handler(wm8350, WM8350_IRQ_UV_LDO4);
+       }
+
+       /* charger, RTC */
+       if (status1) {
+               if (status1 & WM8350_CHG_BAT_HOT_EINT)
+                       wm8350_irq_call_handler(wm8350,
+                                               WM8350_IRQ_CHG_BAT_HOT);
+               if (status1 & WM8350_CHG_BAT_COLD_EINT)
+                       wm8350_irq_call_handler(wm8350,
+                                               WM8350_IRQ_CHG_BAT_COLD);
+               if (status1 & WM8350_CHG_BAT_FAIL_EINT)
+                       wm8350_irq_call_handler(wm8350,
+                                               WM8350_IRQ_CHG_BAT_FAIL);
+               if (status1 & WM8350_CHG_TO_EINT)
+                       wm8350_irq_call_handler(wm8350, WM8350_IRQ_CHG_TO);
+               if (status1 & WM8350_CHG_END_EINT)
+                       wm8350_irq_call_handler(wm8350, WM8350_IRQ_CHG_END);
+               if (status1 & WM8350_CHG_START_EINT)
+                       wm8350_irq_call_handler(wm8350, WM8350_IRQ_CHG_START);
+               if (status1 & WM8350_CHG_FAST_RDY_EINT)
+                       wm8350_irq_call_handler(wm8350,
+                                               WM8350_IRQ_CHG_FAST_RDY);
+               if (status1 & WM8350_CHG_VBATT_LT_3P9_EINT)
+                       wm8350_irq_call_handler(wm8350,
+                                               WM8350_IRQ_CHG_VBATT_LT_3P9);
+               if (status1 & WM8350_CHG_VBATT_LT_3P1_EINT)
+                       wm8350_irq_call_handler(wm8350,
+                                               WM8350_IRQ_CHG_VBATT_LT_3P1);
+               if (status1 & WM8350_CHG_VBATT_LT_2P85_EINT)
+                       wm8350_irq_call_handler(wm8350,
+                                               WM8350_IRQ_CHG_VBATT_LT_2P85);
+               if (status1 & WM8350_RTC_ALM_EINT)
+                       wm8350_irq_call_handler(wm8350, WM8350_IRQ_RTC_ALM);
+               if (status1 & WM8350_RTC_SEC_EINT)
+                       wm8350_irq_call_handler(wm8350, WM8350_IRQ_RTC_SEC);
+               if (status1 & WM8350_RTC_PER_EINT)
+                       wm8350_irq_call_handler(wm8350, WM8350_IRQ_RTC_PER);
+       }
+
+       /* current sink, system, aux adc */
+       if (status2) {
+               if (status2 & WM8350_CS1_EINT)
+                       wm8350_irq_call_handler(wm8350, WM8350_IRQ_CS1);
+               if (status2 & WM8350_CS2_EINT)
+                       wm8350_irq_call_handler(wm8350, WM8350_IRQ_CS2);
+
+               if (status2 & WM8350_SYS_HYST_COMP_FAIL_EINT)
+                       wm8350_irq_call_handler(wm8350,
+                                               WM8350_IRQ_SYS_HYST_COMP_FAIL);
+               if (status2 & WM8350_SYS_CHIP_GT115_EINT)
+                       wm8350_irq_call_handler(wm8350,
+                                               WM8350_IRQ_SYS_CHIP_GT115);
+               if (status2 & WM8350_SYS_CHIP_GT140_EINT)
+                       wm8350_irq_call_handler(wm8350,
+                                               WM8350_IRQ_SYS_CHIP_GT140);
+               if (status2 & WM8350_SYS_WDOG_TO_EINT)
+                       wm8350_irq_call_handler(wm8350,
+                                               WM8350_IRQ_SYS_WDOG_TO);
+
+               if (status2 & WM8350_AUXADC_DATARDY_EINT)
+                       wm8350_irq_call_handler(wm8350,
+                                               WM8350_IRQ_AUXADC_DATARDY);
+               if (status2 & WM8350_AUXADC_DCOMP4_EINT)
+                       wm8350_irq_call_handler(wm8350,
+                                               WM8350_IRQ_AUXADC_DCOMP4);
+               if (status2 & WM8350_AUXADC_DCOMP3_EINT)
+                       wm8350_irq_call_handler(wm8350,
+                                               WM8350_IRQ_AUXADC_DCOMP3);
+               if (status2 & WM8350_AUXADC_DCOMP2_EINT)
+                       wm8350_irq_call_handler(wm8350,
+                                               WM8350_IRQ_AUXADC_DCOMP2);
+               if (status2 & WM8350_AUXADC_DCOMP1_EINT)
+                       wm8350_irq_call_handler(wm8350,
+                                               WM8350_IRQ_AUXADC_DCOMP1);
+
+               if (status2 & WM8350_USB_LIMIT_EINT)
+                       wm8350_irq_call_handler(wm8350, WM8350_IRQ_USB_LIMIT);
+       }
+
+       /* wake, codec, ext */
+       if (comp) {
+               if (comp & WM8350_WKUP_OFF_STATE_EINT)
+                       wm8350_irq_call_handler(wm8350,
+                                               WM8350_IRQ_WKUP_OFF_STATE);
+               if (comp & WM8350_WKUP_HIB_STATE_EINT)
+                       wm8350_irq_call_handler(wm8350,
+                                               WM8350_IRQ_WKUP_HIB_STATE);
+               if (comp & WM8350_WKUP_CONV_FAULT_EINT)
+                       wm8350_irq_call_handler(wm8350,
+                                               WM8350_IRQ_WKUP_CONV_FAULT);
+               if (comp & WM8350_WKUP_WDOG_RST_EINT)
+                       wm8350_irq_call_handler(wm8350,
+                                               WM8350_IRQ_WKUP_WDOG_RST);
+               if (comp & WM8350_WKUP_GP_PWR_ON_EINT)
+                       wm8350_irq_call_handler(wm8350,
+                                               WM8350_IRQ_WKUP_GP_PWR_ON);
+               if (comp & WM8350_WKUP_ONKEY_EINT)
+                       wm8350_irq_call_handler(wm8350, WM8350_IRQ_WKUP_ONKEY);
+               if (comp & WM8350_WKUP_GP_WAKEUP_EINT)
+                       wm8350_irq_call_handler(wm8350,
+                                               WM8350_IRQ_WKUP_GP_WAKEUP);
+
+               if (comp & WM8350_CODEC_JCK_DET_L_EINT)
+                       wm8350_irq_call_handler(wm8350,
+                                               WM8350_IRQ_CODEC_JCK_DET_L);
+               if (comp & WM8350_CODEC_JCK_DET_R_EINT)
+                       wm8350_irq_call_handler(wm8350,
+                                               WM8350_IRQ_CODEC_JCK_DET_R);
+               if (comp & WM8350_CODEC_MICSCD_EINT)
+                       wm8350_irq_call_handler(wm8350,
+                                               WM8350_IRQ_CODEC_MICSCD);
+               if (comp & WM8350_CODEC_MICD_EINT)
+                       wm8350_irq_call_handler(wm8350, WM8350_IRQ_CODEC_MICD);
+
+               if (comp & WM8350_EXT_USB_FB_EINT)
+                       wm8350_irq_call_handler(wm8350, WM8350_IRQ_EXT_USB_FB);
+               if (comp & WM8350_EXT_WALL_FB_EINT)
+                       wm8350_irq_call_handler(wm8350,
+                                               WM8350_IRQ_EXT_WALL_FB);
+               if (comp & WM8350_EXT_BAT_FB_EINT)
+                       wm8350_irq_call_handler(wm8350, WM8350_IRQ_EXT_BAT_FB);
+       }
+
+       if (level_one & WM8350_GP_INT) {
+               int i;
+               u16 gpio;
+
+               gpio = wm8350_reg_read(wm8350, WM8350_GPIO_INT_STATUS);
+               gpio &= ~wm8350_reg_read(wm8350,
+                                        WM8350_GPIO_INT_STATUS_MASK);
+
+               for (i = 0; i < 12; i++) {
+                       if (gpio & (1 << i))
+                               wm8350_irq_call_handler(wm8350,
+                                                       WM8350_IRQ_GPIO(i));
+               }
+       }
+
+       enable_irq(wm8350->chip_irq);
+}
+
+static irqreturn_t wm8350_irq(int irq, void *data)
+{
+       struct wm8350 *wm8350 = data;
+
+       disable_irq_nosync(irq);
+       schedule_work(&wm8350->irq_work);
+
+       return IRQ_HANDLED;
+}
+
+int wm8350_register_irq(struct wm8350 *wm8350, int irq,
+                       void (*handler) (struct wm8350 *, int, void *),
+                       void *data)
+{
+       if (irq < 0 || irq > WM8350_NUM_IRQ || !handler)
+               return -EINVAL;
+
+       if (wm8350->irq[irq].handler)
+               return -EBUSY;
+
+       mutex_lock(&wm8350->irq_mutex);
+       wm8350->irq[irq].handler = handler;
+       wm8350->irq[irq].data = data;
+       mutex_unlock(&wm8350->irq_mutex);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(wm8350_register_irq);
+
+int wm8350_free_irq(struct wm8350 *wm8350, int irq)
+{
+       if (irq < 0 || irq > WM8350_NUM_IRQ)
+               return -EINVAL;
+
+       mutex_lock(&wm8350->irq_mutex);
+       wm8350->irq[irq].handler = NULL;
+       mutex_unlock(&wm8350->irq_mutex);
+       return 0;
+}
+EXPORT_SYMBOL_GPL(wm8350_free_irq);
+
+int wm8350_mask_irq(struct wm8350 *wm8350, int irq)
+{
+       switch (irq) {
+       case WM8350_IRQ_CHG_BAT_HOT:
+               return wm8350_set_bits(wm8350, WM8350_INT_STATUS_1_MASK,
+                                      WM8350_IM_CHG_BAT_HOT_EINT);
+       case WM8350_IRQ_CHG_BAT_COLD:
+               return wm8350_set_bits(wm8350, WM8350_INT_STATUS_1_MASK,
+                                      WM8350_IM_CHG_BAT_COLD_EINT);
+       case WM8350_IRQ_CHG_BAT_FAIL:
+               return wm8350_set_bits(wm8350, WM8350_INT_STATUS_1_MASK,
+                                      WM8350_IM_CHG_BAT_FAIL_EINT);
+       case WM8350_IRQ_CHG_TO:
+               return wm8350_set_bits(wm8350, WM8350_INT_STATUS_1_MASK,
+                                      WM8350_IM_CHG_TO_EINT);
+       case WM8350_IRQ_CHG_END:
+               return wm8350_set_bits(wm8350, WM8350_INT_STATUS_1_MASK,
+                                      WM8350_IM_CHG_END_EINT);
+       case WM8350_IRQ_CHG_START:
+               return wm8350_set_bits(wm8350, WM8350_INT_STATUS_1_MASK,
+                                      WM8350_IM_CHG_START_EINT);
+       case WM8350_IRQ_CHG_FAST_RDY:
+               return wm8350_set_bits(wm8350, WM8350_INT_STATUS_1_MASK,
+                                      WM8350_IM_CHG_FAST_RDY_EINT);
+       case WM8350_IRQ_RTC_PER:
+               return wm8350_set_bits(wm8350, WM8350_INT_STATUS_1_MASK,
+                                      WM8350_IM_RTC_PER_EINT);
+       case WM8350_IRQ_RTC_SEC:
+               return wm8350_set_bits(wm8350, WM8350_INT_STATUS_1_MASK,
+                                      WM8350_IM_RTC_SEC_EINT);
+       case WM8350_IRQ_RTC_ALM:
+               return wm8350_set_bits(wm8350, WM8350_INT_STATUS_1_MASK,
+                                      WM8350_IM_RTC_ALM_EINT);
+       case WM8350_IRQ_CHG_VBATT_LT_3P9:
+               return wm8350_set_bits(wm8350, WM8350_INT_STATUS_1_MASK,
+                                      WM8350_IM_CHG_VBATT_LT_3P9_EINT);
+       case WM8350_IRQ_CHG_VBATT_LT_3P1:
+               return wm8350_set_bits(wm8350, WM8350_INT_STATUS_1_MASK,
+                                      WM8350_IM_CHG_VBATT_LT_3P1_EINT);
+       case WM8350_IRQ_CHG_VBATT_LT_2P85:
+               return wm8350_set_bits(wm8350, WM8350_INT_STATUS_1_MASK,
+                                      WM8350_IM_CHG_VBATT_LT_2P85_EINT);
+       case WM8350_IRQ_CS1:
+               return wm8350_set_bits(wm8350, WM8350_INT_STATUS_2_MASK,
+                                      WM8350_IM_CS1_EINT);
+       case WM8350_IRQ_CS2:
+               return wm8350_set_bits(wm8350, WM8350_INT_STATUS_2_MASK,
+                                      WM8350_IM_CS2_EINT);
+       case WM8350_IRQ_USB_LIMIT:
+               return wm8350_set_bits(wm8350, WM8350_INT_STATUS_2_MASK,
+                                      WM8350_IM_USB_LIMIT_EINT);
+       case WM8350_IRQ_AUXADC_DATARDY:
+               return wm8350_set_bits(wm8350, WM8350_INT_STATUS_2_MASK,
+                                      WM8350_IM_AUXADC_DATARDY_EINT);
+       case WM8350_IRQ_AUXADC_DCOMP4:
+               return wm8350_set_bits(wm8350, WM8350_INT_STATUS_2_MASK,
+                                      WM8350_IM_AUXADC_DCOMP4_EINT);
+       case WM8350_IRQ_AUXADC_DCOMP3:
+               return wm8350_set_bits(wm8350, WM8350_INT_STATUS_2_MASK,
+                                      WM8350_IM_AUXADC_DCOMP3_EINT);
+       case WM8350_IRQ_AUXADC_DCOMP2:
+               return wm8350_set_bits(wm8350, WM8350_INT_STATUS_2_MASK,
+                                      WM8350_IM_AUXADC_DCOMP2_EINT);
+       case WM8350_IRQ_AUXADC_DCOMP1:
+               return wm8350_set_bits(wm8350, WM8350_INT_STATUS_2_MASK,
+                                      WM8350_IM_AUXADC_DCOMP1_EINT);
+       case WM8350_IRQ_SYS_HYST_COMP_FAIL:
+               return wm8350_set_bits(wm8350, WM8350_INT_STATUS_2_MASK,
+                                      WM8350_IM_SYS_HYST_COMP_FAIL_EINT);
+       case WM8350_IRQ_SYS_CHIP_GT115:
+               return wm8350_set_bits(wm8350, WM8350_INT_STATUS_2_MASK,
+                                      WM8350_IM_SYS_CHIP_GT115_EINT);
+       case WM8350_IRQ_SYS_CHIP_GT140:
+               return wm8350_set_bits(wm8350, WM8350_INT_STATUS_2_MASK,
+                                      WM8350_IM_SYS_CHIP_GT140_EINT);
+       case WM8350_IRQ_SYS_WDOG_TO:
+               return wm8350_set_bits(wm8350, WM8350_INT_STATUS_2_MASK,
+                                      WM8350_IM_SYS_WDOG_TO_EINT);
+       case WM8350_IRQ_UV_LDO4:
+               return wm8350_set_bits(wm8350,
+                                      WM8350_UNDER_VOLTAGE_INT_STATUS_MASK,
+                                      WM8350_IM_UV_LDO4_EINT);
+       case WM8350_IRQ_UV_LDO3:
+               return wm8350_set_bits(wm8350,
+                                      WM8350_UNDER_VOLTAGE_INT_STATUS_MASK,
+                                      WM8350_IM_UV_LDO3_EINT);
+       case WM8350_IRQ_UV_LDO2:
+               return wm8350_set_bits(wm8350,
+                                      WM8350_UNDER_VOLTAGE_INT_STATUS_MASK,
+                                      WM8350_IM_UV_LDO2_EINT);
+       case WM8350_IRQ_UV_LDO1:
+               return wm8350_set_bits(wm8350,
+                                      WM8350_UNDER_VOLTAGE_INT_STATUS_MASK,
+                                      WM8350_IM_UV_LDO1_EINT);
+       case WM8350_IRQ_UV_DC6:
+               return wm8350_set_bits(wm8350,
+                                      WM8350_UNDER_VOLTAGE_INT_STATUS_MASK,
+                                      WM8350_IM_UV_DC6_EINT);
+       case WM8350_IRQ_UV_DC5:
+               return wm8350_set_bits(wm8350,
+                                      WM8350_UNDER_VOLTAGE_INT_STATUS_MASK,
+                                      WM8350_IM_UV_DC5_EINT);
+       case WM8350_IRQ_UV_DC4:
+               return wm8350_set_bits(wm8350,
+                                      WM8350_UNDER_VOLTAGE_INT_STATUS_MASK,
+                                      WM8350_IM_UV_DC4_EINT);
+       case WM8350_IRQ_UV_DC3:
+               return wm8350_set_bits(wm8350,
+                                      WM8350_UNDER_VOLTAGE_INT_STATUS_MASK,
+                                      WM8350_IM_UV_DC3_EINT);
+       case WM8350_IRQ_UV_DC2:
+               return wm8350_set_bits(wm8350,
+                                      WM8350_UNDER_VOLTAGE_INT_STATUS_MASK,
+                                      WM8350_IM_UV_DC2_EINT);
+       case WM8350_IRQ_UV_DC1:
+               return wm8350_set_bits(wm8350,
+                                      WM8350_UNDER_VOLTAGE_INT_STATUS_MASK,
+                                      WM8350_IM_UV_DC1_EINT);
+       case WM8350_IRQ_OC_LS:
+               return wm8350_set_bits(wm8350,
+                                      WM8350_OVER_CURRENT_INT_STATUS_MASK,
+                                      WM8350_IM_OC_LS_EINT);
+       case WM8350_IRQ_EXT_USB_FB:
+               return wm8350_set_bits(wm8350,
+                                      WM8350_COMPARATOR_INT_STATUS_MASK,
+                                      WM8350_IM_EXT_USB_FB_EINT);
+       case WM8350_IRQ_EXT_WALL_FB:
+               return wm8350_set_bits(wm8350,
+                                      WM8350_COMPARATOR_INT_STATUS_MASK,
+                                      WM8350_IM_EXT_WALL_FB_EINT);
+       case WM8350_IRQ_EXT_BAT_FB:
+               return wm8350_set_bits(wm8350,
+                                      WM8350_COMPARATOR_INT_STATUS_MASK,
+                                      WM8350_IM_EXT_BAT_FB_EINT);
+       case WM8350_IRQ_CODEC_JCK_DET_L:
+               return wm8350_set_bits(wm8350,
+                                      WM8350_COMPARATOR_INT_STATUS_MASK,
+                                      WM8350_IM_CODEC_JCK_DET_L_EINT);
+       case WM8350_IRQ_CODEC_JCK_DET_R:
+               return wm8350_set_bits(wm8350,
+                                      WM8350_COMPARATOR_INT_STATUS_MASK,
+                                      WM8350_IM_CODEC_JCK_DET_R_EINT);
+       case WM8350_IRQ_CODEC_MICSCD:
+               return wm8350_set_bits(wm8350,
+                                      WM8350_COMPARATOR_INT_STATUS_MASK,
+                                      WM8350_IM_CODEC_MICSCD_EINT);
+       case WM8350_IRQ_CODEC_MICD:
+               return wm8350_set_bits(wm8350,
+                                      WM8350_COMPARATOR_INT_STATUS_MASK,
+                                      WM8350_IM_CODEC_MICD_EINT);
+       case WM8350_IRQ_WKUP_OFF_STATE:
+               return wm8350_set_bits(wm8350,
+                                      WM8350_COMPARATOR_INT_STATUS_MASK,
+                                      WM8350_IM_WKUP_OFF_STATE_EINT);
+       case WM8350_IRQ_WKUP_HIB_STATE:
+               return wm8350_set_bits(wm8350,
+                                      WM8350_COMPARATOR_INT_STATUS_MASK,
+                                      WM8350_IM_WKUP_HIB_STATE_EINT);
+       case WM8350_IRQ_WKUP_CONV_FAULT:
+               return wm8350_set_bits(wm8350,
+                                      WM8350_COMPARATOR_INT_STATUS_MASK,
+                                      WM8350_IM_WKUP_CONV_FAULT_EINT);
+       case WM8350_IRQ_WKUP_WDOG_RST:
+               return wm8350_set_bits(wm8350,
+                                      WM8350_COMPARATOR_INT_STATUS_MASK,
+                                      WM8350_IM_WKUP_OFF_STATE_EINT);
+       case WM8350_IRQ_WKUP_GP_PWR_ON:
+               return wm8350_set_bits(wm8350,
+                                      WM8350_COMPARATOR_INT_STATUS_MASK,
+                                      WM8350_IM_WKUP_GP_PWR_ON_EINT);
+       case WM8350_IRQ_WKUP_ONKEY:
+               return wm8350_set_bits(wm8350,
+                                      WM8350_COMPARATOR_INT_STATUS_MASK,
+                                      WM8350_IM_WKUP_ONKEY_EINT);
+       case WM8350_IRQ_WKUP_GP_WAKEUP:
+               return wm8350_set_bits(wm8350,
+                                      WM8350_COMPARATOR_INT_STATUS_MASK,
+                                      WM8350_IM_WKUP_GP_WAKEUP_EINT);
+       case WM8350_IRQ_GPIO(0):
+               return wm8350_set_bits(wm8350,
+                                      WM8350_GPIO_INT_STATUS_MASK,
+                                      WM8350_IM_GP0_EINT);
+       case WM8350_IRQ_GPIO(1):
+               return wm8350_set_bits(wm8350,
+                                      WM8350_GPIO_INT_STATUS_MASK,
+                                      WM8350_IM_GP1_EINT);
+       case WM8350_IRQ_GPIO(2):
+               return wm8350_set_bits(wm8350,
+                                      WM8350_GPIO_INT_STATUS_MASK,
+                                      WM8350_IM_GP2_EINT);
+       case WM8350_IRQ_GPIO(3):
+               return wm8350_set_bits(wm8350,
+                                      WM8350_GPIO_INT_STATUS_MASK,
+                                      WM8350_IM_GP3_EINT);
+       case WM8350_IRQ_GPIO(4):
+               return wm8350_set_bits(wm8350,
+                                      WM8350_GPIO_INT_STATUS_MASK,
+                                      WM8350_IM_GP4_EINT);
+       case WM8350_IRQ_GPIO(5):
+               return wm8350_set_bits(wm8350,
+                                      WM8350_GPIO_INT_STATUS_MASK,
+                                      WM8350_IM_GP5_EINT);
+       case WM8350_IRQ_GPIO(6):
+               return wm8350_set_bits(wm8350,
+                                      WM8350_GPIO_INT_STATUS_MASK,
+                                      WM8350_IM_GP6_EINT);
+       case WM8350_IRQ_GPIO(7):
+               return wm8350_set_bits(wm8350,
+                                      WM8350_GPIO_INT_STATUS_MASK,
+                                      WM8350_IM_GP7_EINT);
+       case WM8350_IRQ_GPIO(8):
+               return wm8350_set_bits(wm8350,
+                                      WM8350_GPIO_INT_STATUS_MASK,
+                                      WM8350_IM_GP8_EINT);
+       case WM8350_IRQ_GPIO(9):
+               return wm8350_set_bits(wm8350,
+                                      WM8350_GPIO_INT_STATUS_MASK,
+                                      WM8350_IM_GP9_EINT);
+       case WM8350_IRQ_GPIO(10):
+               return wm8350_set_bits(wm8350,
+                                      WM8350_GPIO_INT_STATUS_MASK,
+                                      WM8350_IM_GP10_EINT);
+       case WM8350_IRQ_GPIO(11):
+               return wm8350_set_bits(wm8350,
+                                      WM8350_GPIO_INT_STATUS_MASK,
+                                      WM8350_IM_GP11_EINT);
+       case WM8350_IRQ_GPIO(12):
+               return wm8350_set_bits(wm8350,
+                                      WM8350_GPIO_INT_STATUS_MASK,
+                                      WM8350_IM_GP12_EINT);
+       default:
+               dev_warn(wm8350->dev, "Attempting to mask unknown IRQ %d\n",
+                        irq);
+               return -EINVAL;
+       }
+       return 0;
+}
+EXPORT_SYMBOL_GPL(wm8350_mask_irq);
+
+int wm8350_unmask_irq(struct wm8350 *wm8350, int irq)
+{
+       switch (irq) {
+       case WM8350_IRQ_CHG_BAT_HOT:
+               return wm8350_clear_bits(wm8350, WM8350_INT_STATUS_1_MASK,
+                                        WM8350_IM_CHG_BAT_HOT_EINT);
+       case WM8350_IRQ_CHG_BAT_COLD:
+               return wm8350_clear_bits(wm8350, WM8350_INT_STATUS_1_MASK,
+                                        WM8350_IM_CHG_BAT_COLD_EINT);
+       case WM8350_IRQ_CHG_BAT_FAIL:
+               return wm8350_clear_bits(wm8350, WM8350_INT_STATUS_1_MASK,
+                                        WM8350_IM_CHG_BAT_FAIL_EINT);
+       case WM8350_IRQ_CHG_TO:
+               return wm8350_clear_bits(wm8350, WM8350_INT_STATUS_1_MASK,
+                                        WM8350_IM_CHG_TO_EINT);
+       case WM8350_IRQ_CHG_END:
+               return wm8350_clear_bits(wm8350, WM8350_INT_STATUS_1_MASK,
+                                        WM8350_IM_CHG_END_EINT);
+       case WM8350_IRQ_CHG_START:
+               return wm8350_clear_bits(wm8350, WM8350_INT_STATUS_1_MASK,
+                                        WM8350_IM_CHG_START_EINT);
+       case WM8350_IRQ_CHG_FAST_RDY:
+               return wm8350_clear_bits(wm8350, WM8350_INT_STATUS_1_MASK,
+                                        WM8350_IM_CHG_FAST_RDY_EINT);
+       case WM8350_IRQ_RTC_PER:
+               return wm8350_clear_bits(wm8350, WM8350_INT_STATUS_1_MASK,
+                                        WM8350_IM_RTC_PER_EINT);
+       case WM8350_IRQ_RTC_SEC:
+               return wm8350_clear_bits(wm8350, WM8350_INT_STATUS_1_MASK,
+                                        WM8350_IM_RTC_SEC_EINT);
+       case WM8350_IRQ_RTC_ALM:
+               return wm8350_clear_bits(wm8350, WM8350_INT_STATUS_1_MASK,
+                                        WM8350_IM_RTC_ALM_EINT);
+       case WM8350_IRQ_CHG_VBATT_LT_3P9:
+               return wm8350_clear_bits(wm8350, WM8350_INT_STATUS_1_MASK,
+                                        WM8350_IM_CHG_VBATT_LT_3P9_EINT);
+       case WM8350_IRQ_CHG_VBATT_LT_3P1:
+               return wm8350_clear_bits(wm8350, WM8350_INT_STATUS_1_MASK,
+                                        WM8350_IM_CHG_VBATT_LT_3P1_EINT);
+       case WM8350_IRQ_CHG_VBATT_LT_2P85:
+               return wm8350_clear_bits(wm8350, WM8350_INT_STATUS_1_MASK,
+                                        WM8350_IM_CHG_VBATT_LT_2P85_EINT);
+       case WM8350_IRQ_CS1:
+               return wm8350_clear_bits(wm8350, WM8350_INT_STATUS_2_MASK,
+                                        WM8350_IM_CS1_EINT);
+       case WM8350_IRQ_CS2:
+               return wm8350_clear_bits(wm8350, WM8350_INT_STATUS_2_MASK,
+                                        WM8350_IM_CS2_EINT);
+       case WM8350_IRQ_USB_LIMIT:
+               return wm8350_clear_bits(wm8350, WM8350_INT_STATUS_2_MASK,
+                                        WM8350_IM_USB_LIMIT_EINT);
+       case WM8350_IRQ_AUXADC_DATARDY:
+               return wm8350_clear_bits(wm8350, WM8350_INT_STATUS_2_MASK,
+                                        WM8350_IM_AUXADC_DATARDY_EINT);
+       case WM8350_IRQ_AUXADC_DCOMP4:
+               return wm8350_clear_bits(wm8350, WM8350_INT_STATUS_2_MASK,
+                                        WM8350_IM_AUXADC_DCOMP4_EINT);
+       case WM8350_IRQ_AUXADC_DCOMP3:
+               return wm8350_clear_bits(wm8350, WM8350_INT_STATUS_2_MASK,
+                                        WM8350_IM_AUXADC_DCOMP3_EINT);
+       case WM8350_IRQ_AUXADC_DCOMP2:
+               return wm8350_clear_bits(wm8350, WM8350_INT_STATUS_2_MASK,
+                                        WM8350_IM_AUXADC_DCOMP2_EINT);
+       case WM8350_IRQ_AUXADC_DCOMP1:
+               return wm8350_clear_bits(wm8350, WM8350_INT_STATUS_2_MASK,
+                                        WM8350_IM_AUXADC_DCOMP1_EINT);
+       case WM8350_IRQ_SYS_HYST_COMP_FAIL:
+               return wm8350_clear_bits(wm8350, WM8350_INT_STATUS_2_MASK,
+                                        WM8350_IM_SYS_HYST_COMP_FAIL_EINT);
+       case WM8350_IRQ_SYS_CHIP_GT115:
+               return wm8350_clear_bits(wm8350, WM8350_INT_STATUS_2_MASK,
+                                        WM8350_IM_SYS_CHIP_GT115_EINT);
+       case WM8350_IRQ_SYS_CHIP_GT140:
+               return wm8350_clear_bits(wm8350, WM8350_INT_STATUS_2_MASK,
+                                        WM8350_IM_SYS_CHIP_GT140_EINT);
+       case WM8350_IRQ_SYS_WDOG_TO:
+               return wm8350_clear_bits(wm8350, WM8350_INT_STATUS_2_MASK,
+                                        WM8350_IM_SYS_WDOG_TO_EINT);
+       case WM8350_IRQ_UV_LDO4:
+               return wm8350_clear_bits(wm8350,
+                                        WM8350_UNDER_VOLTAGE_INT_STATUS_MASK,
+                                        WM8350_IM_UV_LDO4_EINT);
+       case WM8350_IRQ_UV_LDO3:
+               return wm8350_clear_bits(wm8350,
+                                        WM8350_UNDER_VOLTAGE_INT_STATUS_MASK,
+                                        WM8350_IM_UV_LDO3_EINT);
+       case WM8350_IRQ_UV_LDO2:
+               return wm8350_clear_bits(wm8350,
+                                        WM8350_UNDER_VOLTAGE_INT_STATUS_MASK,
+                                        WM8350_IM_UV_LDO2_EINT);
+       case WM8350_IRQ_UV_LDO1:
+               return wm8350_clear_bits(wm8350,
+                                        WM8350_UNDER_VOLTAGE_INT_STATUS_MASK,
+                                        WM8350_IM_UV_LDO1_EINT);
+       case WM8350_IRQ_UV_DC6:
+               return wm8350_clear_bits(wm8350,
+                                        WM8350_UNDER_VOLTAGE_INT_STATUS_MASK,
+                                        WM8350_IM_UV_DC6_EINT);
+       case WM8350_IRQ_UV_DC5:
+               return wm8350_clear_bits(wm8350,
+                                        WM8350_UNDER_VOLTAGE_INT_STATUS_MASK,
+                                        WM8350_IM_UV_DC5_EINT);
+       case WM8350_IRQ_UV_DC4:
+               return wm8350_clear_bits(wm8350,
+                                        WM8350_UNDER_VOLTAGE_INT_STATUS_MASK,
+                                        WM8350_IM_UV_DC4_EINT);
+       case WM8350_IRQ_UV_DC3:
+               return wm8350_clear_bits(wm8350,
+                                        WM8350_UNDER_VOLTAGE_INT_STATUS_MASK,
+                                        WM8350_IM_UV_DC3_EINT);
+       case WM8350_IRQ_UV_DC2:
+               return wm8350_clear_bits(wm8350,
+                                        WM8350_UNDER_VOLTAGE_INT_STATUS_MASK,
+                                        WM8350_IM_UV_DC2_EINT);
+       case WM8350_IRQ_UV_DC1:
+               return wm8350_clear_bits(wm8350,
+                                        WM8350_UNDER_VOLTAGE_INT_STATUS_MASK,
+                                        WM8350_IM_UV_DC1_EINT);
+       case WM8350_IRQ_OC_LS:
+               return wm8350_clear_bits(wm8350,
+                                        WM8350_OVER_CURRENT_INT_STATUS_MASK,
+                                        WM8350_IM_OC_LS_EINT);
+       case WM8350_IRQ_EXT_USB_FB:
+               return wm8350_clear_bits(wm8350,
+                                        WM8350_COMPARATOR_INT_STATUS_MASK,
+                                        WM8350_IM_EXT_USB_FB_EINT);
+       case WM8350_IRQ_EXT_WALL_FB:
+               return wm8350_clear_bits(wm8350,
+                                        WM8350_COMPARATOR_INT_STATUS_MASK,
+                                        WM8350_IM_EXT_WALL_FB_EINT);
+       case WM8350_IRQ_EXT_BAT_FB:
+               return wm8350_clear_bits(wm8350,
+                                        WM8350_COMPARATOR_INT_STATUS_MASK,
+                                        WM8350_IM_EXT_BAT_FB_EINT);
+       case WM8350_IRQ_CODEC_JCK_DET_L:
+               return wm8350_clear_bits(wm8350,
+                                        WM8350_COMPARATOR_INT_STATUS_MASK,
+                                        WM8350_IM_CODEC_JCK_DET_L_EINT);
+       case WM8350_IRQ_CODEC_JCK_DET_R:
+               return wm8350_clear_bits(wm8350,
+                                        WM8350_COMPARATOR_INT_STATUS_MASK,
+                                        WM8350_IM_CODEC_JCK_DET_R_EINT);
+       case WM8350_IRQ_CODEC_MICSCD:
+               return wm8350_clear_bits(wm8350,
+                                        WM8350_COMPARATOR_INT_STATUS_MASK,
+                                        WM8350_IM_CODEC_MICSCD_EINT);
+       case WM8350_IRQ_CODEC_MICD:
+               return wm8350_clear_bits(wm8350,
+                                        WM8350_COMPARATOR_INT_STATUS_MASK,
+                                        WM8350_IM_CODEC_MICD_EINT);
+       case WM8350_IRQ_WKUP_OFF_STATE:
+               return wm8350_clear_bits(wm8350,
+                                        WM8350_COMPARATOR_INT_STATUS_MASK,
+                                        WM8350_IM_WKUP_OFF_STATE_EINT);
+       case WM8350_IRQ_WKUP_HIB_STATE:
+               return wm8350_clear_bits(wm8350,
+                                        WM8350_COMPARATOR_INT_STATUS_MASK,
+                                        WM8350_IM_WKUP_HIB_STATE_EINT);
+       case WM8350_IRQ_WKUP_CONV_FAULT:
+               return wm8350_clear_bits(wm8350,
+                                        WM8350_COMPARATOR_INT_STATUS_MASK,
+                                        WM8350_IM_WKUP_CONV_FAULT_EINT);
+       case WM8350_IRQ_WKUP_WDOG_RST:
+               return wm8350_clear_bits(wm8350,
+                                        WM8350_COMPARATOR_INT_STATUS_MASK,
+                                        WM8350_IM_WKUP_OFF_STATE_EINT);
+       case WM8350_IRQ_WKUP_GP_PWR_ON:
+               return wm8350_clear_bits(wm8350,
+                                        WM8350_COMPARATOR_INT_STATUS_MASK,
+                                        WM8350_IM_WKUP_GP_PWR_ON_EINT);
+       case WM8350_IRQ_WKUP_ONKEY:
+               return wm8350_clear_bits(wm8350,
+                                        WM8350_COMPARATOR_INT_STATUS_MASK,
+                                        WM8350_IM_WKUP_ONKEY_EINT);
+       case WM8350_IRQ_WKUP_GP_WAKEUP:
+               return wm8350_clear_bits(wm8350,
+                                        WM8350_COMPARATOR_INT_STATUS_MASK,
+                                        WM8350_IM_WKUP_GP_WAKEUP_EINT);
+       case WM8350_IRQ_GPIO(0):
+               return wm8350_clear_bits(wm8350,
+                                        WM8350_GPIO_INT_STATUS_MASK,
+                                        WM8350_IM_GP0_EINT);
+       case WM8350_IRQ_GPIO(1):
+               return wm8350_clear_bits(wm8350,
+                                        WM8350_GPIO_INT_STATUS_MASK,
+                                        WM8350_IM_GP1_EINT);
+       case WM8350_IRQ_GPIO(2):
+               return wm8350_clear_bits(wm8350,
+                                        WM8350_GPIO_INT_STATUS_MASK,
+                                        WM8350_IM_GP2_EINT);
+       case WM8350_IRQ_GPIO(3):
+               return wm8350_clear_bits(wm8350,
+                                        WM8350_GPIO_INT_STATUS_MASK,
+                                        WM8350_IM_GP3_EINT);
+       case WM8350_IRQ_GPIO(4):
+               return wm8350_clear_bits(wm8350,
+                                        WM8350_GPIO_INT_STATUS_MASK,
+                                        WM8350_IM_GP4_EINT);
+       case WM8350_IRQ_GPIO(5):
+               return wm8350_clear_bits(wm8350,
+                                        WM8350_GPIO_INT_STATUS_MASK,
+                                        WM8350_IM_GP5_EINT);
+       case WM8350_IRQ_GPIO(6):
+               return wm8350_clear_bits(wm8350,
+                                        WM8350_GPIO_INT_STATUS_MASK,
+                                        WM8350_IM_GP6_EINT);
+       case WM8350_IRQ_GPIO(7):
+               return wm8350_clear_bits(wm8350,
+                                        WM8350_GPIO_INT_STATUS_MASK,
+                                        WM8350_IM_GP7_EINT);
+       case WM8350_IRQ_GPIO(8):
+               return wm8350_clear_bits(wm8350,
+                                        WM8350_GPIO_INT_STATUS_MASK,
+                                        WM8350_IM_GP8_EINT);
+       case WM8350_IRQ_GPIO(9):
+               return wm8350_clear_bits(wm8350,
+                                        WM8350_GPIO_INT_STATUS_MASK,
+                                        WM8350_IM_GP9_EINT);
+       case WM8350_IRQ_GPIO(10):
+               return wm8350_clear_bits(wm8350,
+                                        WM8350_GPIO_INT_STATUS_MASK,
+                                        WM8350_IM_GP10_EINT);
+       case WM8350_IRQ_GPIO(11):
+               return wm8350_clear_bits(wm8350,
+                                        WM8350_GPIO_INT_STATUS_MASK,
+                                        WM8350_IM_GP11_EINT);
+       case WM8350_IRQ_GPIO(12):
+               return wm8350_clear_bits(wm8350,
+                                        WM8350_GPIO_INT_STATUS_MASK,
+                                        WM8350_IM_GP12_EINT);
+       default:
+               dev_warn(wm8350->dev, "Attempting to unmask unknown IRQ %d\n",
+                        irq);
+               return -EINVAL;
+       }
+       return 0;
+}
+EXPORT_SYMBOL_GPL(wm8350_unmask_irq);
+
+/*
+ * Cache is always host endian.
+ */
+static int wm8350_create_cache(struct wm8350 *wm8350, int mode)
+{
+       int i, ret = 0;
+       u16 value;
+       const u16 *reg_map;
+
+       switch (mode) {
+#ifdef CONFIG_MFD_WM8350_CONFIG_MODE_0
+       case 0:
+               reg_map = wm8350_mode0_defaults;
+               break;
+#endif
+#ifdef CONFIG_MFD_WM8350_CONFIG_MODE_1
+       case 1:
+               reg_map = wm8350_mode1_defaults;
+               break;
+#endif
+#ifdef CONFIG_MFD_WM8350_CONFIG_MODE_2
+       case 2:
+               reg_map = wm8350_mode2_defaults;
+               break;
+#endif
+#ifdef CONFIG_MFD_WM8350_CONFIG_MODE_3
+       case 3:
+               reg_map = wm8350_mode3_defaults;
+               break;
+#endif
+       default:
+               dev_err(wm8350->dev, "Configuration mode %d not supported\n",
+                       mode);
+               return -EINVAL;
+       }
+
+       wm8350->reg_cache =
+           kzalloc(sizeof(u16) * (WM8350_MAX_REGISTER + 1), GFP_KERNEL);
+       if (wm8350->reg_cache == NULL)
+               return -ENOMEM;
+
+       /* Read the initial cache state back from the device - this is
+        * a PMIC so the device many not be in a virgin state and we
+        * can't rely on the silicon values.
+        */
+       for (i = 0; i < WM8350_MAX_REGISTER; i++) {
+               /* audio register range */
+               if (wm8350_reg_io_map[i].readable &&
+                   (i < WM8350_CLOCK_CONTROL_1 || i > WM8350_AIF_TEST)) {
+                       ret = wm8350->read_dev(wm8350, i, 2, (char *)&value);
+                       if (ret < 0) {
+                               dev_err(wm8350->dev,
+                                      "failed to read initial cache value\n");
+                               goto out;
+                       }
+                       value = be16_to_cpu(value);
+                       value &= wm8350_reg_io_map[i].readable;
+                       wm8350->reg_cache[i] = value;
+               } else
+                       wm8350->reg_cache[i] = reg_map[i];
+       }
+
+out:
+       return ret;
+}
+EXPORT_SYMBOL_GPL(wm8350_create_cache);
+
+/*
+ * Register a client device.  This is non-fatal since there is no need to
+ * fail the entire device init due to a single platform device failing.
+ */
+static void wm8350_client_dev_register(struct wm8350 *wm8350,
+                                      const char *name,
+                                      struct platform_device **pdev)
+{
+       int ret;
+
+       *pdev = platform_device_alloc(name, -1);
+       if (pdev == NULL) {
+               dev_err(wm8350->dev, "Failed to allocate %s\n", name);
+               return;
+       }
+
+       (*pdev)->dev.parent = wm8350->dev;
+       platform_set_drvdata(*pdev, wm8350);
+       ret = platform_device_add(*pdev);
+       if (ret != 0) {
+               dev_err(wm8350->dev, "Failed to register %s: %d\n", name, ret);
+               platform_device_put(*pdev);
+               *pdev = NULL;
+       }
+}
+
+int wm8350_device_init(struct wm8350 *wm8350, int irq,
+                      struct wm8350_platform_data *pdata)
+{
+       int ret = -EINVAL;
+       u16 id1, id2, mask, mode;
+
+       /* get WM8350 revision and config mode */
+       wm8350->read_dev(wm8350, WM8350_RESET_ID, sizeof(id1), &id1);
+       wm8350->read_dev(wm8350, WM8350_ID, sizeof(id2), &id2);
+
+       id1 = be16_to_cpu(id1);
+       id2 = be16_to_cpu(id2);
+
+       if (id1 == 0x6143) {
+               switch ((id2 & WM8350_CHIP_REV_MASK) >> 12) {
+               case WM8350_REV_E:
+                       dev_info(wm8350->dev, "Found Rev E device\n");
+                       wm8350->rev = WM8350_REV_E;
+                       break;
+               case WM8350_REV_F:
+                       dev_info(wm8350->dev, "Found Rev F device\n");
+                       wm8350->rev = WM8350_REV_F;
+                       break;
+               case WM8350_REV_G:
+                       dev_info(wm8350->dev, "Found Rev G device\n");
+                       wm8350->rev = WM8350_REV_G;
+                       break;
+               default:
+                       /* For safety we refuse to run on unknown hardware */
+                       dev_info(wm8350->dev, "Found unknown rev\n");
+                       ret = -ENODEV;
+                       goto err;
+               }
+       } else {
+               dev_info(wm8350->dev, "Device with ID %x is not a WM8350\n",
+                        id1);
+               ret = -ENODEV;
+               goto err;
+       }
+
+       mode = id2 & WM8350_CONF_STS_MASK >> 10;
+       mask = id2 & WM8350_CUST_ID_MASK;
+       dev_info(wm8350->dev, "Config mode %d, ROM mask %d\n", mode, mask);
+
+       ret = wm8350_create_cache(wm8350, mode);
+       if (ret < 0) {
+               printk(KERN_ERR "wm8350: failed to create register cache\n");
+               return ret;
+       }
+
+       if (pdata->init) {
+               ret = pdata->init(wm8350);
+               if (ret != 0) {
+                       dev_err(wm8350->dev, "Platform init() failed: %d\n",
+                               ret);
+                       goto err;
+               }
+       }
+
+       mutex_init(&wm8350->irq_mutex);
+       INIT_WORK(&wm8350->irq_work, wm8350_irq_worker);
+       if (irq != NO_IRQ) {
+               ret = request_irq(irq, wm8350_irq, 0,
+                                 "wm8350", wm8350);
+               if (ret != 0) {
+                       dev_err(wm8350->dev, "Failed to request IRQ: %d\n",
+                               ret);
+                       goto err;
+               }
+       } else {
+               dev_err(wm8350->dev, "No IRQ configured\n");
+               goto err;
+       }
+       wm8350->chip_irq = irq;
+
+       wm8350_reg_write(wm8350, WM8350_SYSTEM_INTERRUPTS_MASK, 0x0);
+
+       wm8350_client_dev_register(wm8350, "wm8350-codec",
+                                  &(wm8350->codec.pdev));
+       wm8350_client_dev_register(wm8350, "wm8350-gpio",
+                                  &(wm8350->gpio.pdev));
+       wm8350_client_dev_register(wm8350, "wm8350-power",
+                                  &(wm8350->power.pdev));
+       wm8350_client_dev_register(wm8350, "wm8350-rtc", &(wm8350->rtc.pdev));
+       wm8350_client_dev_register(wm8350, "wm8350-wdt", &(wm8350->wdt.pdev));
+
+       return 0;
+
+err:
+       kfree(wm8350->reg_cache);
+       return ret;
+}
+EXPORT_SYMBOL_GPL(wm8350_device_init);
+
+void wm8350_device_exit(struct wm8350 *wm8350)
+{
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(wm8350->pmic.pdev); i++)
+               platform_device_unregister(wm8350->pmic.pdev[i]);
+
+       platform_device_unregister(wm8350->wdt.pdev);
+       platform_device_unregister(wm8350->rtc.pdev);
+       platform_device_unregister(wm8350->power.pdev);
+       platform_device_unregister(wm8350->gpio.pdev);
+       platform_device_unregister(wm8350->codec.pdev);
+
+       free_irq(wm8350->chip_irq, wm8350);
+       flush_work(&wm8350->irq_work);
+       kfree(wm8350->reg_cache);
+}
+EXPORT_SYMBOL_GPL(wm8350_device_exit);
+
+MODULE_DESCRIPTION("WM8350 AudioPlus PMIC core driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mfd/wm8350-gpio.c b/drivers/mfd/wm8350-gpio.c
new file mode 100644 (file)
index 0000000..ebf99be
--- /dev/null
@@ -0,0 +1,222 @@
+/*
+ * wm8350-core.c  --  Device access for Wolfson WM8350
+ *
+ * Copyright 2007, 2008 Wolfson Microelectronics PLC.
+ *
+ * Author: Liam Girdwood
+ *
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/errno.h>
+
+#include <linux/mfd/wm8350/core.h>
+#include <linux/mfd/wm8350/gpio.h>
+#include <linux/mfd/wm8350/pmic.h>
+
+static int gpio_set_dir(struct wm8350 *wm8350, int gpio, int dir)
+{
+       int ret;
+
+       wm8350_reg_unlock(wm8350);
+       if (dir == WM8350_GPIO_DIR_OUT)
+               ret = wm8350_clear_bits(wm8350,
+                                       WM8350_GPIO_CONFIGURATION_I_O,
+                                       1 << gpio);
+       else
+               ret = wm8350_set_bits(wm8350,
+                                     WM8350_GPIO_CONFIGURATION_I_O,
+                                     1 << gpio);
+       wm8350_reg_lock(wm8350);
+       return ret;
+}
+
+static int gpio_set_debounce(struct wm8350 *wm8350, int gpio, int db)
+{
+       if (db == WM8350_GPIO_DEBOUNCE_ON)
+               return wm8350_set_bits(wm8350, WM8350_GPIO_DEBOUNCE,
+                                      1 << gpio);
+       else
+               return wm8350_clear_bits(wm8350,
+                                        WM8350_GPIO_DEBOUNCE, 1 << gpio);
+}
+
+static int gpio_set_func(struct wm8350 *wm8350, int gpio, int func)
+{
+       u16 reg;
+
+       wm8350_reg_unlock(wm8350);
+       switch (gpio) {
+       case 0:
+               reg = wm8350_reg_read(wm8350, WM8350_GPIO_FUNCTION_SELECT_1)
+                   & ~WM8350_GP0_FN_MASK;
+               wm8350_reg_write(wm8350, WM8350_GPIO_FUNCTION_SELECT_1,
+                                reg | ((func & 0xf) << 0));
+               break;
+       case 1:
+               reg = wm8350_reg_read(wm8350, WM8350_GPIO_FUNCTION_SELECT_1)
+                   & ~WM8350_GP1_FN_MASK;
+               wm8350_reg_write(wm8350, WM8350_GPIO_FUNCTION_SELECT_1,
+                                reg | ((func & 0xf) << 4));
+               break;
+       case 2:
+               reg = wm8350_reg_read(wm8350, WM8350_GPIO_FUNCTION_SELECT_1)
+                   & ~WM8350_GP2_FN_MASK;
+               wm8350_reg_write(wm8350, WM8350_GPIO_FUNCTION_SELECT_1,
+                                reg | ((func & 0xf) << 8));
+               break;
+       case 3:
+               reg = wm8350_reg_read(wm8350, WM8350_GPIO_FUNCTION_SELECT_1)
+                   & ~WM8350_GP3_FN_MASK;
+               wm8350_reg_write(wm8350, WM8350_GPIO_FUNCTION_SELECT_1,
+                                reg | ((func & 0xf) << 12));
+               break;
+       case 4:
+               reg = wm8350_reg_read(wm8350, WM8350_GPIO_FUNCTION_SELECT_2)
+                   & ~WM8350_GP4_FN_MASK;
+               wm8350_reg_write(wm8350, WM8350_GPIO_FUNCTION_SELECT_2,
+                                reg | ((func & 0xf) << 0));
+               break;
+       case 5:
+               reg = wm8350_reg_read(wm8350, WM8350_GPIO_FUNCTION_SELECT_2)
+                   & ~WM8350_GP5_FN_MASK;
+               wm8350_reg_write(wm8350, WM8350_GPIO_FUNCTION_SELECT_2,
+                                reg | ((func & 0xf) << 4));
+               break;
+       case 6:
+               reg = wm8350_reg_read(wm8350, WM8350_GPIO_FUNCTION_SELECT_2)
+                   & ~WM8350_GP6_FN_MASK;
+               wm8350_reg_write(wm8350, WM8350_GPIO_FUNCTION_SELECT_2,
+                                reg | ((func & 0xf) << 8));
+               break;
+       case 7:
+               reg = wm8350_reg_read(wm8350, WM8350_GPIO_FUNCTION_SELECT_2)
+                   & ~WM8350_GP7_FN_MASK;
+               wm8350_reg_write(wm8350, WM8350_GPIO_FUNCTION_SELECT_2,
+                                reg | ((func & 0xf) << 12));
+               break;
+       case 8:
+               reg = wm8350_reg_read(wm8350, WM8350_GPIO_FUNCTION_SELECT_3)
+                   & ~WM8350_GP8_FN_MASK;
+               wm8350_reg_write(wm8350, WM8350_GPIO_FUNCTION_SELECT_3,
+                                reg | ((func & 0xf) << 0));
+               break;
+       case 9:
+               reg = wm8350_reg_read(wm8350, WM8350_GPIO_FUNCTION_SELECT_3)
+                   & ~WM8350_GP9_FN_MASK;
+               wm8350_reg_write(wm8350, WM8350_GPIO_FUNCTION_SELECT_3,
+                                reg | ((func & 0xf) << 4));
+               break;
+       case 10:
+               reg = wm8350_reg_read(wm8350, WM8350_GPIO_FUNCTION_SELECT_3)
+                   & ~WM8350_GP10_FN_MASK;
+               wm8350_reg_write(wm8350, WM8350_GPIO_FUNCTION_SELECT_3,
+                                reg | ((func & 0xf) << 8));
+               break;
+       case 11:
+               reg = wm8350_reg_read(wm8350, WM8350_GPIO_FUNCTION_SELECT_3)
+                   & ~WM8350_GP11_FN_MASK;
+               wm8350_reg_write(wm8350, WM8350_GPIO_FUNCTION_SELECT_3,
+                                reg | ((func & 0xf) << 12));
+               break;
+       case 12:
+               reg = wm8350_reg_read(wm8350, WM8350_GPIO_FUNCTION_SELECT_4)
+                   & ~WM8350_GP12_FN_MASK;
+               wm8350_reg_write(wm8350, WM8350_GPIO_FUNCTION_SELECT_4,
+                                reg | ((func & 0xf) << 0));
+               break;
+       default:
+               wm8350_reg_lock(wm8350);
+               return -EINVAL;
+       }
+
+       wm8350_reg_lock(wm8350);
+       return 0;
+}
+
+static int gpio_set_pull_up(struct wm8350 *wm8350, int gpio, int up)
+{
+       if (up)
+               return wm8350_set_bits(wm8350,
+                                      WM8350_GPIO_PIN_PULL_UP_CONTROL,
+                                      1 << gpio);
+       else
+               return wm8350_clear_bits(wm8350,
+                                        WM8350_GPIO_PIN_PULL_UP_CONTROL,
+                                        1 << gpio);
+}
+
+static int gpio_set_pull_down(struct wm8350 *wm8350, int gpio, int down)
+{
+       if (down)
+               return wm8350_set_bits(wm8350,
+                                      WM8350_GPIO_PULL_DOWN_CONTROL,
+                                      1 << gpio);
+       else
+               return wm8350_clear_bits(wm8350,
+                                        WM8350_GPIO_PULL_DOWN_CONTROL,
+                                        1 << gpio);
+}
+
+static int gpio_set_polarity(struct wm8350 *wm8350, int gpio, int pol)
+{
+       if (pol == WM8350_GPIO_ACTIVE_HIGH)
+               return wm8350_set_bits(wm8350,
+                                      WM8350_GPIO_PIN_POLARITY_TYPE,
+                                      1 << gpio);
+       else
+               return wm8350_clear_bits(wm8350,
+                                        WM8350_GPIO_PIN_POLARITY_TYPE,
+                                        1 << gpio);
+}
+
+static int gpio_set_invert(struct wm8350 *wm8350, int gpio, int invert)
+{
+       if (invert == WM8350_GPIO_INVERT_ON)
+               return wm8350_set_bits(wm8350, WM8350_GPIO_INT_MODE, 1 << gpio);
+       else
+               return wm8350_clear_bits(wm8350,
+                                        WM8350_GPIO_INT_MODE, 1 << gpio);
+}
+
+int wm8350_gpio_config(struct wm8350 *wm8350, int gpio, int dir, int func,
+                      int pol, int pull, int invert, int debounce)
+{
+       /* make sure we never pull up and down at the same time */
+       if (pull == WM8350_GPIO_PULL_NONE) {
+               if (gpio_set_pull_up(wm8350, gpio, 0))
+                       goto err;
+               if (gpio_set_pull_down(wm8350, gpio, 0))
+                       goto err;
+       } else if (pull == WM8350_GPIO_PULL_UP) {
+               if (gpio_set_pull_down(wm8350, gpio, 0))
+                       goto err;
+               if (gpio_set_pull_up(wm8350, gpio, 1))
+                       goto err;
+       } else if (pull == WM8350_GPIO_PULL_DOWN) {
+               if (gpio_set_pull_up(wm8350, gpio, 0))
+                       goto err;
+               if (gpio_set_pull_down(wm8350, gpio, 1))
+                       goto err;
+       }
+
+       if (gpio_set_invert(wm8350, gpio, invert))
+               goto err;
+       if (gpio_set_polarity(wm8350, gpio, pol))
+               goto err;
+       if (gpio_set_debounce(wm8350, gpio, debounce))
+               goto err;
+       if (gpio_set_dir(wm8350, gpio, dir))
+               goto err;
+       return gpio_set_func(wm8350, gpio, func);
+
+err:
+       return -EIO;
+}
+EXPORT_SYMBOL_GPL(wm8350_gpio_config);
diff --git a/drivers/mfd/wm8350-i2c.c b/drivers/mfd/wm8350-i2c.c
new file mode 100644 (file)
index 0000000..8dfe21b
--- /dev/null
@@ -0,0 +1,120 @@
+/*
+ * wm8350-i2c.c  --  Generic I2C driver for Wolfson WM8350 PMIC
+ *
+ * This driver defines and configures the WM8350 for the Freescale i.MX32ADS.
+ *
+ * Copyright 2007, 2008 Wolfson Microelectronics PLC.
+ *
+ * Author: Liam Girdwood
+ *         linux@wolfsonmicro.com
+ *
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/wm8350/core.h>
+
+static int wm8350_i2c_read_device(struct wm8350 *wm8350, char reg,
+                                 int bytes, void *dest)
+{
+       int ret;
+
+       ret = i2c_master_send(wm8350->i2c_client, &reg, 1);
+       if (ret < 0)
+               return ret;
+       return i2c_master_recv(wm8350->i2c_client, dest, bytes);
+}
+
+static int wm8350_i2c_write_device(struct wm8350 *wm8350, char reg,
+                                  int bytes, void *src)
+{
+       /* we add 1 byte for device register */
+       u8 msg[(WM8350_MAX_REGISTER << 1) + 1];
+
+       if (bytes > ((WM8350_MAX_REGISTER << 1) + 1))
+               return -EINVAL;
+
+       msg[0] = reg;
+       memcpy(&msg[1], src, bytes);
+       return i2c_master_send(wm8350->i2c_client, msg, bytes + 1);
+}
+
+static int wm8350_i2c_probe(struct i2c_client *i2c,
+                           const struct i2c_device_id *id)
+{
+       struct wm8350 *wm8350;
+       int ret = 0;
+
+       wm8350 = kzalloc(sizeof(struct wm8350), GFP_KERNEL);
+       if (wm8350 == NULL) {
+               kfree(i2c);
+               return -ENOMEM;
+       }
+
+       i2c_set_clientdata(i2c, wm8350);
+       wm8350->dev = &i2c->dev;
+       wm8350->i2c_client = i2c;
+       wm8350->read_dev = wm8350_i2c_read_device;
+       wm8350->write_dev = wm8350_i2c_write_device;
+
+       ret = wm8350_device_init(wm8350, i2c->irq, i2c->dev.platform_data);
+       if (ret < 0)
+               goto err;
+
+       return ret;
+
+err:
+       kfree(wm8350);
+       return ret;
+}
+
+static int wm8350_i2c_remove(struct i2c_client *i2c)
+{
+       struct wm8350 *wm8350 = i2c_get_clientdata(i2c);
+
+       wm8350_device_exit(wm8350);
+       kfree(wm8350);
+
+       return 0;
+}
+
+static const struct i2c_device_id wm8350_i2c_id[] = {
+       { "wm8350", 0 },
+       { }
+};
+MODULE_DEVICE_TABLE(i2c, wm8350_i2c_id);
+
+
+static struct i2c_driver wm8350_i2c_driver = {
+       .driver = {
+                  .name = "wm8350",
+                  .owner = THIS_MODULE,
+       },
+       .probe = wm8350_i2c_probe,
+       .remove = wm8350_i2c_remove,
+       .id_table = wm8350_i2c_id,
+};
+
+static int __init wm8350_i2c_init(void)
+{
+       return i2c_add_driver(&wm8350_i2c_driver);
+}
+/* init early so consumer devices can complete system boot */
+subsys_initcall(wm8350_i2c_init);
+
+static void __exit wm8350_i2c_exit(void)
+{
+       i2c_del_driver(&wm8350_i2c_driver);
+}
+module_exit(wm8350_i2c_exit);
+
+MODULE_DESCRIPTION("I2C support for the WM8350 AudioPlus PMIC");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mfd/wm8350-regmap.c b/drivers/mfd/wm8350-regmap.c
new file mode 100644 (file)
index 0000000..974678d
--- /dev/null
@@ -0,0 +1,1347 @@
+/*
+ * wm8350-regmap.c  --  Wolfson Microelectronics WM8350 register map
+ *
+ * This file splits out the tables describing the defaults and access
+ * status of the WM8350 registers since they are rather large.
+ *
+ * Copyright 2007, 2008 Wolfson Microelectronics PLC.
+ *
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ */
+
+#include <linux/mfd/wm8350/core.h>
+
+#ifdef CONFIG_MFD_WM8350_CONFIG_MODE_0
+
+#undef WM8350_HAVE_CONFIG_MODE
+#define WM8350_HAVE_CONFIG_MODE
+
+const u16 wm8350_mode0_defaults[] = {
+       0x17FF,     /* R0   - Reset/ID */
+       0x1000,     /* R1   - ID */
+       0x0000,     /* R2 */
+       0x1002,     /* R3   - System Control 1 */
+       0x0004,     /* R4   - System Control 2 */
+       0x0000,     /* R5   - System Hibernate */
+       0x8A00,     /* R6   - Interface Control */
+       0x0000,     /* R7 */
+       0x8000,     /* R8   - Power mgmt (1) */
+       0x0000,     /* R9   - Power mgmt (2) */
+       0x0000,     /* R10  - Power mgmt (3) */
+       0x2000,     /* R11  - Power mgmt (4) */
+       0x0E00,     /* R12  - Power mgmt (5) */
+       0x0000,     /* R13  - Power mgmt (6) */
+       0x0000,     /* R14  - Power mgmt (7) */
+       0x0000,     /* R15 */
+       0x0000,     /* R16  - RTC Seconds/Minutes */
+       0x0100,     /* R17  - RTC Hours/Day */
+       0x0101,     /* R18  - RTC Date/Month */
+       0x1400,     /* R19  - RTC Year */
+       0x0000,     /* R20  - Alarm Seconds/Minutes */
+       0x0000,     /* R21  - Alarm Hours/Day */
+       0x0000,     /* R22  - Alarm Date/Month */
+       0x0320,     /* R23  - RTC Time Control */
+       0x0000,     /* R24  - System Interrupts */
+       0x0000,     /* R25  - Interrupt Status 1 */
+       0x0000,     /* R26  - Interrupt Status 2 */
+       0x0000,     /* R27  - Power Up Interrupt Status */
+       0x0000,     /* R28  - Under Voltage Interrupt status */
+       0x0000,     /* R29  - Over Current Interrupt status */
+       0x0000,     /* R30  - GPIO Interrupt Status */
+       0x0000,     /* R31  - Comparator Interrupt Status */
+       0x3FFF,     /* R32  - System Interrupts Mask */
+       0x0000,     /* R33  - Interrupt Status 1 Mask */
+       0x0000,     /* R34  - Interrupt Status 2 Mask */
+       0x0000,     /* R35  - Power Up Interrupt Status Mask */
+       0x0000,     /* R36  - Under Voltage Interrupt status Mask */
+       0x0000,     /* R37  - Over Current Interrupt status Mask */
+       0x0000,     /* R38  - GPIO Interrupt Status Mask */
+       0x0000,     /* R39  - Comparator Interrupt Status Mask */
+       0x0040,     /* R40  - Clock Control 1 */
+       0x0000,     /* R41  - Clock Control 2 */
+       0x3B00,     /* R42  - FLL Control 1 */
+       0x7086,     /* R43  - FLL Control 2 */
+       0xC226,     /* R44  - FLL Control 3 */
+       0x0000,     /* R45  - FLL Control 4 */
+       0x0000,     /* R46 */
+       0x0000,     /* R47 */
+       0x0000,     /* R48  - DAC Control */
+       0x0000,     /* R49 */
+       0x00C0,     /* R50  - DAC Digital Volume L */
+       0x00C0,     /* R51  - DAC Digital Volume R */
+       0x0000,     /* R52 */
+       0x0040,     /* R53  - DAC LR Rate */
+       0x0000,     /* R54  - DAC Clock Control */
+       0x0000,     /* R55 */
+       0x0000,     /* R56 */
+       0x0000,     /* R57 */
+       0x4000,     /* R58  - DAC Mute */
+       0x0000,     /* R59  - DAC Mute Volume */
+       0x0000,     /* R60  - DAC Side */
+       0x0000,     /* R61 */
+       0x0000,     /* R62 */
+       0x0000,     /* R63 */
+       0x8000,     /* R64  - ADC Control */
+       0x0000,     /* R65 */
+       0x00C0,     /* R66  - ADC Digital Volume L */
+       0x00C0,     /* R67  - ADC Digital Volume R */
+       0x0000,     /* R68  - ADC Divider */
+       0x0000,     /* R69 */
+       0x0040,     /* R70  - ADC LR Rate */
+       0x0000,     /* R71 */
+       0x0303,     /* R72  - Input Control */
+       0x0000,     /* R73  - IN3 Input Control */
+       0x0000,     /* R74  - Mic Bias Control */
+       0x0000,     /* R75 */
+       0x0000,     /* R76  - Output Control */
+       0x0000,     /* R77  - Jack Detect */
+       0x0000,     /* R78  - Anti Pop Control */
+       0x0000,     /* R79 */
+       0x0040,     /* R80  - Left Input Volume */
+       0x0040,     /* R81  - Right Input Volume */
+       0x0000,     /* R82 */
+       0x0000,     /* R83 */
+       0x0000,     /* R84 */
+       0x0000,     /* R85 */
+       0x0000,     /* R86 */
+       0x0000,     /* R87 */
+       0x0800,     /* R88  - Left Mixer Control */
+       0x1000,     /* R89  - Right Mixer Control */
+       0x0000,     /* R90 */
+       0x0000,     /* R91 */
+       0x0000,     /* R92  - OUT3 Mixer Control */
+       0x0000,     /* R93  - OUT4 Mixer Control */
+       0x0000,     /* R94 */
+       0x0000,     /* R95 */
+       0x0000,     /* R96  - Output Left Mixer Volume */
+       0x0000,     /* R97  - Output Right Mixer Volume */
+       0x0000,     /* R98  - Input Mixer Volume L */
+       0x0000,     /* R99  - Input Mixer Volume R */
+       0x0000,     /* R100 - Input Mixer Volume */
+       0x0000,     /* R101 */
+       0x0000,     /* R102 */
+       0x0000,     /* R103 */
+       0x00E4,     /* R104 - LOUT1 Volume */
+       0x00E4,     /* R105 - ROUT1 Volume */
+       0x00E4,     /* R106 - LOUT2 Volume */
+       0x02E4,     /* R107 - ROUT2 Volume */
+       0x0000,     /* R108 */
+       0x0000,     /* R109 */
+       0x0000,     /* R110 */
+       0x0000,     /* R111 - BEEP Volume */
+       0x0A00,     /* R112 - AI Formating */
+       0x0000,     /* R113 - ADC DAC COMP */
+       0x0020,     /* R114 - AI ADC Control */
+       0x0020,     /* R115 - AI DAC Control */
+       0x0000,     /* R116 - AIF Test */
+       0x0000,     /* R117 */
+       0x0000,     /* R118 */
+       0x0000,     /* R119 */
+       0x0000,     /* R120 */
+       0x0000,     /* R121 */
+       0x0000,     /* R122 */
+       0x0000,     /* R123 */
+       0x0000,     /* R124 */
+       0x0000,     /* R125 */
+       0x0000,     /* R126 */
+       0x0000,     /* R127 */
+       0x1FFF,     /* R128 - GPIO Debounce */
+       0x0000,     /* R129 - GPIO Pin pull up Control */
+       0x03FC,     /* R130 - GPIO Pull down Control */
+       0x0000,     /* R131 - GPIO Interrupt Mode */
+       0x0000,     /* R132 */
+       0x0000,     /* R133 - GPIO Control */
+       0x0FFC,     /* R134 - GPIO Configuration (i/o) */
+       0x0FFC,     /* R135 - GPIO Pin Polarity / Type */
+       0x0000,     /* R136 */
+       0x0000,     /* R137 */
+       0x0000,     /* R138 */
+       0x0000,     /* R139 */
+       0x0013,     /* R140 - GPIO Function Select 1 */
+       0x0000,     /* R141 - GPIO Function Select 2 */
+       0x0000,     /* R142 - GPIO Function Select 3 */
+       0x0003,     /* R143 - GPIO Function Select 4 */
+       0x0000,     /* R144 - Digitiser Control (1) */
+       0x0002,     /* R145 - Digitiser Control (2) */
+       0x0000,     /* R146 */
+       0x0000,     /* R147 */
+       0x0000,     /* R148 */
+       0x0000,     /* R149 */
+       0x0000,     /* R150 */
+       0x0000,     /* R151 */
+       0x7000,     /* R152 - AUX1 Readback */
+       0x7000,     /* R153 - AUX2 Readback */
+       0x7000,     /* R154 - AUX3 Readback */
+       0x7000,     /* R155 - AUX4 Readback */
+       0x0000,     /* R156 - USB Voltage Readback */
+       0x0000,     /* R157 - LINE Voltage Readback */
+       0x0000,     /* R158 - BATT Voltage Readback */
+       0x0000,     /* R159 - Chip Temp Readback */
+       0x0000,     /* R160 */
+       0x0000,     /* R161 */
+       0x0000,     /* R162 */
+       0x0000,     /* R163 - Generic Comparator Control */
+       0x0000,     /* R164 - Generic comparator 1 */
+       0x0000,     /* R165 - Generic comparator 2 */
+       0x0000,     /* R166 - Generic comparator 3 */
+       0x0000,     /* R167 - Generic comparator 4 */
+       0xA00F,     /* R168 - Battery Charger Control 1 */
+       0x0B06,     /* R169 - Battery Charger Control 2 */
+       0x0000,     /* R170 - Battery Charger Control 3 */
+       0x0000,     /* R171 */
+       0x0000,     /* R172 - Current Sink Driver A */
+       0x0000,     /* R173 - CSA Flash control */
+       0x0000,     /* R174 - Current Sink Driver B */
+       0x0000,     /* R175 - CSB Flash control */
+       0x0000,     /* R176 - DCDC/LDO requested */
+       0x002D,     /* R177 - DCDC Active options */
+       0x0000,     /* R178 - DCDC Sleep options */
+       0x0025,     /* R179 - Power-check comparator */
+       0x000E,     /* R180 - DCDC1 Control */
+       0x0000,     /* R181 - DCDC1 Timeouts */
+       0x1006,     /* R182 - DCDC1 Low Power */
+       0x0018,     /* R183 - DCDC2 Control */
+       0x0000,     /* R184 - DCDC2 Timeouts */
+       0x0000,     /* R185 */
+       0x0000,     /* R186 - DCDC3 Control */
+       0x0000,     /* R187 - DCDC3 Timeouts */
+       0x0006,     /* R188 - DCDC3 Low Power */
+       0x0000,     /* R189 - DCDC4 Control */
+       0x0000,     /* R190 - DCDC4 Timeouts */
+       0x0006,     /* R191 - DCDC4 Low Power */
+       0x0008,     /* R192 - DCDC5 Control */
+       0x0000,     /* R193 - DCDC5 Timeouts */
+       0x0000,     /* R194 */
+       0x0000,     /* R195 - DCDC6 Control */
+       0x0000,     /* R196 - DCDC6 Timeouts */
+       0x0006,     /* R197 - DCDC6 Low Power */
+       0x0000,     /* R198 */
+       0x0003,     /* R199 - Limit Switch Control */
+       0x001C,     /* R200 - LDO1 Control */
+       0x0000,     /* R201 - LDO1 Timeouts */
+       0x001C,     /* R202 - LDO1 Low Power */
+       0x001B,     /* R203 - LDO2 Control */
+       0x0000,     /* R204 - LDO2 Timeouts */
+       0x001C,     /* R205 - LDO2 Low Power */
+       0x001B,     /* R206 - LDO3 Control */
+       0x0000,     /* R207 - LDO3 Timeouts */
+       0x001C,     /* R208 - LDO3 Low Power */
+       0x001B,     /* R209 - LDO4 Control */
+       0x0000,     /* R210 - LDO4 Timeouts */
+       0x001C,     /* R211 - LDO4 Low Power */
+       0x0000,     /* R212 */
+       0x0000,     /* R213 */
+       0x0000,     /* R214 */
+       0x0000,     /* R215 - VCC_FAULT Masks */
+       0x001F,     /* R216 - Main Bandgap Control */
+       0x0000,     /* R217 - OSC Control */
+       0x9000,     /* R218 - RTC Tick Control */
+       0x0000,     /* R219 */
+       0x4000,     /* R220 - RAM BIST 1 */
+       0x0000,     /* R221 */
+       0x0000,     /* R222 */
+       0x0000,     /* R223 */
+       0x0000,     /* R224 */
+       0x0000,     /* R225 - DCDC/LDO status */
+       0x0000,     /* R226 */
+       0x0000,     /* R227 */
+       0x0000,     /* R228 */
+       0x0000,     /* R229 */
+       0xE000,     /* R230 - GPIO Pin Status */
+       0x0000,     /* R231 */
+       0x0000,     /* R232 */
+       0x0000,     /* R233 */
+       0x0000,     /* R234 */
+       0x0000,     /* R235 */
+       0x0000,     /* R236 */
+       0x0000,     /* R237 */
+       0x0000,     /* R238 */
+       0x0000,     /* R239 */
+       0x0000,     /* R240 */
+       0x0000,     /* R241 */
+       0x0000,     /* R242 */
+       0x0000,     /* R243 */
+       0x0000,     /* R244 */
+       0x0000,     /* R245 */
+       0x0000,     /* R246 */
+       0x0000,     /* R247 */
+       0x0000,     /* R248 */
+       0x0000,     /* R249 */
+       0x0000,     /* R250 */
+       0x0000,     /* R251 */
+       0x0000,     /* R252 */
+       0x0000,     /* R253 */
+       0x0000,     /* R254 */
+       0x0000,     /* R255 */
+};
+#endif
+
+#ifdef CONFIG_MFD_WM8350_CONFIG_MODE_1
+
+#undef WM8350_HAVE_CONFIG_MODE
+#define WM8350_HAVE_CONFIG_MODE
+
+const u16 wm8350_mode1_defaults[] = {
+       0x17FF,     /* R0   - Reset/ID */
+       0x1000,     /* R1   - ID */
+       0x0000,     /* R2 */
+       0x1002,     /* R3   - System Control 1 */
+       0x0014,     /* R4   - System Control 2 */
+       0x0000,     /* R5   - System Hibernate */
+       0x8A00,     /* R6   - Interface Control */
+       0x0000,     /* R7 */
+       0x8000,     /* R8   - Power mgmt (1) */
+       0x0000,     /* R9   - Power mgmt (2) */
+       0x0000,     /* R10  - Power mgmt (3) */
+       0x2000,     /* R11  - Power mgmt (4) */
+       0x0E00,     /* R12  - Power mgmt (5) */
+       0x0000,     /* R13  - Power mgmt (6) */
+       0x0000,     /* R14  - Power mgmt (7) */
+       0x0000,     /* R15 */
+       0x0000,     /* R16  - RTC Seconds/Minutes */
+       0x0100,     /* R17  - RTC Hours/Day */
+       0x0101,     /* R18  - RTC Date/Month */
+       0x1400,     /* R19  - RTC Year */
+       0x0000,     /* R20  - Alarm Seconds/Minutes */
+       0x0000,     /* R21  - Alarm Hours/Day */
+       0x0000,     /* R22  - Alarm Date/Month */
+       0x0320,     /* R23  - RTC Time Control */
+       0x0000,     /* R24  - System Interrupts */
+       0x0000,     /* R25  - Interrupt Status 1 */
+       0x0000,     /* R26  - Interrupt Status 2 */
+       0x0000,     /* R27  - Power Up Interrupt Status */
+       0x0000,     /* R28  - Under Voltage Interrupt status */
+       0x0000,     /* R29  - Over Current Interrupt status */
+       0x0000,     /* R30  - GPIO Interrupt Status */
+       0x0000,     /* R31  - Comparator Interrupt Status */
+       0x3FFF,     /* R32  - System Interrupts Mask */
+       0x0000,     /* R33  - Interrupt Status 1 Mask */
+       0x0000,     /* R34  - Interrupt Status 2 Mask */
+       0x0000,     /* R35  - Power Up Interrupt Status Mask */
+       0x0000,     /* R36  - Under Voltage Interrupt status Mask */
+       0x0000,     /* R37  - Over Current Interrupt status Mask */
+       0x0000,     /* R38  - GPIO Interrupt Status Mask */
+       0x0000,     /* R39  - Comparator Interrupt Status Mask */
+       0x0040,     /* R40  - Clock Control 1 */
+       0x0000,     /* R41  - Clock Control 2 */
+       0x3B00,     /* R42  - FLL Control 1 */
+       0x7086,     /* R43  - FLL Control 2 */
+       0xC226,     /* R44  - FLL Control 3 */
+       0x0000,     /* R45  - FLL Control 4 */
+       0x0000,     /* R46 */
+       0x0000,     /* R47 */
+       0x0000,     /* R48  - DAC Control */
+       0x0000,     /* R49 */
+       0x00C0,     /* R50  - DAC Digital Volume L */
+       0x00C0,     /* R51  - DAC Digital Volume R */
+       0x0000,     /* R52 */
+       0x0040,     /* R53  - DAC LR Rate */
+       0x0000,     /* R54  - DAC Clock Control */
+       0x0000,     /* R55 */
+       0x0000,     /* R56 */
+       0x0000,     /* R57 */
+       0x4000,     /* R58  - DAC Mute */
+       0x0000,     /* R59  - DAC Mute Volume */
+       0x0000,     /* R60  - DAC Side */
+       0x0000,     /* R61 */
+       0x0000,     /* R62 */
+       0x0000,     /* R63 */
+       0x8000,     /* R64  - ADC Control */
+       0x0000,     /* R65 */
+       0x00C0,     /* R66  - ADC Digital Volume L */
+       0x00C0,     /* R67  - ADC Digital Volume R */
+       0x0000,     /* R68  - ADC Divider */
+       0x0000,     /* R69 */
+       0x0040,     /* R70  - ADC LR Rate */
+       0x0000,     /* R71 */
+       0x0303,     /* R72  - Input Control */
+       0x0000,     /* R73  - IN3 Input Control */
+       0x0000,     /* R74  - Mic Bias Control */
+       0x0000,     /* R75 */
+       0x0000,     /* R76  - Output Control */
+       0x0000,     /* R77  - Jack Detect */
+       0x0000,     /* R78  - Anti Pop Control */
+       0x0000,     /* R79 */
+       0x0040,     /* R80  - Left Input Volume */
+       0x0040,     /* R81  - Right Input Volume */
+       0x0000,     /* R82 */
+       0x0000,     /* R83 */
+       0x0000,     /* R84 */
+       0x0000,     /* R85 */
+       0x0000,     /* R86 */
+       0x0000,     /* R87 */
+       0x0800,     /* R88  - Left Mixer Control */
+       0x1000,     /* R89  - Right Mixer Control */
+       0x0000,     /* R90 */
+       0x0000,     /* R91 */
+       0x0000,     /* R92  - OUT3 Mixer Control */
+       0x0000,     /* R93  - OUT4 Mixer Control */
+       0x0000,     /* R94 */
+       0x0000,     /* R95 */
+       0x0000,     /* R96  - Output Left Mixer Volume */
+       0x0000,     /* R97  - Output Right Mixer Volume */
+       0x0000,     /* R98  - Input Mixer Volume L */
+       0x0000,     /* R99  - Input Mixer Volume R */
+       0x0000,     /* R100 - Input Mixer Volume */
+       0x0000,     /* R101 */
+       0x0000,     /* R102 */
+       0x0000,     /* R103 */
+       0x00E4,     /* R104 - LOUT1 Volume */
+       0x00E4,     /* R105 - ROUT1 Volume */
+       0x00E4,     /* R106 - LOUT2 Volume */
+       0x02E4,     /* R107 - ROUT2 Volume */
+       0x0000,     /* R108 */
+       0x0000,     /* R109 */
+       0x0000,     /* R110 */
+       0x0000,     /* R111 - BEEP Volume */
+       0x0A00,     /* R112 - AI Formating */
+       0x0000,     /* R113 - ADC DAC COMP */
+       0x0020,     /* R114 - AI ADC Control */
+       0x0020,     /* R115 - AI DAC Control */
+       0x0000,     /* R116 - AIF Test */
+       0x0000,     /* R117 */
+       0x0000,     /* R118 */
+       0x0000,     /* R119 */
+       0x0000,     /* R120 */
+       0x0000,     /* R121 */
+       0x0000,     /* R122 */
+       0x0000,     /* R123 */
+       0x0000,     /* R124 */
+       0x0000,     /* R125 */
+       0x0000,     /* R126 */
+       0x0000,     /* R127 */
+       0x1FFF,     /* R128 - GPIO Debounce */
+       0x0000,     /* R129 - GPIO Pin pull up Control */
+       0x03FC,     /* R130 - GPIO Pull down Control */
+       0x0000,     /* R131 - GPIO Interrupt Mode */
+       0x0000,     /* R132 */
+       0x0000,     /* R133 - GPIO Control */
+       0x00FB,     /* R134 - GPIO Configuration (i/o) */
+       0x04FE,     /* R135 - GPIO Pin Polarity / Type */
+       0x0000,     /* R136 */
+       0x0000,     /* R137 */
+       0x0000,     /* R138 */
+       0x0000,     /* R139 */
+       0x0312,     /* R140 - GPIO Function Select 1 */
+       0x1003,     /* R141 - GPIO Function Select 2 */
+       0x1331,     /* R142 - GPIO Function Select 3 */
+       0x0003,     /* R143 - GPIO Function Select 4 */
+       0x0000,     /* R144 - Digitiser Control (1) */
+       0x0002,     /* R145 - Digitiser Control (2) */
+       0x0000,     /* R146 */
+       0x0000,     /* R147 */
+       0x0000,     /* R148 */
+       0x0000,     /* R149 */
+       0x0000,     /* R150 */
+       0x0000,     /* R151 */
+       0x7000,     /* R152 - AUX1 Readback */
+       0x7000,     /* R153 - AUX2 Readback */
+       0x7000,     /* R154 - AUX3 Readback */
+       0x7000,     /* R155 - AUX4 Readback */
+       0x0000,     /* R156 - USB Voltage Readback */
+       0x0000,     /* R157 - LINE Voltage Readback */
+       0x0000,     /* R158 - BATT Voltage Readback */
+       0x0000,     /* R159 - Chip Temp Readback */
+       0x0000,     /* R160 */
+       0x0000,     /* R161 */
+       0x0000,     /* R162 */
+       0x0000,     /* R163 - Generic Comparator Control */
+       0x0000,     /* R164 - Generic comparator 1 */
+       0x0000,     /* R165 - Generic comparator 2 */
+       0x0000,     /* R166 - Generic comparator 3 */
+       0x0000,     /* R167 - Generic comparator 4 */
+       0xA00F,     /* R168 - Battery Charger Control 1 */
+       0x0B06,     /* R169 - Battery Charger Control 2 */
+       0x0000,     /* R170 - Battery Charger Control 3 */
+       0x0000,     /* R171 */
+       0x0000,     /* R172 - Current Sink Driver A */
+       0x0000,     /* R173 - CSA Flash control */
+       0x0000,     /* R174 - Current Sink Driver B */
+       0x0000,     /* R175 - CSB Flash control */
+       0x0000,     /* R176 - DCDC/LDO requested */
+       0x002D,     /* R177 - DCDC Active options */
+       0x0000,     /* R178 - DCDC Sleep options */
+       0x0025,     /* R179 - Power-check comparator */
+       0x0062,     /* R180 - DCDC1 Control */
+       0x0400,     /* R181 - DCDC1 Timeouts */
+       0x1006,     /* R182 - DCDC1 Low Power */
+       0x0018,     /* R183 - DCDC2 Control */
+       0x0000,     /* R184 - DCDC2 Timeouts */
+       0x0000,     /* R185 */
+       0x0026,     /* R186 - DCDC3 Control */
+       0x0400,     /* R187 - DCDC3 Timeouts */
+       0x0006,     /* R188 - DCDC3 Low Power */
+       0x0062,     /* R189 - DCDC4 Control */
+       0x0400,     /* R190 - DCDC4 Timeouts */
+       0x0006,     /* R191 - DCDC4 Low Power */
+       0x0008,     /* R192 - DCDC5 Control */
+       0x0000,     /* R193 - DCDC5 Timeouts */
+       0x0000,     /* R194 */
+       0x0026,     /* R195 - DCDC6 Control */
+       0x0800,     /* R196 - DCDC6 Timeouts */
+       0x0006,     /* R197 - DCDC6 Low Power */
+       0x0000,     /* R198 */
+       0x0003,     /* R199 - Limit Switch Control */
+       0x0006,     /* R200 - LDO1 Control */
+       0x0400,     /* R201 - LDO1 Timeouts */
+       0x001C,     /* R202 - LDO1 Low Power */
+       0x0006,     /* R203 - LDO2 Control */
+       0x0400,     /* R204 - LDO2 Timeouts */
+       0x001C,     /* R205 - LDO2 Low Power */
+       0x001B,     /* R206 - LDO3 Control */
+       0x0000,     /* R207 - LDO3 Timeouts */
+       0x001C,     /* R208 - LDO3 Low Power */
+       0x001B,     /* R209 - LDO4 Control */
+       0x0000,     /* R210 - LDO4 Timeouts */
+       0x001C,     /* R211 - LDO4 Low Power */
+       0x0000,     /* R212 */
+       0x0000,     /* R213 */
+       0x0000,     /* R214 */
+       0x0000,     /* R215 - VCC_FAULT Masks */
+       0x001F,     /* R216 - Main Bandgap Control */
+       0x0000,     /* R217 - OSC Control */
+       0x9000,     /* R218 - RTC Tick Control */
+       0x0000,     /* R219 */
+       0x4000,     /* R220 - RAM BIST 1 */
+       0x0000,     /* R221 */
+       0x0000,     /* R222 */
+       0x0000,     /* R223 */
+       0x0000,     /* R224 */
+       0x0000,     /* R225 - DCDC/LDO status */
+       0x0000,     /* R226 */
+       0x0000,     /* R227 */
+       0x0000,     /* R228 */
+       0x0000,     /* R229 */
+       0xE000,     /* R230 - GPIO Pin Status */
+       0x0000,     /* R231 */
+       0x0000,     /* R232 */
+       0x0000,     /* R233 */
+       0x0000,     /* R234 */
+       0x0000,     /* R235 */
+       0x0000,     /* R236 */
+       0x0000,     /* R237 */
+       0x0000,     /* R238 */
+       0x0000,     /* R239 */
+       0x0000,     /* R240 */
+       0x0000,     /* R241 */
+       0x0000,     /* R242 */
+       0x0000,     /* R243 */
+       0x0000,     /* R244 */
+       0x0000,     /* R245 */
+       0x0000,     /* R246 */
+       0x0000,     /* R247 */
+       0x0000,     /* R248 */
+       0x0000,     /* R249 */
+       0x0000,     /* R250 */
+       0x0000,     /* R251 */
+       0x0000,     /* R252 */
+       0x0000,     /* R253 */
+       0x0000,     /* R254 */
+       0x0000,     /* R255 */
+};
+#endif
+
+#ifdef CONFIG_MFD_WM8350_CONFIG_MODE_2
+
+#undef WM8350_HAVE_CONFIG_MODE
+#define WM8350_HAVE_CONFIG_MODE
+
+const u16 wm8350_mode2_defaults[] = {
+       0x17FF,     /* R0   - Reset/ID */
+       0x1000,     /* R1   - ID */
+       0x0000,     /* R2 */
+       0x1002,     /* R3   - System Control 1 */
+       0x0014,     /* R4   - System Control 2 */
+       0x0000,     /* R5   - System Hibernate */
+       0x8A00,     /* R6   - Interface Control */
+       0x0000,     /* R7 */
+       0x8000,     /* R8   - Power mgmt (1) */
+       0x0000,     /* R9   - Power mgmt (2) */
+       0x0000,     /* R10  - Power mgmt (3) */
+       0x2000,     /* R11  - Power mgmt (4) */
+       0x0E00,     /* R12  - Power mgmt (5) */
+       0x0000,     /* R13  - Power mgmt (6) */
+       0x0000,     /* R14  - Power mgmt (7) */
+       0x0000,     /* R15 */
+       0x0000,     /* R16  - RTC Seconds/Minutes */
+       0x0100,     /* R17  - RTC Hours/Day */
+       0x0101,     /* R18  - RTC Date/Month */
+       0x1400,     /* R19  - RTC Year */
+       0x0000,     /* R20  - Alarm Seconds/Minutes */
+       0x0000,     /* R21  - Alarm Hours/Day */
+       0x0000,     /* R22  - Alarm Date/Month */
+       0x0320,     /* R23  - RTC Time Control */
+       0x0000,     /* R24  - System Interrupts */
+       0x0000,     /* R25  - Interrupt Status 1 */
+       0x0000,     /* R26  - Interrupt Status 2 */
+       0x0000,     /* R27  - Power Up Interrupt Status */
+       0x0000,     /* R28  - Under Voltage Interrupt status */
+       0x0000,     /* R29  - Over Current Interrupt status */
+       0x0000,     /* R30  - GPIO Interrupt Status */
+       0x0000,     /* R31  - Comparator Interrupt Status */
+       0x3FFF,     /* R32  - System Interrupts Mask */
+       0x0000,     /* R33  - Interrupt Status 1 Mask */
+       0x0000,     /* R34  - Interrupt Status 2 Mask */
+       0x0000,     /* R35  - Power Up Interrupt Status Mask */
+       0x0000,     /* R36  - Under Voltage Interrupt status Mask */
+       0x0000,     /* R37  - Over Current Interrupt status Mask */
+       0x0000,     /* R38  - GPIO Interrupt Status Mask */
+       0x0000,     /* R39  - Comparator Interrupt Status Mask */
+       0x0040,     /* R40  - Clock Control 1 */
+       0x0000,     /* R41  - Clock Control 2 */
+       0x3B00,     /* R42  - FLL Control 1 */
+       0x7086,     /* R43  - FLL Control 2 */
+       0xC226,     /* R44  - FLL Control 3 */
+       0x0000,     /* R45  - FLL Control 4 */
+       0x0000,     /* R46 */
+       0x0000,     /* R47 */
+       0x0000,     /* R48  - DAC Control */
+       0x0000,     /* R49 */
+       0x00C0,     /* R50  - DAC Digital Volume L */
+       0x00C0,     /* R51  - DAC Digital Volume R */
+       0x0000,     /* R52 */
+       0x0040,     /* R53  - DAC LR Rate */
+       0x0000,     /* R54  - DAC Clock Control */
+       0x0000,     /* R55 */
+       0x0000,     /* R56 */
+       0x0000,     /* R57 */
+       0x4000,     /* R58  - DAC Mute */
+       0x0000,     /* R59  - DAC Mute Volume */
+       0x0000,     /* R60  - DAC Side */
+       0x0000,     /* R61 */
+       0x0000,     /* R62 */
+       0x0000,     /* R63 */
+       0x8000,     /* R64  - ADC Control */
+       0x0000,     /* R65 */
+       0x00C0,     /* R66  - ADC Digital Volume L */
+       0x00C0,     /* R67  - ADC Digital Volume R */
+       0x0000,     /* R68  - ADC Divider */
+       0x0000,     /* R69 */
+       0x0040,     /* R70  - ADC LR Rate */
+       0x0000,     /* R71 */
+       0x0303,     /* R72  - Input Control */
+       0x0000,     /* R73  - IN3 Input Control */
+       0x0000,     /* R74  - Mic Bias Control */
+       0x0000,     /* R75 */
+       0x0000,     /* R76  - Output Control */
+       0x0000,     /* R77  - Jack Detect */
+       0x0000,     /* R78  - Anti Pop Control */
+       0x0000,     /* R79 */
+       0x0040,     /* R80  - Left Input Volume */
+       0x0040,     /* R81  - Right Input Volume */
+       0x0000,     /* R82 */
+       0x0000,     /* R83 */
+       0x0000,     /* R84 */
+       0x0000,     /* R85 */
+       0x0000,     /* R86 */
+       0x0000,     /* R87 */
+       0x0800,     /* R88  - Left Mixer Control */
+       0x1000,     /* R89  - Right Mixer Control */
+       0x0000,     /* R90 */
+       0x0000,     /* R91 */
+       0x0000,     /* R92  - OUT3 Mixer Control */
+       0x0000,     /* R93  - OUT4 Mixer Control */
+       0x0000,     /* R94 */
+       0x0000,     /* R95 */
+       0x0000,     /* R96  - Output Left Mixer Volume */
+       0x0000,     /* R97  - Output Right Mixer Volume */
+       0x0000,     /* R98  - Input Mixer Volume L */
+       0x0000,     /* R99  - Input Mixer Volume R */
+       0x0000,     /* R100 - Input Mixer Volume */
+       0x0000,     /* R101 */
+       0x0000,     /* R102 */
+       0x0000,     /* R103 */
+       0x00E4,     /* R104 - LOUT1 Volume */
+       0x00E4,     /* R105 - ROUT1 Volume */
+       0x00E4,     /* R106 - LOUT2 Volume */
+       0x02E4,     /* R107 - ROUT2 Volume */
+       0x0000,     /* R108 */
+       0x0000,     /* R109 */
+       0x0000,     /* R110 */
+       0x0000,     /* R111 - BEEP Volume */
+       0x0A00,     /* R112 - AI Formating */
+       0x0000,     /* R113 - ADC DAC COMP */
+       0x0020,     /* R114 - AI ADC Control */
+       0x0020,     /* R115 - AI DAC Control */
+       0x0000,     /* R116 - AIF Test */
+       0x0000,     /* R117 */
+       0x0000,     /* R118 */
+       0x0000,     /* R119 */
+       0x0000,     /* R120 */
+       0x0000,     /* R121 */
+       0x0000,     /* R122 */
+       0x0000,     /* R123 */
+       0x0000,     /* R124 */
+       0x0000,     /* R125 */
+       0x0000,     /* R126 */
+       0x0000,     /* R127 */
+       0x1FFF,     /* R128 - GPIO Debounce */
+       0x0000,     /* R129 - GPIO Pin pull up Control */
+       0x03FC,     /* R130 - GPIO Pull down Control */
+       0x0000,     /* R131 - GPIO Interrupt Mode */
+       0x0000,     /* R132 */
+       0x0000,     /* R133 - GPIO Control */
+       0x08FB,     /* R134 - GPIO Configuration (i/o) */
+       0x0CFE,     /* R135 - GPIO Pin Polarity / Type */
+       0x0000,     /* R136 */
+       0x0000,     /* R137 */
+       0x0000,     /* R138 */
+       0x0000,     /* R139 */
+       0x0312,     /* R140 - GPIO Function Select 1 */
+       0x0003,     /* R141 - GPIO Function Select 2 */
+       0x2331,     /* R142 - GPIO Function Select 3 */
+       0x0003,     /* R143 - GPIO Function Select 4 */
+       0x0000,     /* R144 - Digitiser Control (1) */
+       0x0002,     /* R145 - Digitiser Control (2) */
+       0x0000,     /* R146 */
+       0x0000,     /* R147 */
+       0x0000,     /* R148 */
+       0x0000,     /* R149 */
+       0x0000,     /* R150 */
+       0x0000,     /* R151 */
+       0x7000,     /* R152 - AUX1 Readback */
+       0x7000,     /* R153 - AUX2 Readback */
+       0x7000,     /* R154 - AUX3 Readback */
+       0x7000,     /* R155 - AUX4 Readback */
+       0x0000,     /* R156 - USB Voltage Readback */
+       0x0000,     /* R157 - LINE Voltage Readback */
+       0x0000,     /* R158 - BATT Voltage Readback */
+       0x0000,     /* R159 - Chip Temp Readback */
+       0x0000,     /* R160 */
+       0x0000,     /* R161 */
+       0x0000,     /* R162 */
+       0x0000,     /* R163 - Generic Comparator Control */
+       0x0000,     /* R164 - Generic comparator 1 */
+       0x0000,     /* R165 - Generic comparator 2 */
+       0x0000,     /* R166 - Generic comparator 3 */
+       0x0000,     /* R167 - Generic comparator 4 */
+       0xA00F,     /* R168 - Battery Charger Control 1 */
+       0x0B06,     /* R169 - Battery Charger Control 2 */
+       0x0000,     /* R170 - Battery Charger Control 3 */
+       0x0000,     /* R171 */
+       0x0000,     /* R172 - Current Sink Driver A */
+       0x0000,     /* R173 - CSA Flash control */
+       0x0000,     /* R174 - Current Sink Driver B */
+       0x0000,     /* R175 - CSB Flash control */
+       0x0000,     /* R176 - DCDC/LDO requested */
+       0x002D,     /* R177 - DCDC Active options */
+       0x0000,     /* R178 - DCDC Sleep options */
+       0x0025,     /* R179 - Power-check comparator */
+       0x000E,     /* R180 - DCDC1 Control */
+       0x0400,     /* R181 - DCDC1 Timeouts */
+       0x1006,     /* R182 - DCDC1 Low Power */
+       0x0018,     /* R183 - DCDC2 Control */
+       0x0000,     /* R184 - DCDC2 Timeouts */
+       0x0000,     /* R185 */
+       0x002E,     /* R186 - DCDC3 Control */
+       0x0800,     /* R187 - DCDC3 Timeouts */
+       0x0006,     /* R188 - DCDC3 Low Power */
+       0x000E,     /* R189 - DCDC4 Control */
+       0x0800,     /* R190 - DCDC4 Timeouts */
+       0x0006,     /* R191 - DCDC4 Low Power */
+       0x0008,     /* R192 - DCDC5 Control */
+       0x0000,     /* R193 - DCDC5 Timeouts */
+       0x0000,     /* R194 */
+       0x0026,     /* R195 - DCDC6 Control */
+       0x0C00,     /* R196 - DCDC6 Timeouts */
+       0x0006,     /* R197 - DCDC6 Low Power */
+       0x0000,     /* R198 */
+       0x0003,     /* R199 - Limit Switch Control */
+       0x001A,     /* R200 - LDO1 Control */
+       0x0800,     /* R201 - LDO1 Timeouts */
+       0x001C,     /* R202 - LDO1 Low Power */
+       0x0010,     /* R203 - LDO2 Control */
+       0x0800,     /* R204 - LDO2 Timeouts */
+       0x001C,     /* R205 - LDO2 Low Power */
+       0x000A,     /* R206 - LDO3 Control */
+       0x0C00,     /* R207 - LDO3 Timeouts */
+       0x001C,     /* R208 - LDO3 Low Power */
+       0x001A,     /* R209 - LDO4 Control */
+       0x0800,     /* R210 - LDO4 Timeouts */
+       0x001C,     /* R211 - LDO4 Low Power */
+       0x0000,     /* R212 */
+       0x0000,     /* R213 */
+       0x0000,     /* R214 */
+       0x0000,     /* R215 - VCC_FAULT Masks */
+       0x001F,     /* R216 - Main Bandgap Control */
+       0x0000,     /* R217 - OSC Control */
+       0x9000,     /* R218 - RTC Tick Control */
+       0x0000,     /* R219 */
+       0x4000,     /* R220 - RAM BIST 1 */
+       0x0000,     /* R221 */
+       0x0000,     /* R222 */
+       0x0000,     /* R223 */
+       0x0000,     /* R224 */
+       0x0000,     /* R225 - DCDC/LDO status */
+       0x0000,     /* R226 */
+       0x0000,     /* R227 */
+       0x0000,     /* R228 */
+       0x0000,     /* R229 */
+       0xE000,     /* R230 - GPIO Pin Status */
+       0x0000,     /* R231 */
+       0x0000,     /* R232 */
+       0x0000,     /* R233 */
+       0x0000,     /* R234 */
+       0x0000,     /* R235 */
+       0x0000,     /* R236 */
+       0x0000,     /* R237 */
+       0x0000,     /* R238 */
+       0x0000,     /* R239 */
+       0x0000,     /* R240 */
+       0x0000,     /* R241 */
+       0x0000,     /* R242 */
+       0x0000,     /* R243 */
+       0x0000,     /* R244 */
+       0x0000,     /* R245 */
+       0x0000,     /* R246 */
+       0x0000,     /* R247 */
+       0x0000,     /* R248 */
+       0x0000,     /* R249 */
+       0x0000,     /* R250 */
+       0x0000,     /* R251 */
+       0x0000,     /* R252 */
+       0x0000,     /* R253 */
+       0x0000,     /* R254 */
+       0x0000,     /* R255 */
+};
+#endif
+
+#ifdef CONFIG_MFD_WM8350_CONFIG_MODE_3
+
+#undef WM8350_HAVE_CONFIG_MODE
+#define WM8350_HAVE_CONFIG_MODE
+
+const u16 wm8350_mode3_defaults[] = {
+       0x17FF,     /* R0   - Reset/ID */
+       0x1000,     /* R1   - ID */
+       0x0000,     /* R2 */
+       0x1000,     /* R3   - System Control 1 */
+       0x0004,     /* R4   - System Control 2 */
+       0x0000,     /* R5   - System Hibernate */
+       0x8A00,     /* R6   - Interface Control */
+       0x0000,     /* R7 */
+       0x8000,     /* R8   - Power mgmt (1) */
+       0x0000,     /* R9   - Power mgmt (2) */
+       0x0000,     /* R10  - Power mgmt (3) */
+       0x2000,     /* R11  - Power mgmt (4) */
+       0x0E00,     /* R12  - Power mgmt (5) */
+       0x0000,     /* R13  - Power mgmt (6) */
+       0x0000,     /* R14  - Power mgmt (7) */
+       0x0000,     /* R15 */
+       0x0000,     /* R16  - RTC Seconds/Minutes */
+       0x0100,     /* R17  - RTC Hours/Day */
+       0x0101,     /* R18  - RTC Date/Month */
+       0x1400,     /* R19  - RTC Year */
+       0x0000,     /* R20  - Alarm Seconds/Minutes */
+       0x0000,     /* R21  - Alarm Hours/Day */
+       0x0000,     /* R22  - Alarm Date/Month */
+       0x0320,     /* R23  - RTC Time Control */
+       0x0000,     /* R24  - System Interrupts */
+       0x0000,     /* R25  - Interrupt Status 1 */
+       0x0000,     /* R26  - Interrupt Status 2 */
+       0x0000,     /* R27  - Power Up Interrupt Status */
+       0x0000,     /* R28  - Under Voltage Interrupt status */
+       0x0000,     /* R29  - Over Current Interrupt status */
+       0x0000,     /* R30  - GPIO Interrupt Status */
+       0x0000,     /* R31  - Comparator Interrupt Status */
+       0x3FFF,     /* R32  - System Interrupts Mask */
+       0x0000,     /* R33  - Interrupt Status 1 Mask */
+       0x0000,     /* R34  - Interrupt Status 2 Mask */
+       0x0000,     /* R35  - Power Up Interrupt Status Mask */
+       0x0000,     /* R36  - Under Voltage Interrupt status Mask */
+       0x0000,     /* R37  - Over Current Interrupt status Mask */
+       0x0000,     /* R38  - GPIO Interrupt Status Mask */
+       0x0000,     /* R39  - Comparator Interrupt Status Mask */
+       0x0040,     /* R40  - Clock Control 1 */
+       0x0000,     /* R41  - Clock Control 2 */
+       0x3B00,     /* R42  - FLL Control 1 */
+       0x7086,     /* R43  - FLL Control 2 */
+       0xC226,     /* R44  - FLL Control 3 */
+       0x0000,     /* R45  - FLL Control 4 */
+       0x0000,     /* R46 */
+       0x0000,     /* R47 */
+       0x0000,     /* R48  - DAC Control */
+       0x0000,     /* R49 */
+       0x00C0,     /* R50  - DAC Digital Volume L */
+       0x00C0,     /* R51  - DAC Digital Volume R */
+       0x0000,     /* R52 */
+       0x0040,     /* R53  - DAC LR Rate */
+       0x0000,     /* R54  - DAC Clock Control */
+       0x0000,     /* R55 */
+       0x0000,     /* R56 */
+       0x0000,     /* R57 */
+       0x4000,     /* R58  - DAC Mute */
+       0x0000,     /* R59  - DAC Mute Volume */
+       0x0000,     /* R60  - DAC Side */
+       0x0000,     /* R61 */
+       0x0000,     /* R62 */
+       0x0000,     /* R63 */
+       0x8000,     /* R64  - ADC Control */
+       0x0000,     /* R65 */
+       0x00C0,     /* R66  - ADC Digital Volume L */
+       0x00C0,     /* R67  - ADC Digital Volume R */
+       0x0000,     /* R68  - ADC Divider */
+       0x0000,     /* R69 */
+       0x0040,     /* R70  - ADC LR Rate */
+       0x0000,     /* R71 */
+       0x0303,     /* R72  - Input Control */
+       0x0000,     /* R73  - IN3 Input Control */
+       0x0000,     /* R74  - Mic Bias Control */
+       0x0000,     /* R75 */
+       0x0000,     /* R76  - Output Control */
+       0x0000,     /* R77  - Jack Detect */
+       0x0000,     /* R78  - Anti Pop Control */
+       0x0000,     /* R79 */
+       0x0040,     /* R80  - Left Input Volume */
+       0x0040,     /* R81  - Right Input Volume */
+       0x0000,     /* R82 */
+       0x0000,     /* R83 */
+       0x0000,     /* R84 */
+       0x0000,     /* R85 */
+       0x0000,     /* R86 */
+       0x0000,     /* R87 */
+       0x0800,     /* R88  - Left Mixer Control */
+       0x1000,     /* R89  - Right Mixer Control */
+       0x0000,     /* R90 */
+       0x0000,     /* R91 */
+       0x0000,     /* R92  - OUT3 Mixer Control */
+       0x0000,     /* R93  - OUT4 Mixer Control */
+       0x0000,     /* R94 */
+       0x0000,     /* R95 */
+       0x0000,     /* R96  - Output Left Mixer Volume */
+       0x0000,     /* R97  - Output Right Mixer Volume */
+       0x0000,     /* R98  - Input Mixer Volume L */
+       0x0000,     /* R99  - Input Mixer Volume R */
+       0x0000,     /* R100 - Input Mixer Volume */
+       0x0000,     /* R101 */
+       0x0000,     /* R102 */
+       0x0000,     /* R103 */
+       0x00E4,     /* R104 - LOUT1 Volume */
+       0x00E4,     /* R105 - ROUT1 Volume */
+       0x00E4,     /* R106 - LOUT2 Volume */
+       0x02E4,     /* R107 - ROUT2 Volume */
+       0x0000,     /* R108 */
+       0x0000,     /* R109 */
+       0x0000,     /* R110 */
+       0x0000,     /* R111 - BEEP Volume */
+       0x0A00,     /* R112 - AI Formating */
+       0x0000,     /* R113 - ADC DAC COMP */
+       0x0020,     /* R114 - AI ADC Control */
+       0x0020,     /* R115 - AI DAC Control */
+       0x0000,     /* R116 - AIF Test */
+       0x0000,     /* R117 */
+       0x0000,     /* R118 */
+       0x0000,     /* R119 */
+       0x0000,     /* R120 */
+       0x0000,     /* R121 */
+       0x0000,     /* R122 */
+       0x0000,     /* R123 */
+       0x0000,     /* R124 */
+       0x0000,     /* R125 */
+       0x0000,     /* R126 */
+       0x0000,     /* R127 */
+       0x1FFF,     /* R128 - GPIO Debounce */
+       0x0000,     /* R129 - GPIO Pin pull up Control */
+       0x03FC,     /* R130 - GPIO Pull down Control */
+       0x0000,     /* R131 - GPIO Interrupt Mode */
+       0x0000,     /* R132 */
+       0x0000,     /* R133 - GPIO Control */
+       0x0A7B,     /* R134 - GPIO Configuration (i/o) */
+       0x06FE,     /* R135 - GPIO Pin Polarity / Type */
+       0x0000,     /* R136 */
+       0x0000,     /* R137 */
+       0x0000,     /* R138 */
+       0x0000,     /* R139 */
+       0x1312,     /* R140 - GPIO Function Select 1 */
+       0x1030,     /* R141 - GPIO Function Select 2 */
+       0x2231,     /* R142 - GPIO Function Select 3 */
+       0x0003,     /* R143 - GPIO Function Select 4 */
+       0x0000,     /* R144 - Digitiser Control (1) */
+       0x0002,     /* R145 - Digitiser Control (2) */
+       0x0000,     /* R146 */
+       0x0000,     /* R147 */
+       0x0000,     /* R148 */
+       0x0000,     /* R149 */
+       0x0000,     /* R150 */
+       0x0000,     /* R151 */
+       0x7000,     /* R152 - AUX1 Readback */
+       0x7000,     /* R153 - AUX2 Readback */
+       0x7000,     /* R154 - AUX3 Readback */
+       0x7000,     /* R155 - AUX4 Readback */
+       0x0000,     /* R156 - USB Voltage Readback */
+       0x0000,     /* R157 - LINE Voltage Readback */
+       0x0000,     /* R158 - BATT Voltage Readback */
+       0x0000,     /* R159 - Chip Temp Readback */
+       0x0000,     /* R160 */
+       0x0000,     /* R161 */
+       0x0000,     /* R162 */
+       0x0000,     /* R163 - Generic Comparator Control */
+       0x0000,     /* R164 - Generic comparator 1 */
+       0x0000,     /* R165 - Generic comparator 2 */
+       0x0000,     /* R166 - Generic comparator 3 */
+       0x0000,     /* R167 - Generic comparator 4 */
+       0xA00F,     /* R168 - Battery Charger Control 1 */
+       0x0B06,     /* R169 - Battery Charger Control 2 */
+       0x0000,     /* R170 - Battery Charger Control 3 */
+       0x0000,     /* R171 */
+       0x0000,     /* R172 - Current Sink Driver A */
+       0x0000,     /* R173 - CSA Flash control */
+       0x0000,     /* R174 - Current Sink Driver B */
+       0x0000,     /* R175 - CSB Flash control */
+       0x0000,     /* R176 - DCDC/LDO requested */
+       0x002D,     /* R177 - DCDC Active options */
+       0x0000,     /* R178 - DCDC Sleep options */
+       0x0025,     /* R179 - Power-check comparator */
+       0x000E,     /* R180 - DCDC1 Control */
+       0x0400,     /* R181 - DCDC1 Timeouts */
+       0x1006,     /* R182 - DCDC1 Low Power */
+       0x0018,     /* R183 - DCDC2 Control */
+       0x0000,     /* R184 - DCDC2 Timeouts */
+       0x0000,     /* R185 */
+       0x000E,     /* R186 - DCDC3 Control */
+       0x0400,     /* R187 - DCDC3 Timeouts */
+       0x0006,     /* R188 - DCDC3 Low Power */
+       0x0026,     /* R189 - DCDC4 Control */
+       0x0400,     /* R190 - DCDC4 Timeouts */
+       0x0006,     /* R191 - DCDC4 Low Power */
+       0x0008,     /* R192 - DCDC5 Control */
+       0x0000,     /* R193 - DCDC5 Timeouts */
+       0x0000,     /* R194 */
+       0x0026,     /* R195 - DCDC6 Control */
+       0x0400,     /* R196 - DCDC6 Timeouts */
+       0x0006,     /* R197 - DCDC6 Low Power */
+       0x0000,     /* R198 */
+       0x0003,     /* R199 - Limit Switch Control */
+       0x001C,     /* R200 - LDO1 Control */
+       0x0000,     /* R201 - LDO1 Timeouts */
+       0x001C,     /* R202 - LDO1 Low Power */
+       0x001C,     /* R203 - LDO2 Control */
+       0x0400,     /* R204 - LDO2 Timeouts */
+       0x001C,     /* R205 - LDO2 Low Power */
+       0x001C,     /* R206 - LDO3 Control */
+       0x0400,     /* R207 - LDO3 Timeouts */
+       0x001C,     /* R208 - LDO3 Low Power */
+       0x001F,     /* R209 - LDO4 Control */
+       0x0400,     /* R210 - LDO4 Timeouts */
+       0x001C,     /* R211 - LDO4 Low Power */
+       0x0000,     /* R212 */
+       0x0000,     /* R213 */
+       0x0000,     /* R214 */
+       0x0000,     /* R215 - VCC_FAULT Masks */
+       0x001F,     /* R216 - Main Bandgap Control */
+       0x0000,     /* R217 - OSC Control */
+       0x9000,     /* R218 - RTC Tick Control */
+       0x0000,     /* R219 */
+       0x4000,     /* R220 - RAM BIST 1 */
+       0x0000,     /* R221 */
+       0x0000,     /* R222 */
+       0x0000,     /* R223 */
+       0x0000,     /* R224 */
+       0x0000,     /* R225 - DCDC/LDO status */
+       0x0000,     /* R226 */
+       0x0000,     /* R227 */
+       0x0000,     /* R228 */
+       0x0000,     /* R229 */
+       0xE000,     /* R230 - GPIO Pin Status */
+       0x0000,     /* R231 */
+       0x0000,     /* R232 */
+       0x0000,     /* R233 */
+       0x0000,     /* R234 */
+       0x0000,     /* R235 */
+       0x0000,     /* R236 */
+       0x0000,     /* R237 */
+       0x0000,     /* R238 */
+       0x0000,     /* R239 */
+       0x0000,     /* R240 */
+       0x0000,     /* R241 */
+       0x0000,     /* R242 */
+       0x0000,     /* R243 */
+       0x0000,     /* R244 */
+       0x0000,     /* R245 */
+       0x0000,     /* R246 */
+       0x0000,     /* R247 */
+       0x0000,     /* R248 */
+       0x0000,     /* R249 */
+       0x0000,     /* R250 */
+       0x0000,     /* R251 */
+       0x0000,     /* R252 */
+       0x0000,     /* R253 */
+       0x0000,     /* R254 */
+       0x0000,     /* R255 */
+};
+#endif
+
+/* The register defaults for the config mode used must be compiled in but
+ * due to the impact on kernel size it is possible to disable
+ */
+#ifndef WM8350_HAVE_CONFIG_MODE
+#warning No WM8350 config modes supported - select at least one of the
+#warning MFD_WM8350_CONFIG_MODE_n options from the board driver.
+#endif
+
+/*
+ * Access masks.
+ */
+
+const struct wm8350_reg_access wm8350_reg_io_map[] = {
+       /*  read    write volatile */
+       { 0xFFFF, 0xFFFF, 0xFFFF }, /* R0   - Reset/ID */
+       { 0x7CFF, 0x0C00, 0x7FFF }, /* R1   - ID */
+       { 0x0000, 0x0000, 0x0000 }, /* R2 */
+       { 0xBE3B, 0xBE3B, 0x8000 }, /* R3   - System Control 1 */
+       { 0xFCF7, 0xFCF7, 0xF800 }, /* R4   - System Control 2 */
+       { 0x80FF, 0x80FF, 0x8000 }, /* R5   - System Hibernate */
+       { 0xFB0E, 0xFB0E, 0x0000 }, /* R6   - Interface Control */
+       { 0x0000, 0x0000, 0x0000 }, /* R7 */
+       { 0xE537, 0xE537, 0xFFFF }, /* R8   - Power mgmt (1) */
+       { 0x0FF3, 0x0FF3, 0xFFFF }, /* R9   - Power mgmt (2) */
+       { 0x008F, 0x008F, 0xFFFF }, /* R10  - Power mgmt (3) */
+       { 0x6D3C, 0x6D3C, 0xFFFF }, /* R11  - Power mgmt (4) */
+       { 0x1F8F, 0x1F8F, 0xFFFF }, /* R12  - Power mgmt (5) */
+       { 0x8F3F, 0x8F3F, 0xFFFF }, /* R13  - Power mgmt (6) */
+       { 0x0003, 0x0003, 0xFFFF }, /* R14  - Power mgmt (7) */
+       { 0x0000, 0x0000, 0x0000 }, /* R15 */
+       { 0x7F7F, 0x7F7F, 0xFFFF }, /* R16  - RTC Seconds/Minutes */
+       { 0x073F, 0x073F, 0xFFFF }, /* R17  - RTC Hours/Day */
+       { 0x1F3F, 0x1F3F, 0xFFFF }, /* R18  - RTC Date/Month */
+       { 0x3FFF, 0x00FF, 0xFFFF }, /* R19  - RTC Year */
+       { 0x7F7F, 0x7F7F, 0x0000 }, /* R20  - Alarm Seconds/Minutes */
+       { 0x0F3F, 0x0F3F, 0x0000 }, /* R21  - Alarm Hours/Day */
+       { 0x1F3F, 0x1F3F, 0x0000 }, /* R22  - Alarm Date/Month */
+       { 0xEF7F, 0xEA7F, 0xFFFF }, /* R23  - RTC Time Control */
+       { 0x3BFF, 0x0000, 0xFFFF }, /* R24  - System Interrupts */
+       { 0xFEE7, 0x0000, 0xFFFF }, /* R25  - Interrupt Status 1 */
+       { 0x35FF, 0x0000, 0xFFFF }, /* R26  - Interrupt Status 2 */
+       { 0x0F3F, 0x0000, 0xFFFF }, /* R27  - Power Up Interrupt Status */
+       { 0x0F3F, 0x0000, 0xFFFF }, /* R28  - Under Voltage Interrupt status */
+       { 0x8000, 0x0000, 0xFFFF }, /* R29  - Over Current Interrupt status */
+       { 0x1FFF, 0x0000, 0xFFFF }, /* R30  - GPIO Interrupt Status */
+       { 0xEF7F, 0x0000, 0xFFFF }, /* R31  - Comparator Interrupt Status */
+       { 0x3FFF, 0x3FFF, 0x0000 }, /* R32  - System Interrupts Mask */
+       { 0xFEE7, 0xFEE7, 0x0000 }, /* R33  - Interrupt Status 1 Mask */
+       { 0xF5FF, 0xF5FF, 0x0000 }, /* R34  - Interrupt Status 2 Mask */
+       { 0x0F3F, 0x0F3F, 0x0000 }, /* R35  - Power Up Interrupt Status Mask */
+       { 0x0F3F, 0x0F3F, 0x0000 }, /* R36  - Under Voltage Int status Mask */
+       { 0x8000, 0x8000, 0x0000 }, /* R37  - Over Current Int status Mask */
+       { 0x1FFF, 0x1FFF, 0x0000 }, /* R38  - GPIO Interrupt Status Mask */
+       { 0xEF7F, 0xEF7F, 0x0000 }, /* R39  - Comparator IntStatus Mask */
+       { 0xC9F7, 0xC9F7, 0xFFFF }, /* R40  - Clock Control 1 */
+       { 0x8001, 0x8001, 0x0000 }, /* R41  - Clock Control 2 */
+       { 0xFFF7, 0xFFF7, 0xFFFF }, /* R42  - FLL Control 1 */
+       { 0xFBFF, 0xFBFF, 0x0000 }, /* R43  - FLL Control 2 */
+       { 0xFFFF, 0xFFFF, 0x0000 }, /* R44  - FLL Control 3 */
+       { 0x0033, 0x0033, 0x0000 }, /* R45  - FLL Control 4 */
+       { 0x0000, 0x0000, 0x0000 }, /* R46 */
+       { 0x0000, 0x0000, 0x0000 }, /* R47 */
+       { 0x3033, 0x3033, 0x0000 }, /* R48  - DAC Control */
+       { 0x0000, 0x0000, 0x0000 }, /* R49 */
+       { 0x81FF, 0x81FF, 0xFFFF }, /* R50  - DAC Digital Volume L */
+       { 0x81FF, 0x81FF, 0xFFFF }, /* R51  - DAC Digital Volume R */
+       { 0x0000, 0x0000, 0x0000 }, /* R52 */
+       { 0x0FFF, 0x0FFF, 0xFFFF }, /* R53  - DAC LR Rate */
+       { 0x0017, 0x0017, 0x0000 }, /* R54  - DAC Clock Control */
+       { 0x0000, 0x0000, 0x0000 }, /* R55 */
+       { 0x0000, 0x0000, 0x0000 }, /* R56 */
+       { 0x0000, 0x0000, 0x0000 }, /* R57 */
+       { 0x4000, 0x4000, 0x0000 }, /* R58  - DAC Mute */
+       { 0x7000, 0x7000, 0x0000 }, /* R59  - DAC Mute Volume */
+       { 0x3C00, 0x3C00, 0x0000 }, /* R60  - DAC Side */
+       { 0x0000, 0x0000, 0x0000 }, /* R61 */
+       { 0x0000, 0x0000, 0x0000 }, /* R62 */
+       { 0x0000, 0x0000, 0x0000 }, /* R63 */
+       { 0x8303, 0x8303, 0xFFFF }, /* R64  - ADC Control */
+       { 0x0000, 0x0000, 0x0000 }, /* R65 */
+       { 0x81FF, 0x81FF, 0xFFFF }, /* R66  - ADC Digital Volume L */
+       { 0x81FF, 0x81FF, 0xFFFF }, /* R67  - ADC Digital Volume R */
+       { 0x0FFF, 0x0FFF, 0x0000 }, /* R68  - ADC Divider */
+       { 0x0000, 0x0000, 0x0000 }, /* R69 */
+       { 0x0FFF, 0x0FFF, 0xFFFF }, /* R70  - ADC LR Rate */
+       { 0x0000, 0x0000, 0x0000 }, /* R71 */
+       { 0x0707, 0x0707, 0xFFFF }, /* R72  - Input Control */
+       { 0xC0C0, 0xC0C0, 0xFFFF }, /* R73  - IN3 Input Control */
+       { 0xC09F, 0xC09F, 0xFFFF }, /* R74  - Mic Bias Control */
+       { 0x0000, 0x0000, 0x0000 }, /* R75 */
+       { 0x0F15, 0x0F15, 0xFFFF }, /* R76  - Output Control */
+       { 0xC000, 0xC000, 0xFFFF }, /* R77  - Jack Detect */
+       { 0x03FF, 0x03FF, 0x0000 }, /* R78  - Anti Pop Control */
+       { 0x0000, 0x0000, 0x0000 }, /* R79 */
+       { 0xE1FC, 0xE1FC, 0x8000 }, /* R80  - Left Input Volume */
+       { 0xE1FC, 0xE1FC, 0x8000 }, /* R81  - Right Input Volume */
+       { 0x0000, 0x0000, 0x0000 }, /* R82 */
+       { 0x0000, 0x0000, 0x0000 }, /* R83 */
+       { 0x0000, 0x0000, 0x0000 }, /* R84 */
+       { 0x0000, 0x0000, 0x0000 }, /* R85 */
+       { 0x0000, 0x0000, 0x0000 }, /* R86 */
+       { 0x0000, 0x0000, 0x0000 }, /* R87 */
+       { 0x9807, 0x9807, 0xFFFF }, /* R88  - Left Mixer Control */
+       { 0x980B, 0x980B, 0xFFFF }, /* R89  - Right Mixer Control */
+       { 0x0000, 0x0000, 0x0000 }, /* R90 */
+       { 0x0000, 0x0000, 0x0000 }, /* R91 */
+       { 0x8909, 0x8909, 0xFFFF }, /* R92  - OUT3 Mixer Control */
+       { 0x9E07, 0x9E07, 0xFFFF }, /* R93  - OUT4 Mixer Control */
+       { 0x0000, 0x0000, 0x0000 }, /* R94 */
+       { 0x0000, 0x0000, 0x0000 }, /* R95 */
+       { 0x0EEE, 0x0EEE, 0x0000 }, /* R96  - Output Left Mixer Volume */
+       { 0xE0EE, 0xE0EE, 0x0000 }, /* R97  - Output Right Mixer Volume */
+       { 0x0E0F, 0x0E0F, 0x0000 }, /* R98  - Input Mixer Volume L */
+       { 0xE0E1, 0xE0E1, 0x0000 }, /* R99  - Input Mixer Volume R */
+       { 0x800E, 0x800E, 0x0000 }, /* R100 - Input Mixer Volume */
+       { 0x0000, 0x0000, 0x0000 }, /* R101 */
+       { 0x0000, 0x0000, 0x0000 }, /* R102 */
+       { 0x0000, 0x0000, 0x0000 }, /* R103 */
+       { 0xE1FC, 0xE1FC, 0xFFFF }, /* R104 - LOUT1 Volume */
+       { 0xE1FC, 0xE1FC, 0xFFFF }, /* R105 - ROUT1 Volume */
+       { 0xE1FC, 0xE1FC, 0xFFFF }, /* R106 - LOUT2 Volume */
+       { 0xE7FC, 0xE7FC, 0xFFFF }, /* R107 - ROUT2 Volume */
+       { 0x0000, 0x0000, 0x0000 }, /* R108 */
+       { 0x0000, 0x0000, 0x0000 }, /* R109 */
+       { 0x0000, 0x0000, 0x0000 }, /* R110 */
+       { 0x80E0, 0x80E0, 0xFFFF }, /* R111 - BEEP Volume */
+       { 0xBF00, 0xBF00, 0x0000 }, /* R112 - AI Formating */
+       { 0x00F1, 0x00F1, 0x0000 }, /* R113 - ADC DAC COMP */
+       { 0x00F8, 0x00F8, 0x0000 }, /* R114 - AI ADC Control */
+       { 0x40FB, 0x40FB, 0x0000 }, /* R115 - AI DAC Control */
+       { 0x7C30, 0x7C30, 0x0000 }, /* R116 - AIF Test */
+       { 0x0000, 0x0000, 0x0000 }, /* R117 */
+       { 0x0000, 0x0000, 0x0000 }, /* R118 */
+       { 0x0000, 0x0000, 0x0000 }, /* R119 */
+       { 0x0000, 0x0000, 0x0000 }, /* R120 */
+       { 0x0000, 0x0000, 0x0000 }, /* R121 */
+       { 0x0000, 0x0000, 0x0000 }, /* R122 */
+       { 0x0000, 0x0000, 0x0000 }, /* R123 */
+       { 0x0000, 0x0000, 0x0000 }, /* R124 */
+       { 0x0000, 0x0000, 0x0000 }, /* R125 */
+       { 0x0000, 0x0000, 0x0000 }, /* R126 */
+       { 0x0000, 0x0000, 0x0000 }, /* R127 */
+       { 0x1FFF, 0x1FFF, 0x0000 }, /* R128 - GPIO Debounce */
+       { 0x1FFF, 0x1FFF, 0x0000 }, /* R129 - GPIO Pin pull up Control */
+       { 0x1FFF, 0x1FFF, 0x0000 }, /* R130 - GPIO Pull down Control */
+       { 0x1FFF, 0x1FFF, 0x0000 }, /* R131 - GPIO Interrupt Mode */
+       { 0x0000, 0x0000, 0x0000 }, /* R132 */
+       { 0x00C0, 0x00C0, 0x0000 }, /* R133 - GPIO Control */
+       { 0x1FFF, 0x1FFF, 0x0000 }, /* R134 - GPIO Configuration (i/o) */
+       { 0x1FFF, 0x1FFF, 0x0000 }, /* R135 - GPIO Pin Polarity / Type */
+       { 0x0000, 0x0000, 0x0000 }, /* R136 */
+       { 0x0000, 0x0000, 0x0000 }, /* R137 */
+       { 0x0000, 0x0000, 0x0000 }, /* R138 */
+       { 0x0000, 0x0000, 0x0000 }, /* R139 */
+       { 0xFFFF, 0xFFFF, 0x0000 }, /* R140 - GPIO Function Select 1 */
+       { 0xFFFF, 0xFFFF, 0x0000 }, /* R141 - GPIO Function Select 2 */
+       { 0xFFFF, 0xFFFF, 0x0000 }, /* R142 - GPIO Function Select 3 */
+       { 0x000F, 0x000F, 0x0000 }, /* R143 - GPIO Function Select 4 */
+       { 0xF0FF, 0xF0FF, 0xA000 }, /* R144 - Digitiser Control (1) */
+       { 0x3707, 0x3707, 0x0000 }, /* R145 - Digitiser Control (2) */
+       { 0x0000, 0x0000, 0x0000 }, /* R146 */
+       { 0x0000, 0x0000, 0x0000 }, /* R147 */
+       { 0x0000, 0x0000, 0x0000 }, /* R148 */
+       { 0x0000, 0x0000, 0x0000 }, /* R149 */
+       { 0x0000, 0x0000, 0x0000 }, /* R150 */
+       { 0x0000, 0x0000, 0x0000 }, /* R151 */
+       { 0x7FFF, 0x7000, 0xFFFF }, /* R152 - AUX1 Readback */
+       { 0x7FFF, 0x7000, 0xFFFF }, /* R153 - AUX2 Readback */
+       { 0x7FFF, 0x7000, 0xFFFF }, /* R154 - AUX3 Readback */
+       { 0x7FFF, 0x7000, 0xFFFF }, /* R155 - AUX4 Readback */
+       { 0x0FFF, 0x0000, 0xFFFF }, /* R156 - USB Voltage Readback */
+       { 0x0FFF, 0x0000, 0xFFFF }, /* R157 - LINE Voltage Readback */
+       { 0x0FFF, 0x0000, 0xFFFF }, /* R158 - BATT Voltage Readback */
+       { 0x0FFF, 0x0000, 0xFFFF }, /* R159 - Chip Temp Readback */
+       { 0x0000, 0x0000, 0x0000 }, /* R160 */
+       { 0x0000, 0x0000, 0x0000 }, /* R161 */
+       { 0x0000, 0x0000, 0x0000 }, /* R162 */
+       { 0x000F, 0x000F, 0x0000 }, /* R163 - Generic Comparator Control */
+       { 0xFFFF, 0xFFFF, 0x0000 }, /* R164 - Generic comparator 1 */
+       { 0xFFFF, 0xFFFF, 0x0000 }, /* R165 - Generic comparator 2 */
+       { 0xFFFF, 0xFFFF, 0x0000 }, /* R166 - Generic comparator 3 */
+       { 0xFFFF, 0xFFFF, 0x0000 }, /* R167 - Generic comparator 4 */
+       { 0xBFFF, 0xBFFF, 0x8000 }, /* R168 - Battery Charger Control 1 */
+       { 0xFFFF, 0x4FFF, 0xB000 }, /* R169 - Battery Charger Control 2 */
+       { 0x007F, 0x007F, 0x0000 }, /* R170 - Battery Charger Control 3 */
+       { 0x0000, 0x0000, 0x0000 }, /* R171 */
+       { 0x903F, 0x903F, 0xFFFF }, /* R172 - Current Sink Driver A */
+       { 0xE333, 0xE333, 0xFFFF }, /* R173 - CSA Flash control */
+       { 0x903F, 0x903F, 0xFFFF }, /* R174 - Current Sink Driver B */
+       { 0xE333, 0xE333, 0xFFFF }, /* R175 - CSB Flash control */
+       { 0x8F3F, 0x8F3F, 0xFFFF }, /* R176 - DCDC/LDO requested */
+       { 0x332D, 0x332D, 0x0000 }, /* R177 - DCDC Active options */
+       { 0x002D, 0x002D, 0x0000 }, /* R178 - DCDC Sleep options */
+       { 0x5177, 0x5177, 0x8000 }, /* R179 - Power-check comparator */
+       { 0x047F, 0x047F, 0x0000 }, /* R180 - DCDC1 Control */
+       { 0xFFC0, 0xFFC0, 0x0000 }, /* R181 - DCDC1 Timeouts */
+       { 0x737F, 0x737F, 0x0000 }, /* R182 - DCDC1 Low Power */
+       { 0x535B, 0x535B, 0x0000 }, /* R183 - DCDC2 Control */
+       { 0xFFC0, 0xFFC0, 0x0000 }, /* R184 - DCDC2 Timeouts */
+       { 0x0000, 0x0000, 0x0000 }, /* R185 */
+       { 0x047F, 0x047F, 0x0000 }, /* R186 - DCDC3 Control */
+       { 0xFFC0, 0xFFC0, 0x0000 }, /* R187 - DCDC3 Timeouts */
+       { 0x737F, 0x737F, 0x0000 }, /* R188 - DCDC3 Low Power */
+       { 0x047F, 0x047F, 0x0000 }, /* R189 - DCDC4 Control */
+       { 0xFFC0, 0xFFC0, 0x0000 }, /* R190 - DCDC4 Timeouts */
+       { 0x737F, 0x737F, 0x0000 }, /* R191 - DCDC4 Low Power */
+       { 0x535B, 0x535B, 0x0000 }, /* R192 - DCDC5 Control */
+       { 0xFFC0, 0xFFC0, 0x0000 }, /* R193 - DCDC5 Timeouts */
+       { 0x0000, 0x0000, 0x0000 }, /* R194 */
+       { 0x047F, 0x047F, 0x0000 }, /* R195 - DCDC6 Control */
+       { 0xFFC0, 0xFFC0, 0x0000 }, /* R196 - DCDC6 Timeouts */
+       { 0x737F, 0x737F, 0x0000 }, /* R197 - DCDC6 Low Power */
+       { 0x0000, 0x0000, 0x0000 }, /* R198 */
+       { 0xFFD3, 0xFFD3, 0x0000 }, /* R199 - Limit Switch Control */
+       { 0x441F, 0x441F, 0x0000 }, /* R200 - LDO1 Control */
+       { 0xFFC0, 0xFFC0, 0x0000 }, /* R201 - LDO1 Timeouts */
+       { 0x331F, 0x331F, 0x0000 }, /* R202 - LDO1 Low Power */
+       { 0x441F, 0x441F, 0x0000 }, /* R203 - LDO2 Control */
+       { 0xFFC0, 0xFFC0, 0x0000 }, /* R204 - LDO2 Timeouts */
+       { 0x331F, 0x331F, 0x0000 }, /* R205 - LDO2 Low Power */
+       { 0x441F, 0x441F, 0x0000 }, /* R206 - LDO3 Control */
+       { 0xFFC0, 0xFFC0, 0x0000 }, /* R207 - LDO3 Timeouts */
+       { 0x331F, 0x331F, 0x0000 }, /* R208 - LDO3 Low Power */
+       { 0x441F, 0x441F, 0x0000 }, /* R209 - LDO4 Control */
+       { 0xFFC0, 0xFFC0, 0x0000 }, /* R210 - LDO4 Timeouts */
+       { 0x331F, 0x331F, 0x0000 }, /* R211 - LDO4 Low Power */
+       { 0x0000, 0x0000, 0x0000 }, /* R212 */
+       { 0x0000, 0x0000, 0x0000 }, /* R213 */
+       { 0x0000, 0x0000, 0x0000 }, /* R214 */
+       { 0x8F3F, 0x8F3F, 0x0000 }, /* R215 - VCC_FAULT Masks */
+       { 0xFF3F, 0xE03F, 0x0000 }, /* R216 - Main Bandgap Control */
+       { 0xEF2F, 0xE02F, 0x0000 }, /* R217 - OSC Control */
+       { 0xF3FF, 0xB3FF, 0xc000 }, /* R218 - RTC Tick Control */
+       { 0xFFFF, 0xFFFF, 0xFFFF }, /* R219 */
+       { 0x09FF, 0x01FF, 0x0000 }, /* R220 - RAM BIST 1 */
+       { 0x0000, 0x0000, 0x0000 }, /* R221 */
+       { 0xFFFF, 0xFFFF, 0xFFFF }, /* R222 */
+       { 0xFFFF, 0xFFFF, 0xFFFF }, /* R223 */
+       { 0x0000, 0x0000, 0x0000 }, /* R224 */
+       { 0x8F3F, 0x0000, 0xFFFF }, /* R225 - DCDC/LDO status */
+       { 0x0000, 0x0000, 0x0000 }, /* R226 */
+       { 0x0000, 0x0000, 0xFFFF }, /* R227 */
+       { 0x0000, 0x0000, 0x0000 }, /* R228 */
+       { 0x0000, 0x0000, 0x0000 }, /* R229 */
+       { 0xFFFF, 0x1FFF, 0xFFFF }, /* R230 - GPIO Pin Status */
+       { 0xFFFF, 0x1FFF, 0xFFFF }, /* R231 */
+       { 0xFFFF, 0x1FFF, 0xFFFF }, /* R232 */
+       { 0xFFFF, 0x1FFF, 0xFFFF }, /* R233 */
+       { 0x0000, 0x0000, 0x0000 }, /* R234 */
+       { 0x0000, 0x0000, 0x0000 }, /* R235 */
+       { 0x0000, 0x0000, 0x0000 }, /* R236 */
+       { 0x0000, 0x0000, 0x0000 }, /* R237 */
+       { 0x0000, 0x0000, 0x0000 }, /* R238 */
+       { 0x0000, 0x0000, 0x0000 }, /* R239 */
+       { 0x0000, 0x0000, 0x0000 }, /* R240 */
+       { 0x0000, 0x0000, 0x0000 }, /* R241 */
+       { 0x0000, 0x0000, 0x0000 }, /* R242 */
+       { 0x0000, 0x0000, 0x0000 }, /* R243 */
+       { 0x0000, 0x0000, 0x0000 }, /* R244 */
+       { 0x0000, 0x0000, 0x0000 }, /* R245 */
+       { 0x0000, 0x0000, 0x0000 }, /* R246 */
+       { 0x0000, 0x0000, 0x0000 }, /* R247 */
+       { 0xFFFF, 0x0010, 0xFFFF }, /* R248 */
+       { 0x0000, 0x0000, 0x0000 }, /* R249 */
+       { 0xFFFF, 0x0010, 0xFFFF }, /* R250 */
+       { 0xFFFF, 0x0010, 0xFFFF }, /* R251 */
+       { 0x0000, 0x0000, 0x0000 }, /* R252 */
+       { 0xFFFF, 0x0010, 0xFFFF }, /* R253 */
+       { 0x0000, 0x0000, 0x0000 }, /* R254 */
+       { 0x0000, 0x0000, 0x0000 }, /* R255 */
+};
diff --git a/drivers/mfd/wm8400-core.c b/drivers/mfd/wm8400-core.c
new file mode 100644 (file)
index 0000000..6a0cedb
--- /dev/null
@@ -0,0 +1,455 @@
+/*
+ * Core driver for WM8400.
+ *
+ * Copyright 2008 Wolfson Microelectronics PLC.
+ *
+ * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ */
+
+#include <linux/bug.h>
+#include <linux/i2c.h>
+#include <linux/kernel.h>
+#include <linux/mfd/wm8400-private.h>
+#include <linux/mfd/wm8400-audio.h>
+
+static struct {
+       u16  readable;    /* Mask of readable bits */
+       u16  writable;    /* Mask of writable bits */
+       u16  vol;         /* Mask of volatile bits */
+       int  is_codec;    /* Register controlled by codec reset */
+       u16  default_val; /* Value on reset */
+} reg_data[] = {
+       { 0xFFFF, 0xFFFF, 0x0000, 0, 0x6172 }, /* R0 */
+       { 0x7000, 0x0000, 0x8000, 0, 0x0000 }, /* R1 */
+       { 0xFF17, 0xFF17, 0x0000, 0, 0x0000 }, /* R2 */
+       { 0xEBF3, 0xEBF3, 0x0000, 1, 0x6000 }, /* R3 */
+       { 0x3CF3, 0x3CF3, 0x0000, 1, 0x0000 }, /* R4  */
+       { 0xF1F8, 0xF1F8, 0x0000, 1, 0x4050 }, /* R5  */
+       { 0xFC1F, 0xFC1F, 0x0000, 1, 0x4000 }, /* R6  */
+       { 0xDFDE, 0xDFDE, 0x0000, 1, 0x01C8 }, /* R7  */
+       { 0xFCFC, 0xFCFC, 0x0000, 1, 0x0000 }, /* R8  */
+       { 0xEFFF, 0xEFFF, 0x0000, 1, 0x0040 }, /* R9  */
+       { 0xEFFF, 0xEFFF, 0x0000, 1, 0x0040 }, /* R10 */
+       { 0x27F7, 0x27F7, 0x0000, 1, 0x0004 }, /* R11 */
+       { 0x01FF, 0x01FF, 0x0000, 1, 0x00C0 }, /* R12 */
+       { 0x01FF, 0x01FF, 0x0000, 1, 0x00C0 }, /* R13 */
+       { 0x1FEF, 0x1FEF, 0x0000, 1, 0x0000 }, /* R14 */
+       { 0x0163, 0x0163, 0x0000, 1, 0x0100 }, /* R15 */
+       { 0x01FF, 0x01FF, 0x0000, 1, 0x00C0 }, /* R16 */
+       { 0x01FF, 0x01FF, 0x0000, 1, 0x00C0 }, /* R17 */
+       { 0x1FFF, 0x0FFF, 0x0000, 1, 0x0000 }, /* R18 */
+       { 0xFFFF, 0xFFFF, 0x0000, 1, 0x1000 }, /* R19 */
+       { 0xFFFF, 0xFFFF, 0x0000, 1, 0x1010 }, /* R20 */
+       { 0xFFFF, 0xFFFF, 0x0000, 1, 0x1010 }, /* R21 */
+       { 0x0FDD, 0x0FDD, 0x0000, 1, 0x8000 }, /* R22 */
+       { 0x1FFF, 0x1FFF, 0x0000, 1, 0x0800 }, /* R23 */
+       { 0x0000, 0x01DF, 0x0000, 1, 0x008B }, /* R24 */
+       { 0x0000, 0x01DF, 0x0000, 1, 0x008B }, /* R25 */
+       { 0x0000, 0x01DF, 0x0000, 1, 0x008B }, /* R26 */
+       { 0x0000, 0x01DF, 0x0000, 1, 0x008B }, /* R27 */
+       { 0x0000, 0x01FF, 0x0000, 1, 0x0000 }, /* R28 */
+       { 0x0000, 0x01FF, 0x0000, 1, 0x0000 }, /* R29 */
+       { 0x0000, 0x0077, 0x0000, 1, 0x0066 }, /* R30 */
+       { 0x0000, 0x0033, 0x0000, 1, 0x0022 }, /* R31 */
+       { 0x0000, 0x01FF, 0x0000, 1, 0x0079 }, /* R32 */
+       { 0x0000, 0x01FF, 0x0000, 1, 0x0079 }, /* R33 */
+       { 0x0000, 0x0003, 0x0000, 1, 0x0003 }, /* R34 */
+       { 0x0000, 0x01FF, 0x0000, 1, 0x0003 }, /* R35 */
+       { 0x0000, 0x0000, 0x0000, 0, 0x0000 }, /* R36 */
+       { 0x0000, 0x003F, 0x0000, 1, 0x0100 }, /* R37 */
+       { 0x0000, 0x0000, 0x0000, 0, 0x0000 }, /* R38 */
+       { 0x0000, 0x000F, 0x0000, 0, 0x0000 }, /* R39 */
+       { 0x0000, 0x00FF, 0x0000, 1, 0x0000 }, /* R40 */
+       { 0x0000, 0x01B7, 0x0000, 1, 0x0000 }, /* R41 */
+       { 0x0000, 0x01B7, 0x0000, 1, 0x0000 }, /* R42 */
+       { 0x0000, 0x01FF, 0x0000, 1, 0x0000 }, /* R43 */
+       { 0x0000, 0x01FF, 0x0000, 1, 0x0000 }, /* R44 */
+       { 0x0000, 0x00FD, 0x0000, 1, 0x0000 }, /* R45 */
+       { 0x0000, 0x00FD, 0x0000, 1, 0x0000 }, /* R46 */
+       { 0x0000, 0x01FF, 0x0000, 1, 0x0000 }, /* R47 */
+       { 0x0000, 0x01FF, 0x0000, 1, 0x0000 }, /* R48 */
+       { 0x0000, 0x01FF, 0x0000, 1, 0x0000 }, /* R49 */
+       { 0x0000, 0x01FF, 0x0000, 1, 0x0000 }, /* R50 */
+       { 0x0000, 0x01B3, 0x0000, 1, 0x0180 }, /* R51 */
+       { 0x0000, 0x0077, 0x0000, 1, 0x0000 }, /* R52 */
+       { 0x0000, 0x0077, 0x0000, 1, 0x0000 }, /* R53 */
+       { 0x0000, 0x00FF, 0x0000, 1, 0x0000 }, /* R54 */
+       { 0x0000, 0x0001, 0x0000, 1, 0x0000 }, /* R55 */
+       { 0x0000, 0x003F, 0x0000, 1, 0x0000 }, /* R56 */
+       { 0x0000, 0x004F, 0x0000, 1, 0x0000 }, /* R57 */
+       { 0x0000, 0x00FD, 0x0000, 1, 0x0000 }, /* R58 */
+       { 0x0000, 0x0000, 0x0000, 0, 0x0000 }, /* R59 */
+       { 0x1FFF, 0x1FFF, 0x0000, 1, 0x0000 }, /* R60 */
+       { 0xFFFF, 0xFFFF, 0x0000, 1, 0x0000 }, /* R61 */
+       { 0x03FF, 0x03FF, 0x0000, 1, 0x0000 }, /* R62 */
+       { 0x007F, 0x007F, 0x0000, 1, 0x0000 }, /* R63 */
+       { 0x0000, 0x0000, 0x0000, 0, 0x0000 }, /* R64 */
+       { 0xDFFF, 0xDFFF, 0x0000, 0, 0x0000 }, /* R65 */
+       { 0xDFFF, 0xDFFF, 0x0000, 0, 0x0000 }, /* R66 */
+       { 0xDFFF, 0xDFFF, 0x0000, 0, 0x0000 }, /* R67 */
+       { 0xDFFF, 0xDFFF, 0x0000, 0, 0x0000 }, /* R68 */
+       { 0x0000, 0x0000, 0x0000, 0, 0x0000 }, /* R69 */
+       { 0xFFFF, 0xFFFF, 0x0000, 0, 0x4400 }, /* R70 */
+       { 0x23FF, 0x23FF, 0x0000, 0, 0x0000 }, /* R71 */
+       { 0xFFFF, 0xFFFF, 0x0000, 0, 0x4400 }, /* R72 */
+       { 0x23FF, 0x23FF, 0x0000, 0, 0x0000 }, /* R73 */
+       { 0x0000, 0x0000, 0x0000, 0, 0x0000 }, /* R74 */
+       { 0x000E, 0x000E, 0x0000, 0, 0x0008 }, /* R75 */
+       { 0xE00F, 0xE00F, 0x0000, 0, 0x0000 }, /* R76 */
+       { 0x0000, 0x0000, 0x0000, 0, 0x0000 }, /* R77 */
+       { 0x03C0, 0x03C0, 0x0000, 0, 0x02C0 }, /* R78 */
+       { 0xFFFF, 0x0000, 0xffff, 0, 0x0000 }, /* R79 */
+       { 0xFFFF, 0xFFFF, 0x0000, 0, 0x0000 }, /* R80 */
+       { 0xFFFF, 0x0000, 0xffff, 0, 0x0000 }, /* R81 */
+       { 0x2BFF, 0x0000, 0xffff, 0, 0x0000 }, /* R82 */
+       { 0x0000, 0x0000, 0x0000, 0, 0x0000 }, /* R83 */
+       { 0x80FF, 0x80FF, 0x0000, 0, 0x00ff }, /* R84 */
+};
+
+static int wm8400_read(struct wm8400 *wm8400, u8 reg, int num_regs, u16 *dest)
+{
+       int i, ret = 0;
+
+       BUG_ON(reg + num_regs - 1 > ARRAY_SIZE(wm8400->reg_cache));
+
+       /* If there are any volatile reads then read back the entire block */
+       for (i = reg; i < reg + num_regs; i++)
+               if (reg_data[i].vol) {
+                       ret = wm8400->read_dev(wm8400->io_data, reg,
+                                              num_regs, dest);
+                       if (ret != 0)
+                               return ret;
+                       for (i = 0; i < num_regs; i++)
+                               dest[i] = be16_to_cpu(dest[i]);
+
+                       return 0;
+               }
+
+       /* Otherwise use the cache */
+       memcpy(dest, &wm8400->reg_cache[reg], num_regs * sizeof(u16));
+
+       return 0;
+}
+
+static int wm8400_write(struct wm8400 *wm8400, u8 reg, int num_regs,
+                       u16 *src)
+{
+       int ret, i;
+
+       BUG_ON(reg + num_regs - 1 > ARRAY_SIZE(wm8400->reg_cache));
+
+       for (i = 0; i < num_regs; i++) {
+               BUG_ON(!reg_data[reg + i].writable);
+               wm8400->reg_cache[reg + i] = src[i];
+               src[i] = cpu_to_be16(src[i]);
+       }
+
+       /* Do the actual I/O */
+       ret = wm8400->write_dev(wm8400->io_data, reg, num_regs, src);
+       if (ret != 0)
+               return -EIO;
+
+       return 0;
+}
+
+/**
+ * wm8400_reg_read - Single register read
+ *
+ * @wm8400: Pointer to wm8400 control structure
+ * @reg:    Register to read
+ *
+ * @return  Read value
+ */
+u16 wm8400_reg_read(struct wm8400 *wm8400, u8 reg)
+{
+       u16 val;
+
+       mutex_lock(&wm8400->io_lock);
+
+       wm8400_read(wm8400, reg, 1, &val);
+
+       mutex_unlock(&wm8400->io_lock);
+
+       return val;
+}
+EXPORT_SYMBOL_GPL(wm8400_reg_read);
+
+int wm8400_block_read(struct wm8400 *wm8400, u8 reg, int count, u16 *data)
+{
+       int ret;
+
+       mutex_lock(&wm8400->io_lock);
+
+       ret = wm8400_read(wm8400, reg, count, data);
+
+       mutex_unlock(&wm8400->io_lock);
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(wm8400_block_read);
+
+/**
+ * wm8400_set_bits - Bitmask write
+ *
+ * @wm8400: Pointer to wm8400 control structure
+ * @reg:    Register to access
+ * @mask:   Mask of bits to change
+ * @val:    Value to set for masked bits
+ */
+int wm8400_set_bits(struct wm8400 *wm8400, u8 reg, u16 mask, u16 val)
+{
+       u16 tmp;
+       int ret;
+
+       mutex_lock(&wm8400->io_lock);
+
+       ret = wm8400_read(wm8400, reg, 1, &tmp);
+       tmp = (tmp & ~mask) | val;
+       if (ret == 0)
+               ret = wm8400_write(wm8400, reg, 1, &tmp);
+
+       mutex_unlock(&wm8400->io_lock);
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(wm8400_set_bits);
+
+/**
+ * wm8400_reset_codec_reg_cache - Reset cached codec registers to
+ * their default values.
+ */
+void wm8400_reset_codec_reg_cache(struct wm8400 *wm8400)
+{
+       int i;
+
+       mutex_lock(&wm8400->io_lock);
+
+       /* Reset all codec registers to their initial value */
+       for (i = 0; i < ARRAY_SIZE(wm8400->reg_cache); i++)
+               if (reg_data[i].is_codec)
+                       wm8400->reg_cache[i] = reg_data[i].default_val;
+
+       mutex_unlock(&wm8400->io_lock);
+}
+EXPORT_SYMBOL_GPL(wm8400_reset_codec_reg_cache);
+
+/*
+ * wm8400_init - Generic initialisation
+ *
+ * The WM8400 can be configured as either an I2C or SPI device.  Probe
+ * functions for each bus set up the accessors then call into this to
+ * set up the device itself.
+ */
+static int wm8400_init(struct wm8400 *wm8400,
+                      struct wm8400_platform_data *pdata)
+{
+       u16 reg;
+       int ret, i;
+
+       mutex_init(&wm8400->io_lock);
+
+       wm8400->dev->driver_data = wm8400;
+
+       /* Check that this is actually a WM8400 */
+       ret = wm8400->read_dev(wm8400->io_data, WM8400_RESET_ID, 1, &reg);
+       if (ret != 0) {
+               dev_err(wm8400->dev, "Chip ID register read failed\n");
+               return -EIO;
+       }
+       if (be16_to_cpu(reg) != reg_data[WM8400_RESET_ID].default_val) {
+               dev_err(wm8400->dev, "Device is not a WM8400, ID is %x\n",
+                       be16_to_cpu(reg));
+               return -ENODEV;
+       }
+
+       /* We don't know what state the hardware is in and since this
+        * is a PMIC we can't reset it safely so initialise the register
+        * cache from the hardware.
+        */
+       ret = wm8400->read_dev(wm8400->io_data, 0,
+                              ARRAY_SIZE(wm8400->reg_cache),
+                              wm8400->reg_cache);
+       if (ret != 0) {
+               dev_err(wm8400->dev, "Register cache read failed\n");
+               return -EIO;
+       }
+       for (i = 0; i < ARRAY_SIZE(wm8400->reg_cache); i++)
+               wm8400->reg_cache[i] = be16_to_cpu(wm8400->reg_cache[i]);
+
+       /* If the codec is in reset use hard coded values */
+       if (!(wm8400->reg_cache[WM8400_POWER_MANAGEMENT_1] & WM8400_CODEC_ENA))
+               for (i = 0; i < ARRAY_SIZE(wm8400->reg_cache); i++)
+                       if (reg_data[i].is_codec)
+                               wm8400->reg_cache[i] = reg_data[i].default_val;
+
+       ret = wm8400_read(wm8400, WM8400_ID, 1, &reg);
+       if (ret != 0) {
+               dev_err(wm8400->dev, "ID register read failed: %d\n", ret);
+               return ret;
+       }
+       reg = (reg & WM8400_CHIP_REV_MASK) >> WM8400_CHIP_REV_SHIFT;
+       dev_info(wm8400->dev, "WM8400 revision %x\n", reg);
+
+       if (pdata && pdata->platform_init) {
+               ret = pdata->platform_init(wm8400->dev);
+               if (ret != 0)
+                       dev_err(wm8400->dev, "Platform init failed: %d\n",
+                               ret);
+       } else
+               dev_warn(wm8400->dev, "No platform initialisation supplied\n");
+
+       return ret;
+}
+
+static void wm8400_release(struct wm8400 *wm8400)
+{
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(wm8400->regulators); i++)
+               if (wm8400->regulators[i].name)
+                       platform_device_unregister(&wm8400->regulators[i]);
+}
+
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+static int wm8400_i2c_read(void *io_data, char reg, int count, u16 *dest)
+{
+       struct i2c_client *i2c = io_data;
+       struct i2c_msg xfer[2];
+       int ret;
+
+       /* Write register */
+       xfer[0].addr = i2c->addr;
+       xfer[0].flags = 0;
+       xfer[0].len = 1;
+       xfer[0].buf = &reg;
+
+       /* Read data */
+       xfer[1].addr = i2c->addr;
+       xfer[1].flags = I2C_M_RD;
+       xfer[1].len = count * sizeof(u16);
+       xfer[1].buf = (u8 *)dest;
+
+       ret = i2c_transfer(i2c->adapter, xfer, 2);
+       if (ret == 2)
+               ret = 0;
+       else if (ret >= 0)
+               ret = -EIO;
+
+       return ret;
+}
+
+static int wm8400_i2c_write(void *io_data, char reg, int count, const u16 *src)
+{
+       struct i2c_client *i2c = io_data;
+       u8 *msg;
+       int ret;
+
+       /* We add 1 byte for device register - ideally I2C would gather. */
+       msg = kmalloc((count * sizeof(u16)) + 1, GFP_KERNEL);
+       if (msg == NULL)
+               return -ENOMEM;
+
+       msg[0] = reg;
+       memcpy(&msg[1], src, count * sizeof(u16));
+
+       ret = i2c_master_send(i2c, msg, (count * sizeof(u16)) + 1);
+
+       if (ret == (count * 2) + 1)
+               ret = 0;
+       else if (ret >= 0)
+               ret = -EIO;
+
+       kfree(msg);
+
+       return ret;
+}
+
+static int wm8400_i2c_probe(struct i2c_client *i2c,
+                           const struct i2c_device_id *id)
+{
+       struct wm8400 *wm8400;
+       int ret;
+
+       wm8400 = kzalloc(sizeof(struct wm8400), GFP_KERNEL);
+       if (wm8400 == NULL) {
+               ret = -ENOMEM;
+               goto err;
+       }
+
+       wm8400->io_data = i2c;
+       wm8400->read_dev = wm8400_i2c_read;
+       wm8400->write_dev = wm8400_i2c_write;
+       wm8400->dev = &i2c->dev;
+       i2c_set_clientdata(i2c, wm8400);
+
+       ret = wm8400_init(wm8400, i2c->dev.platform_data);
+       if (ret != 0)
+               goto struct_err;
+
+       return 0;
+
+struct_err:
+       i2c_set_clientdata(i2c, NULL);
+       kfree(wm8400);
+err:
+       return ret;
+}
+
+static int wm8400_i2c_remove(struct i2c_client *i2c)
+{
+       struct wm8400 *wm8400 = i2c_get_clientdata(i2c);
+
+       wm8400_release(wm8400);
+       i2c_set_clientdata(i2c, NULL);
+       kfree(wm8400);
+
+       return 0;
+}
+
+static const struct i2c_device_id wm8400_i2c_id[] = {
+       { "wm8400", 0 },
+       { }
+};
+MODULE_DEVICE_TABLE(i2c, wm8400_i2c_id);
+
+static struct i2c_driver wm8400_i2c_driver = {
+       .driver = {
+               .name = "WM8400",
+               .owner = THIS_MODULE,
+       },
+       .probe    = wm8400_i2c_probe,
+       .remove   = wm8400_i2c_remove,
+       .id_table = wm8400_i2c_id,
+};
+#endif
+
+static int __init wm8400_module_init(void)
+{
+       int ret = -ENODEV;
+
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+       ret = i2c_add_driver(&wm8400_i2c_driver);
+       if (ret != 0)
+               pr_err("Failed to register I2C driver: %d\n", ret);
+#endif
+
+       return ret;
+}
+module_init(wm8400_module_init);
+
+static void __exit wm8400_module_exit(void)
+{
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+       i2c_del_driver(&wm8400_i2c_driver);
+#endif
+}
+module_exit(wm8400_module_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
index 5ba4bab6d43e75342e9932cd93b6207e6d0dd517..7d15e7c6bcad51f65860ff6b42e09881e8e2064a 100644 (file)
@@ -17,7 +17,7 @@
        Annapolis MD 21403
 
     Fixed (again!) the missing interrupt locking on TX/RX shifting.
-       Alan Cox <Alan.Cox@linux.org>
+       Alan Cox <alan@lxorguk.ukuu.org.uk>
 
     Removed calls to init_etherdev since they are no longer needed, and
     cleaned up modularization just a bit. The driver still allows only
     the board. Now getting 150K/second FTP with a 3c501 card. Still playing
     with a TX-TX optimisation to see if we can touch 180-200K/second as seems
     theoretically maximum.
-               19950402 Alan Cox <Alan.Cox@linux.org>
+               19950402 Alan Cox <alan@lxorguk.ukuu.org.uk>
 
     Cleaned up for 2.3.x because we broke SMP now.
-               20000208 Alan Cox <alan@redhat.com>
+               20000208 Alan Cox <alan@lxorguk.ukuu.org.uk>
 
     Check up pass for 2.5. Nothing significant changed
-               20021009 Alan Cox <alan@redhat.com>
+               20021009 Alan Cox <alan@lxorguk.ukuu.org.uk>
 
     Fixed zero fill corner case
-               20030104 Alan Cox <alan@redhat.com>
+               20030104 Alan Cox <alan@lxorguk.ukuu.org.uk>
 
 
    For the avoidance of doubt the "preferred form" of this code is one which
 
 
 static const char version[] =
-       DRV_NAME ".c: " DRV_VERSION " Alan Cox (alan@redhat.com).\n";
+       DRV_NAME ".c: " DRV_VERSION " Alan Cox (alan@lxorguk.ukuu.org.uk).\n";
 
 /*
  *     Braindamage remaining:
index e4e3241628d615bb752d99c3cfc18c2ab4e0124e..a0f8b6e2d0af80af13b5774fe40ec9c5ee132db7 100644 (file)
@@ -18,7 +18,7 @@
 
        2001/11/17 - Added ethtool support (jgarzik)
 
-       2002/10/28 - Locking updates for 2.5 (alan@redhat.com)
+       2002/10/28 - Locking updates for 2.5 (alan@lxorguk.ukuu.org.uk)
 
 */
 
index e9d529442b067e5c016195be99cfb0d5c4927dc2..1d8af334833124b3aa3660c9cb1a963814b30507 100644 (file)
@@ -2400,7 +2400,7 @@ config EHEA
          will be called ehea.
 
 config ENIC
-       tristate "E, the Cisco 10G Ethernet NIC"
+       tristate "Cisco 10G Ethernet NIC support"
        depends on PCI && INET
        select INET_LRO
        help
index a0b4c8516073d7384c4baeb2fd1064ec7dc31234..735fc9476403c4df27229298af32021ddd9f1762 100644 (file)
@@ -4,7 +4,7 @@
  *      - Jay Schulist <jschlst@samba.org>
  *
  *     With more than a little help from;
- *     - Alan Cox <Alan.Cox@linux.org> 
+ *     - Alan Cox <alan@lxorguk.ukuu.org.uk>
  *
  *      Derived from:
  *      - skeleton.c: A network driver outline for linux.
index 455ef529cd626ca167626161cc6cc3a35d981fb6..bc8e2413abd25d6c5106bcc83d10098fde44489d 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2003-2007 Chelsio, Inc. All rights reserved.
+ * Copyright (c) 2003-2008 Chelsio, Inc. All rights reserved.
  *
  * This software is available to you under a choice of one of two
  * licenses.  You may choose to be licensed under the terms of the GNU
index 744fac0b1617643294122f6cd9027a9671e9a9d0..5c3c05da4d96338e3f80e659c3c6e09d0017121b 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2005-2007 Chelsio, Inc. All rights reserved.
+ * Copyright (c) 2005-2008 Chelsio, Inc. All rights reserved.
  *
  * This software is available to you under a choice of one of two
  * licenses.  You may choose to be licensed under the terms of the GNU
index 593fb643a615086ff6a16cb8ed88844bf0705804..e312d315a42d4e5ded041e3c1d8c717ae6ae02a6 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2005-2007 Chelsio, Inc. All rights reserved.
+ * Copyright (c) 2005-2008 Chelsio, Inc. All rights reserved.
  *
  * This software is available to you under a choice of one of two
  * licenses.  You may choose to be licensed under the terms of the GNU
index 6ad92405d9a034268796aafb7ece1540aa37c6bc..1d8d46eb3c960e0efd3725d731f947abb02b438f 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2003-2007 Chelsio, Inc. All rights reserved.
+ * Copyright (c) 2003-2008 Chelsio, Inc. All rights reserved.
  *
  * This software is available to you under a choice of one of two
  * licenses.  You may choose to be licensed under the terms of the GNU
index 45e92164c26047765ed0aacb6cdb76025042c9b2..47e53769af5b7f074e45e07505be797263261458 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2006-2007 Chelsio, Inc. All rights reserved.
+ * Copyright (c) 2006-2008 Chelsio, Inc. All rights reserved.
  *
  * This software is available to you under a choice of one of two
  * licenses.  You may choose to be licensed under the terms of the GNU
index 3e8d5faec3a4a9b6dd7ab9aeebf7e6f62ef74431..b19e4376ba76ec1584b948507783f8a566c7575b 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2003-2007 Chelsio, Inc. All rights reserved.
+ * Copyright (c) 2003-2008 Chelsio, Inc. All rights reserved.
  *
  * This software is available to you under a choice of one of two
  * licenses.  You may choose to be licensed under the terms of the GNU
index f31985df0bb9a2646adefd39746b8f05ab89072d..1ace41a13ac360363b2857475920a014daad4e94 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2003-2007 Chelsio, Inc. All rights reserved.
+ * Copyright (c) 2003-2008 Chelsio, Inc. All rights reserved.
  *
  * This software is available to you under a choice of one of two
  * licenses.  You may choose to be licensed under the terms of the GNU
index 0f6fd63b28475cdf5bc0be26ebb5cf07cd88035d..265aa8a15afaed2eb5a61177917d07cd6649b476 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2006-2007 Chelsio, Inc. All rights reserved.
+ * Copyright (c) 2006-2008 Chelsio, Inc. All rights reserved.
  *
  * This software is available to you under a choice of one of two
  * licenses.  You may choose to be licensed under the terms of the GNU
index 7a379138b5a6ebe757da3ce5c86abfd37174fc46..d514e5019dfc36b0935bace2be32056b0ee4f272 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2006-2007 Chelsio, Inc. All rights reserved.
+ * Copyright (c) 2006-2008 Chelsio, Inc. All rights reserved.
  *
  * This software is available to you under a choice of one of two
  * licenses.  You may choose to be licensed under the terms of the GNU
index b75ddd8777fe8dbd0c900e775351e9ebe753fe13..0d9b0e6dccff41f58544345b0570cd762241b0c3 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2004-2007 Chelsio, Inc. All rights reserved.
+ * Copyright (c) 2004-2008 Chelsio, Inc. All rights reserved.
  *
  * This software is available to you under a choice of one of two
  * licenses.  You may choose to be licensed under the terms of the GNU
index b2c5314582aa0cb954872d29127388f8edc5645c..4407ac9bb5559e87e427c445982cfa188a975ccf 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2003-2007 Chelsio, Inc. All rights reserved.
+ * Copyright (c) 2003-2008 Chelsio, Inc. All rights reserved.
  *
  * This software is available to you under a choice of one of two
  * licenses.  You may choose to be licensed under the terms of the GNU
index 42ce65f76a87517904bd89b4378cace767c53580..fd3eb07e3f40a5bb093307a1dab7c21f3bef338e 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2003-2007 Chelsio, Inc. All rights reserved.
+ * Copyright (c) 2003-2008 Chelsio, Inc. All rights reserved.
  *
  * This software is available to you under a choice of one of two
  * licenses.  You may choose to be licensed under the terms of the GNU
index 4c4d6e877ea698baf6ed92bbfc606f1b389dc476..3b5517b8fbde74a1131d932c890d0d5e3f39ab97 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2003-2007 Chelsio, Inc. All rights reserved.
+ * Copyright (c) 2003-2008 Chelsio, Inc. All rights reserved.
  *
  * This software is available to you under a choice of one of two
  * licenses.  You may choose to be licensed under the terms of the GNU
index 87919419b707faf3107877181dd96ec0b8cd5938..c6480be0bc1f7ed033b9a9ed0d856d12b4490255 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2005-2007 Chelsio, Inc. All rights reserved.
+ * Copyright (c) 2005-2008 Chelsio, Inc. All rights reserved.
  *
  * This software is available to you under a choice of one of two
  * licenses.  You may choose to be licensed under the terms of the GNU
index 917970ed24a1cbd1369d1160889ce8a61b0f0307..852c399a8b0ae838a0c963c2b3788918626e1e02 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2004-2007 Chelsio, Inc. All rights reserved.
+ * Copyright (c) 2004-2008 Chelsio, Inc. All rights reserved.
  *
  * This software is available to you under a choice of one of two
  * licenses.  You may choose to be licensed under the terms of the GNU
index 4da5b09b9bc29221ec7a2eae370bb9b8832a398a..968f64be3743acb165dbe40013e5d2a4dd60ee42 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2003-2007 Chelsio, Inc. All rights reserved.
+ * Copyright (c) 2003-2008 Chelsio, Inc. All rights reserved.
  *
  * This software is available to you under a choice of one of two
  * licenses.  You may choose to be licensed under the terms of the GNU
index 0a21cfbd2b2135fd47aedad1803fcc5d6c446d8f..be55e9ae74d18caeadfc8f6d2e6ca663ef773692 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2006-2007 Chelsio Communications.  All rights reserved.
+ * Copyright (C) 2006-2008 Chelsio Communications.  All rights reserved.
  *
  * This software is available to you under a choice of one of two
  * licenses.  You may choose to be licensed under the terms of the GNU
index 29db711303b9040070aa6c2bc54f7295ed520a24..bb8698a867545384842032a10d330c3ee785cc0b 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2003-2007 Chelsio, Inc. All rights reserved.
+ * Copyright (c) 2003-2008 Chelsio, Inc. All rights reserved.
  *
  * This software is available to you under a choice of one of two
  * licenses.  You may choose to be licensed under the terms of the GNU
@@ -35,7 +35,7 @@
 #define DRV_DESC "Chelsio T3 Network Driver"
 #define DRV_NAME "cxgb3"
 /* Driver version */
-#define DRV_VERSION "1.0-ko"
+#define DRV_VERSION "1.1.0-ko"
 
 /* Firmware version */
 #define FW_VERSION_MAJOR 7
index 306c2dc4ab34884d2687fdae9c302d80b01b2426..33f956bd6b59b8f96134c9e09cc23149f4556b77 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2005-2007 Chelsio, Inc. All rights reserved.
+ * Copyright (c) 2005-2008 Chelsio, Inc. All rights reserved.
  *
  * This software is available to you under a choice of one of two
  * licenses.  You may choose to be licensed under the terms of the GNU
index ffdc0a1892bd2334ed770647ea5c82a8ba498645..9d7786937aadf4c7e806d3bec7c6c58e8dd46d04 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2005-2007 Chelsio, Inc. All rights reserved.
+ * Copyright (c) 2005-2008 Chelsio, Inc. All rights reserved.
  *
  * This software is available to you under a choice of one of two
  * licenses.  You may choose to be licensed under the terms of the GNU
index 795c594a4b7c550e1d4200206205961aff4820a5..b751c1b96cfa77e2d445e3c182a4d2003cb8de73 100644 (file)
@@ -8,7 +8,7 @@
  *
  * Many modifications, and currently maintained, by
  *  Philip Blundell <philb@gnu.org>
- * Added the Compaq LTE  Alan Cox <alan@redhat.com>
+ * Added the Compaq LTE  Alan Cox <alan@lxorguk.ukuu.org.uk>
  * Added MCA support Adam Fritzler
  *
  * Note - this driver is experimental still - it has problems on faster
index f3a47a87dbbe3206304d2827187e2561780d165a..180e968dc54d87401eaa174c30a724223425d4bf 100644 (file)
@@ -34,6 +34,7 @@
 #include <linux/ip.h>
 #include <linux/ipv6.h>
 #include <linux/tcp.h>
+#include <net/ip6_checksum.h>
 
 #include "cq_enet_desc.h"
 #include "vnic_dev.h"
index 95e3464068db540c1d2a3f530960586ee2d0cfe0..f02764725a221fdde54b43ea9be1e7d65c8e8b75 100644 (file)
@@ -71,7 +71,7 @@ History:
   June 1st, 2000
        corrected version codes, added support for the latest 2.3 changes
   Oct 28th, 2002
-       cleaned up for the 2.5 tree <alan@redhat.com>
+       cleaned up for the 2.5 tree <alan@lxorguk.ukuu.org.uk>
 
  *************************************************************************/
 
index 156f159aafbb4819ff9c88433c2efee7897767a4..81c6cdc3851f8afd54d3319df5e6e9b99d516a25 100644 (file)
@@ -37,6 +37,7 @@
 #include <linux/tcp.h>
 #include <linux/udp.h>
 #include <linux/if_vlan.h>
+#include <net/ip6_checksum.h>
 #include "jme.h"
 
 static int force_pseudohp = -1;
index 51ad3765e0750c2857555c437ed259631a5394b7..85587a6667b9f170d3b55686a6474c20754a4c15 100644 (file)
@@ -9,7 +9,7 @@
  *     2 of the License, or (at your option) any later version.
  *
  *     Copyright (C) 1996 Paul Mackerras.
- *     Copyright (C) 1998 Alan Cox <alan@redhat.com>
+ *     Copyright (C) 1998 Alan Cox <alan@lxorguk.ukuu.org.uk>
  *
  *     Modified heavily by Joshua M. Thompson based on Dave Huang's NetBSD driver
  *
index 0b28d0d8ffa8d766a5e05c43d778773f3781ecb2..c235cdba69c6384d11b83ace2b2a1aac2f4a1905 100644 (file)
@@ -15,7 +15,7 @@
     incorporated herein by reference.
     Donald Becker may be reached at becker@scyld.com
     
-    Updated for 2.5.x by Alan Cox <alan@redhat.com>
+    Updated for 2.5.x by Alan Cox <alan@lxorguk.ukuu.org.uk>
 
 ======================================================================*/
 
index 54df34f21c5fe78c11073dc3adf9c065910b9442..448cd40aeba5ab37c2c913db34745ac87b2857fd 100644 (file)
@@ -69,7 +69,7 @@ Driver Notes and Issues
 History
 -------------------------------------------------------------------------------
 Log: nmclan_cs.c,v
- * 2.5.75-ac1 2003/07/11 Alan Cox <alan@redhat.com>
+ * 2.5.75-ac1 2003/07/11 Alan Cox <alan@lxorguk.ukuu.org.uk>
  * Fixed hang on card eject as we probe it
  * Cleaned up to use new style locking.
  *
index 6671e2da0d572c07e09bdaffaf12f3d5da9af48b..d0ed1ef284a80404510e8f169537f40e4cb678a3 100644 (file)
@@ -55,6 +55,7 @@ EXPORT_SYMBOL(mdiobus_alloc);
 
 /**
  * mdiobus_release - mii_bus device release callback
+ * @d: the target struct device that contains the mii_bus
  *
  * Description: called when the last reference to an mii_bus is
  * dropped, to free the underlying memory.
index 17162748005893a86ca206faf95c5ec194be5eb5..f11e900b437b66044b422da02e26fe4a4efdeff6 100644 (file)
@@ -557,6 +557,7 @@ int genphy_restart_aneg(struct phy_device *phydev)
 
        return ctl;
 }
+EXPORT_SYMBOL(genphy_restart_aneg);
 
 
 /**
index c37ea436c9189adebf24bb579ba68e0659eae184..38116f9d41635c7cc23ad13a4e6b618b59cf5b06 100644 (file)
@@ -58,7 +58,7 @@
  */
 #if (PAGE_SHIFT == 12) || (PAGE_SHIFT == 13)   /* 4k & 8k pages */
 #define TX_DESC_PER_OAL ((MAX_SKB_FRAGS - TX_DESC_PER_IOCB) + 2)
-#elif (PAGE_SHIFT == 16)       /* 64k pages */
+#else /* all other page sizes */
 #define TX_DESC_PER_OAL 0
 #endif
 
index 297877b68c46740e81c49af69d21cc450c7daef3..4b2caa6b7ac5eba5a01d2cc9c3ddbe0611e3a573 100644 (file)
@@ -39,6 +39,7 @@
 #include <linux/delay.h>
 #include <linux/mm.h>
 #include <linux/vmalloc.h>
+#include <net/ip6_checksum.h>
 
 #include "qlge.h"
 
index ec871f646766dc7294101fe5ad7f789b66d3f0bb..c41d68761364550ebe2d2d8376f2a8c293b5d799 100644 (file)
@@ -29,7 +29,8 @@
  *
  *     Tigran Aivazian <tigran@sco.com>:       TLan_PciProbe() now uses
  *                                             new PCI BIOS interface.
- *     Alan Cox        <alan@redhat.com>:      Fixed the out of memory
+ *     Alan Cox        <alan@lxorguk.ukuu.org.uk>:
+ *                                             Fixed the out of memory
  *                                             handling.
  *
  *     Torben Mathiasen <torben.mathiasen@compaq.com> New Maintainer!
index fa73e6eed6be52ed1c9f7972f70e26a05112ad59..ed50d288e4940a79bdefca127a6c92d5713ab45d 100644 (file)
@@ -25,7 +25,7 @@
  *  To do:
  *    1. Multicast support.
  *
- *  Initial 2.5 cleanup Alan Cox <alan@redhat.com>  2002/10/28
+ *  Initial 2.5 cleanup Alan Cox <alan@lxorguk.ukuu.org.uk>  2002/10/28
  */
 
 #include <linux/module.h>
index f54c45049d50d86c700e3f18a5c2525321a662c9..124d5d690dde2508a29b8d5ae7bd907bcd259c38 100644 (file)
@@ -1688,6 +1688,7 @@ static void __devinit de21040_get_mac_address (struct de_private *de)
        unsigned i;
 
        dw32 (ROMCmd, 0);       /* Reset the pointer with a dummy write. */
+       udelay(5);
 
        for (i = 0; i < 6; i++) {
                int value, boguscnt = 100000;
index 656200472fa17aa3e5e8e5d592c4fc2fa1ff2077..8e46a513a252a8fecf48629e064a6f220bc7c100 100644 (file)
@@ -23,7 +23,7 @@
     Marcelo Tosatti <marcelo@conectiva.com.br> :
     Made it compile in 2.3 (device to net_device)
 
-    Alan Cox <alan@redhat.com> :
+    Alan Cox <alan@lxorguk.ukuu.org.uk> :
     Cleaned up for kernel merge.
     Removed the back compatibility support
     Reformatted, fixing spelling etc as I went
@@ -49,7 +49,7 @@
     support.  Updated PCI resource allocation.  Do not
     forget to unmap PCI mapped skbs.
 
-    Alan Cox <alan@redhat.com>
+    Alan Cox <alan@lxorguk.ukuu.org.uk>
     Added new PCI identifiers provided by Clear Zhang at ALi
     for their 1563 ethernet device.
 
index ad20f96edfa11e5d597881b49795c7967ee7e2a4..2dced383bcfb8a3f93ef4cfc69569f40105cdba5 100644 (file)
@@ -12,7 +12,7 @@
  *     Scatter gather
  *     More testing
  *
- * The changes are (c) Copyright 2004, Red Hat Inc. <alan@redhat.com>
+ * The changes are (c) Copyright 2004, Red Hat Inc. <alan@lxorguk.ukuu.org.uk>
  * Additional fixes and clean up: Francois Romieu
  *
  * This source has not been verified for use in safety critical systems.
index 243bd8d918fedfdf350fce7e0a1a3c0be8451b7d..ccd9cd35ecbe9e7f82ca370739415add9e4026b3 100644 (file)
@@ -18,7 +18,8 @@
  *     DMA now uses get_free_page as kmalloc buffers may span a 64K 
  *     boundary.
  *
- *     Modified for SMP safety and SMP locking by Alan Cox <alan@redhat.com>
+ *     Modified for SMP safety and SMP locking by Alan Cox
+ *                                     <alan@lxorguk.ukuu.org.uk>
  *
  *     Performance
  *
index 4f372396c5121a29a06b970d8ac865f2ed996556..85b3e785d484f31e942d5d5a47a349f02a3a0b22 100644 (file)
@@ -2,7 +2,7 @@
  *     Description of Z8530 Z85C30 and Z85230 communications chips
  *
  * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
- * Copyright (C) 1998 Alan Cox <alan@redhat.com>
+ * Copyright (C) 1998 Alan Cox <alan@lxorguk.ukuu.org.uk>
  */
 
 #ifndef _Z8530_H
index 136220b5ca81a389fee29bf0a28d622723c0d927..e939a73ff794a731583292d5e41e7e8a23f64d8e 100644 (file)
@@ -4387,7 +4387,7 @@ MODULE_LICENSE("GPL");
  *
  * Thanks go also to:
  *     James Ashton (jaa101@syseng.anu.edu.au),
- *     Alan Cox (alan@redhat.com),
+ *     Alan Cox (alan@lxorguk.ukuu.org.uk),
  *     Allan Creighton (allanc@cs.usyd.edu.au),
  *     Matthew Geier (matthew@cs.usyd.edu.au),
  *     Remo di Giovanni (remo@cs.usyd.edu.au),
index b33ac47dd8df710956340f9cd258e6b6e1c4cd23..44d31bbf39e49d57409b5f41a3e487040974d1e6 100644 (file)
  *
  * Thanks go also to:
  *     James Ashton <jaa101@syseng.anu.edu.au>,
- *     Alan Cox <alan@redhat.com>,
+ *     Alan Cox <alan@lxorguk.ukuu.org.uk>,
  *     Allan Creighton <allanc@cs.usyd.edu.au>,
  *     Matthew Geier <matthew@cs.usyd.edu.au>,
  *     Remo di Giovanni <remo@cs.usyd.edu.au>,
index 8467d028732514277a098815f3597ebfed1591ae..7d27631e6e627bba3202633d68ff5578dd84e663 100644 (file)
@@ -276,7 +276,7 @@ int __init ibmphp_access_ebda (void)
        iounmap (io_mem);
        debug ("returned ebda segment: %x\n", ebda_seg);
        
-       io_mem = ioremap (ebda_seg<<4, 65000);
+       io_mem = ioremap(ebda_seg<<4, 1024);
        if (!io_mem )
                return -ENOMEM;
        next_offset = 0x180;
index 3abbfad9ddab592f592001e31e3911efe002566c..d5e2106760f81743267dd85169553c4158c1373b 100644 (file)
@@ -378,11 +378,10 @@ static int pbus_size_mem(struct pci_bus *bus, unsigned long mask, unsigned long
        align = 0;
        min_align = 0;
        for (order = 0; order <= max_order; order++) {
-#ifdef CONFIG_RESOURCES_64BIT
-               resource_size_t align1 = 1ULL << (order + 20);
-#else
-               resource_size_t align1 = 1U << (order + 20);
-#endif
+               resource_size_t align1 = 1;
+
+               align1 <<= (order + 20);
+
                if (!align)
                        min_align = align1;
                else if (ALIGN(align + min_align, min_align) < align1)
index a656128f1fdd80c70f01f1ec4f128b6b2aacb954..4dada6ee1119464a7d3136bfb47d3805c605d0c4 100644 (file)
@@ -56,4 +56,28 @@ config REGULATOR_BQ24022
          charging select between 100 mA and 500 mA charging current
          limit.
 
+config REGULATOR_WM8350
+       tristate "Wolfson Microelectroncis WM8350 AudioPlus PMIC"
+       depends on MFD_WM8350
+       select REGULATOR
+       help
+         This driver provides support for the voltage and current regulators
+          of the WM8350 AudioPlus PMIC.
+
+config REGULATOR_WM8400
+       tristate "Wolfson Microelectroncis WM8400 AudioPlus PMIC"
+       depends on MFD_WM8400
+       select REGULATOR
+       help
+         This driver provides support for the voltage regulators of the
+         WM8400 AudioPlus PMIC.
+
+config REGULATOR_DA903X
+       tristate "Support regulators on Dialog Semiconductor DA9030/DA9034 PMIC"
+       depends on PMIC_DA903X
+       select REGULATOR
+       help
+         Say y here to support the BUCKs and LDOs regulators found on
+         Dialog Semiconductor DA9030/DA9034 PMIC.
+
 endmenu
index ac2c64efe65c6478a851aecb11cb9eb4f793466e..254d40c02ee8cefd29e4f332f12b6c99603d705d 100644 (file)
@@ -8,5 +8,8 @@ obj-$(CONFIG_REGULATOR_FIXED_VOLTAGE) += fixed.o
 obj-$(CONFIG_REGULATOR_VIRTUAL_CONSUMER) += virtual.o
 
 obj-$(CONFIG_REGULATOR_BQ24022) += bq24022.o
+obj-$(CONFIG_REGULATOR_WM8350) += wm8350-regulator.o
+obj-$(CONFIG_REGULATOR_WM8400) += wm8400-regulator.o
+obj-$(CONFIG_REGULATOR_DA903X) += da903x.o
 
 ccflags-$(CONFIG_REGULATOR_DEBUG) += -DDEBUG
index 263699d6152d2b662e1889df0cd37235c51c664c..366565aba865380672a3ed0e2e04e07b98d40238 100644 (file)
 #include <linux/regulator/bq24022.h>
 #include <linux/regulator/driver.h>
 
+
 static int bq24022_set_current_limit(struct regulator_dev *rdev,
                                        int min_uA, int max_uA)
 {
-       struct platform_device *pdev = rdev_get_drvdata(rdev);
-       struct bq24022_mach_info *pdata = pdev->dev.platform_data;
+       struct bq24022_mach_info *pdata = rdev_get_drvdata(rdev);
 
-       dev_dbg(&pdev->dev, "setting current limit to %s mA\n",
+       dev_dbg(rdev_get_dev(rdev), "setting current limit to %s mA\n",
                max_uA >= 500000 ? "500" : "100");
 
        /* REVISIT: maybe return error if min_uA != 0 ? */
@@ -34,18 +34,16 @@ static int bq24022_set_current_limit(struct regulator_dev *rdev,
 
 static int bq24022_get_current_limit(struct regulator_dev *rdev)
 {
-       struct platform_device *pdev = rdev_get_drvdata(rdev);
-       struct bq24022_mach_info *pdata = pdev->dev.platform_data;
+       struct bq24022_mach_info *pdata = rdev_get_drvdata(rdev);
 
        return gpio_get_value(pdata->gpio_iset2) ? 500000 : 100000;
 }
 
 static int bq24022_enable(struct regulator_dev *rdev)
 {
-       struct platform_device *pdev = rdev_get_drvdata(rdev);
-       struct bq24022_mach_info *pdata = pdev->dev.platform_data;
+       struct bq24022_mach_info *pdata = rdev_get_drvdata(rdev);
 
-       dev_dbg(&pdev->dev, "enabling charger\n");
+       dev_dbg(rdev_get_dev(rdev), "enabling charger\n");
 
        gpio_set_value(pdata->gpio_nce, 0);
        return 0;
@@ -53,10 +51,9 @@ static int bq24022_enable(struct regulator_dev *rdev)
 
 static int bq24022_disable(struct regulator_dev *rdev)
 {
-       struct platform_device *pdev = rdev_get_drvdata(rdev);
-       struct bq24022_mach_info *pdata = pdev->dev.platform_data;
+       struct bq24022_mach_info *pdata = rdev_get_drvdata(rdev);
 
-       dev_dbg(&pdev->dev, "disabling charger\n");
+       dev_dbg(rdev_get_dev(rdev), "disabling charger\n");
 
        gpio_set_value(pdata->gpio_nce, 1);
        return 0;
@@ -108,7 +105,7 @@ static int __init bq24022_probe(struct platform_device *pdev)
        ret = gpio_direction_output(pdata->gpio_iset2, 0);
        ret = gpio_direction_output(pdata->gpio_nce, 1);
 
-       bq24022 = regulator_register(&bq24022_desc, pdev);
+       bq24022 = regulator_register(&bq24022_desc, &pdev->dev, pdata);
        if (IS_ERR(bq24022)) {
                dev_dbg(&pdev->dev, "couldn't register regulator\n");
                ret = PTR_ERR(bq24022);
index 9c79862615685d39d92f7e2b73cd2a8638bf21d7..02a774424e8de2bbade4eafeaadf6b8160fc05ef 100644 (file)
@@ -2,8 +2,9 @@
  * core.c  --  Voltage/Current Regulator framework.
  *
  * Copyright 2007, 2008 Wolfson Microelectronics PLC.
+ * Copyright 2008 SlimLogic Ltd.
  *
- * Author: Liam Girdwood <liam.girdwood@wolfsonmicro.com>
+ * Author: Liam Girdwood <lrg@slimlogic.co.uk>
  *
  *  This program is free software; you can redistribute  it and/or modify it
  *  under  the terms of  the GNU General  Public License as published by the
@@ -64,14 +65,9 @@ struct regulator_map {
        struct list_head list;
        struct device *dev;
        const char *supply;
-       const char *regulator;
+       struct regulator_dev *regulator;
 };
 
-static inline struct regulator_dev *to_rdev(struct device *d)
-{
-       return container_of(d, struct regulator_dev, dev);
-}
-
 /*
  * struct regulator
  *
@@ -227,7 +223,7 @@ static ssize_t device_requested_uA_show(struct device *dev,
 static ssize_t regulator_uV_show(struct device *dev,
                                struct device_attribute *attr, char *buf)
 {
-       struct regulator_dev *rdev = to_rdev(dev);
+       struct regulator_dev *rdev = dev_get_drvdata(dev);
        ssize_t ret;
 
        mutex_lock(&rdev->mutex);
@@ -240,15 +236,31 @@ static ssize_t regulator_uV_show(struct device *dev,
 static ssize_t regulator_uA_show(struct device *dev,
                                struct device_attribute *attr, char *buf)
 {
-       struct regulator_dev *rdev = to_rdev(dev);
+       struct regulator_dev *rdev = dev_get_drvdata(dev);
 
        return sprintf(buf, "%d\n", _regulator_get_current_limit(rdev));
 }
 
+static ssize_t regulator_name_show(struct device *dev,
+                            struct device_attribute *attr, char *buf)
+{
+       struct regulator_dev *rdev = dev_get_drvdata(dev);
+       const char *name;
+
+       if (rdev->constraints->name)
+               name = rdev->constraints->name;
+       else if (rdev->desc->name)
+               name = rdev->desc->name;
+       else
+               name = "";
+
+       return sprintf(buf, "%s\n", name);
+}
+
 static ssize_t regulator_opmode_show(struct device *dev,
                                    struct device_attribute *attr, char *buf)
 {
-       struct regulator_dev *rdev = to_rdev(dev);
+       struct regulator_dev *rdev = dev_get_drvdata(dev);
        int mode = _regulator_get_mode(rdev);
 
        switch (mode) {
@@ -267,7 +279,7 @@ static ssize_t regulator_opmode_show(struct device *dev,
 static ssize_t regulator_state_show(struct device *dev,
                                   struct device_attribute *attr, char *buf)
 {
-       struct regulator_dev *rdev = to_rdev(dev);
+       struct regulator_dev *rdev = dev_get_drvdata(dev);
        int state = _regulator_is_enabled(rdev);
 
        if (state > 0)
@@ -281,7 +293,7 @@ static ssize_t regulator_state_show(struct device *dev,
 static ssize_t regulator_min_uA_show(struct device *dev,
                                    struct device_attribute *attr, char *buf)
 {
-       struct regulator_dev *rdev = to_rdev(dev);
+       struct regulator_dev *rdev = dev_get_drvdata(dev);
 
        if (!rdev->constraints)
                return sprintf(buf, "constraint not defined\n");
@@ -292,7 +304,7 @@ static ssize_t regulator_min_uA_show(struct device *dev,
 static ssize_t regulator_max_uA_show(struct device *dev,
                                    struct device_attribute *attr, char *buf)
 {
-       struct regulator_dev *rdev = to_rdev(dev);
+       struct regulator_dev *rdev = dev_get_drvdata(dev);
 
        if (!rdev->constraints)
                return sprintf(buf, "constraint not defined\n");
@@ -303,7 +315,7 @@ static ssize_t regulator_max_uA_show(struct device *dev,
 static ssize_t regulator_min_uV_show(struct device *dev,
                                    struct device_attribute *attr, char *buf)
 {
-       struct regulator_dev *rdev = to_rdev(dev);
+       struct regulator_dev *rdev = dev_get_drvdata(dev);
 
        if (!rdev->constraints)
                return sprintf(buf, "constraint not defined\n");
@@ -314,7 +326,7 @@ static ssize_t regulator_min_uV_show(struct device *dev,
 static ssize_t regulator_max_uV_show(struct device *dev,
                                    struct device_attribute *attr, char *buf)
 {
-       struct regulator_dev *rdev = to_rdev(dev);
+       struct regulator_dev *rdev = dev_get_drvdata(dev);
 
        if (!rdev->constraints)
                return sprintf(buf, "constraint not defined\n");
@@ -325,7 +337,7 @@ static ssize_t regulator_max_uV_show(struct device *dev,
 static ssize_t regulator_total_uA_show(struct device *dev,
                                      struct device_attribute *attr, char *buf)
 {
-       struct regulator_dev *rdev = to_rdev(dev);
+       struct regulator_dev *rdev = dev_get_drvdata(dev);
        struct regulator *regulator;
        int uA = 0;
 
@@ -339,14 +351,14 @@ static ssize_t regulator_total_uA_show(struct device *dev,
 static ssize_t regulator_num_users_show(struct device *dev,
                                      struct device_attribute *attr, char *buf)
 {
-       struct regulator_dev *rdev = to_rdev(dev);
+       struct regulator_dev *rdev = dev_get_drvdata(dev);
        return sprintf(buf, "%d\n", rdev->use_count);
 }
 
 static ssize_t regulator_type_show(struct device *dev,
                                  struct device_attribute *attr, char *buf)
 {
-       struct regulator_dev *rdev = to_rdev(dev);
+       struct regulator_dev *rdev = dev_get_drvdata(dev);
 
        switch (rdev->desc->type) {
        case REGULATOR_VOLTAGE:
@@ -360,7 +372,7 @@ static ssize_t regulator_type_show(struct device *dev,
 static ssize_t regulator_suspend_mem_uV_show(struct device *dev,
                                struct device_attribute *attr, char *buf)
 {
-       struct regulator_dev *rdev = to_rdev(dev);
+       struct regulator_dev *rdev = dev_get_drvdata(dev);
 
        if (!rdev->constraints)
                return sprintf(buf, "not defined\n");
@@ -370,7 +382,7 @@ static ssize_t regulator_suspend_mem_uV_show(struct device *dev,
 static ssize_t regulator_suspend_disk_uV_show(struct device *dev,
                                struct device_attribute *attr, char *buf)
 {
-       struct regulator_dev *rdev = to_rdev(dev);
+       struct regulator_dev *rdev = dev_get_drvdata(dev);
 
        if (!rdev->constraints)
                return sprintf(buf, "not defined\n");
@@ -380,7 +392,7 @@ static ssize_t regulator_suspend_disk_uV_show(struct device *dev,
 static ssize_t regulator_suspend_standby_uV_show(struct device *dev,
                                struct device_attribute *attr, char *buf)
 {
-       struct regulator_dev *rdev = to_rdev(dev);
+       struct regulator_dev *rdev = dev_get_drvdata(dev);
 
        if (!rdev->constraints)
                return sprintf(buf, "not defined\n");
@@ -406,7 +418,7 @@ static ssize_t suspend_opmode_show(struct regulator_dev *rdev,
 static ssize_t regulator_suspend_mem_mode_show(struct device *dev,
                                struct device_attribute *attr, char *buf)
 {
-       struct regulator_dev *rdev = to_rdev(dev);
+       struct regulator_dev *rdev = dev_get_drvdata(dev);
 
        if (!rdev->constraints)
                return sprintf(buf, "not defined\n");
@@ -417,7 +429,7 @@ static ssize_t regulator_suspend_mem_mode_show(struct device *dev,
 static ssize_t regulator_suspend_disk_mode_show(struct device *dev,
                                struct device_attribute *attr, char *buf)
 {
-       struct regulator_dev *rdev = to_rdev(dev);
+       struct regulator_dev *rdev = dev_get_drvdata(dev);
 
        if (!rdev->constraints)
                return sprintf(buf, "not defined\n");
@@ -428,7 +440,7 @@ static ssize_t regulator_suspend_disk_mode_show(struct device *dev,
 static ssize_t regulator_suspend_standby_mode_show(struct device *dev,
                                struct device_attribute *attr, char *buf)
 {
-       struct regulator_dev *rdev = to_rdev(dev);
+       struct regulator_dev *rdev = dev_get_drvdata(dev);
 
        if (!rdev->constraints)
                return sprintf(buf, "not defined\n");
@@ -439,7 +451,7 @@ static ssize_t regulator_suspend_standby_mode_show(struct device *dev,
 static ssize_t regulator_suspend_mem_state_show(struct device *dev,
                                   struct device_attribute *attr, char *buf)
 {
-       struct regulator_dev *rdev = to_rdev(dev);
+       struct regulator_dev *rdev = dev_get_drvdata(dev);
 
        if (!rdev->constraints)
                return sprintf(buf, "not defined\n");
@@ -453,7 +465,7 @@ static ssize_t regulator_suspend_mem_state_show(struct device *dev,
 static ssize_t regulator_suspend_disk_state_show(struct device *dev,
                                   struct device_attribute *attr, char *buf)
 {
-       struct regulator_dev *rdev = to_rdev(dev);
+       struct regulator_dev *rdev = dev_get_drvdata(dev);
 
        if (!rdev->constraints)
                return sprintf(buf, "not defined\n");
@@ -467,7 +479,7 @@ static ssize_t regulator_suspend_disk_state_show(struct device *dev,
 static ssize_t regulator_suspend_standby_state_show(struct device *dev,
                                   struct device_attribute *attr, char *buf)
 {
-       struct regulator_dev *rdev = to_rdev(dev);
+       struct regulator_dev *rdev = dev_get_drvdata(dev);
 
        if (!rdev->constraints)
                return sprintf(buf, "not defined\n");
@@ -477,7 +489,9 @@ static ssize_t regulator_suspend_standby_state_show(struct device *dev,
        else
                return sprintf(buf, "disabled\n");
 }
+
 static struct device_attribute regulator_dev_attrs[] = {
+       __ATTR(name, 0444, regulator_name_show, NULL),
        __ATTR(microvolts, 0444, regulator_uV_show, NULL),
        __ATTR(microamps, 0444, regulator_uA_show, NULL),
        __ATTR(opmode, 0444, regulator_opmode_show, NULL),
@@ -512,7 +526,7 @@ static struct device_attribute regulator_dev_attrs[] = {
 
 static void regulator_dev_release(struct device *dev)
 {
-       struct regulator_dev *rdev = to_rdev(dev);
+       struct regulator_dev *rdev = dev_get_drvdata(dev);
        kfree(rdev);
 }
 
@@ -569,8 +583,11 @@ static int suspend_set_state(struct regulator_dev *rdev,
 
        /* enable & disable are mandatory for suspend control */
        if (!rdev->desc->ops->set_suspend_enable ||
-               !rdev->desc->ops->set_suspend_disable)
+               !rdev->desc->ops->set_suspend_disable) {
+               printk(KERN_ERR "%s: no way to set suspend state\n",
+                       __func__);
                return -EINVAL;
+       }
 
        if (rstate->enabled)
                ret = rdev->desc->ops->set_suspend_enable(rdev);
@@ -656,6 +673,155 @@ static void print_constraints(struct regulator_dev *rdev)
        printk(KERN_INFO "regulator: %s: %s\n", rdev->desc->name, buf);
 }
 
+/**
+ * set_machine_constraints - sets regulator constraints
+ * @regulator: regulator source
+ *
+ * Allows platform initialisation code to define and constrain
+ * regulator circuits e.g. valid voltage/current ranges, etc.  NOTE:
+ * Constraints *must* be set by platform code in order for some
+ * regulator operations to proceed i.e. set_voltage, set_current_limit,
+ * set_mode.
+ */
+static int set_machine_constraints(struct regulator_dev *rdev,
+       struct regulation_constraints *constraints)
+{
+       int ret = 0;
+       const char *name;
+       struct regulator_ops *ops = rdev->desc->ops;
+
+       if (constraints->name)
+               name = constraints->name;
+       else if (rdev->desc->name)
+               name = rdev->desc->name;
+       else
+               name = "regulator";
+
+       rdev->constraints = constraints;
+
+       /* do we need to apply the constraint voltage */
+       if (rdev->constraints->apply_uV &&
+               rdev->constraints->min_uV == rdev->constraints->max_uV &&
+               ops->set_voltage) {
+               ret = ops->set_voltage(rdev,
+                       rdev->constraints->min_uV, rdev->constraints->max_uV);
+                       if (ret < 0) {
+                               printk(KERN_ERR "%s: failed to apply %duV constraint to %s\n",
+                                      __func__,
+                                      rdev->constraints->min_uV, name);
+                               rdev->constraints = NULL;
+                               goto out;
+                       }
+       }
+
+       /* are we enabled at boot time by firmware / bootloader */
+       if (rdev->constraints->boot_on)
+               rdev->use_count = 1;
+
+       /* do we need to setup our suspend state */
+       if (constraints->initial_state) {
+               ret = suspend_prepare(rdev, constraints->initial_state);
+               if (ret < 0) {
+                       printk(KERN_ERR "%s: failed to set suspend state for %s\n",
+                              __func__, name);
+                       rdev->constraints = NULL;
+                       goto out;
+               }
+       }
+
+       /* if always_on is set then turn the regulator on if it's not
+        * already on. */
+       if (constraints->always_on && ops->enable &&
+           ((ops->is_enabled && !ops->is_enabled(rdev)) ||
+            (!ops->is_enabled && !constraints->boot_on))) {
+               ret = ops->enable(rdev);
+               if (ret < 0) {
+                       printk(KERN_ERR "%s: failed to enable %s\n",
+                              __func__, name);
+                       rdev->constraints = NULL;
+                       goto out;
+               }
+       }
+
+       print_constraints(rdev);
+out:
+       return ret;
+}
+
+/**
+ * set_supply - set regulator supply regulator
+ * @regulator: regulator name
+ * @supply: supply regulator name
+ *
+ * Called by platform initialisation code to set the supply regulator for this
+ * regulator. This ensures that a regulators supply will also be enabled by the
+ * core if it's child is enabled.
+ */
+static int set_supply(struct regulator_dev *rdev,
+       struct regulator_dev *supply_rdev)
+{
+       int err;
+
+       err = sysfs_create_link(&rdev->dev.kobj, &supply_rdev->dev.kobj,
+                               "supply");
+       if (err) {
+               printk(KERN_ERR
+                      "%s: could not add device link %s err %d\n",
+                      __func__, supply_rdev->dev.kobj.name, err);
+                      goto out;
+       }
+       rdev->supply = supply_rdev;
+       list_add(&rdev->slist, &supply_rdev->supply_list);
+out:
+       return err;
+}
+
+/**
+ * set_consumer_device_supply: Bind a regulator to a symbolic supply
+ * @regulator: regulator source
+ * @dev:       device the supply applies to
+ * @supply:    symbolic name for supply
+ *
+ * Allows platform initialisation code to map physical regulator
+ * sources to symbolic names for supplies for use by devices.  Devices
+ * should use these symbolic names to request regulators, avoiding the
+ * need to provide board-specific regulator names as platform data.
+ */
+static int set_consumer_device_supply(struct regulator_dev *rdev,
+       struct device *consumer_dev, const char *supply)
+{
+       struct regulator_map *node;
+
+       if (supply == NULL)
+               return -EINVAL;
+
+       node = kmalloc(sizeof(struct regulator_map), GFP_KERNEL);
+       if (node == NULL)
+               return -ENOMEM;
+
+       node->regulator = rdev;
+       node->dev = consumer_dev;
+       node->supply = supply;
+
+       list_add(&node->list, &regulator_map_list);
+       return 0;
+}
+
+static void unset_consumer_device_supply(struct regulator_dev *rdev,
+       struct device *consumer_dev)
+{
+       struct regulator_map *node, *n;
+
+       list_for_each_entry_safe(node, n, &regulator_map_list, list) {
+               if (rdev == node->regulator &&
+                       consumer_dev == node->dev) {
+                       list_del(&node->list);
+                       kfree(node);
+                       return;
+               }
+       }
+}
+
 #define REG_STR_SIZE   32
 
 static struct regulator *create_regulator(struct regulator_dev *rdev,
@@ -746,7 +912,6 @@ struct regulator *regulator_get(struct device *dev, const char *id)
        struct regulator_dev *rdev;
        struct regulator_map *map;
        struct regulator *regulator = ERR_PTR(-ENODEV);
-       const char *supply = id;
 
        if (id == NULL) {
                printk(KERN_ERR "regulator: get() with no identifier\n");
@@ -758,15 +923,9 @@ struct regulator *regulator_get(struct device *dev, const char *id)
        list_for_each_entry(map, &regulator_map_list, list) {
                if (dev == map->dev &&
                    strcmp(map->supply, id) == 0) {
-                       supply = map->regulator;
-                       break;
-               }
-       }
-
-       list_for_each_entry(rdev, &regulator_list, list) {
-               if (strcmp(supply, rdev->desc->name) == 0 &&
-                   try_module_get(rdev->owner))
+                       rdev = map->regulator;
                        goto found;
+               }
        }
        printk(KERN_ERR "regulator: Unable to get requested regulator: %s\n",
               id);
@@ -774,12 +933,16 @@ struct regulator *regulator_get(struct device *dev, const char *id)
        return regulator;
 
 found:
+       if (!try_module_get(rdev->owner))
+               goto out;
+
        regulator = create_regulator(rdev, dev, id);
        if (regulator == NULL) {
                regulator = ERR_PTR(-ENOMEM);
                module_put(rdev->owner);
        }
 
+out:
        mutex_unlock(&regulator_list_mutex);
        return regulator;
 }
@@ -1559,11 +1722,12 @@ EXPORT_SYMBOL_GPL(regulator_notifier_call_chain);
  * Returns 0 on success.
  */
 struct regulator_dev *regulator_register(struct regulator_desc *regulator_desc,
-                                         void *reg_data)
+       struct device *dev, void *driver_data)
 {
        static atomic_t regulator_no = ATOMIC_INIT(0);
        struct regulator_dev *rdev;
-       int ret;
+       struct regulator_init_data *init_data = dev->platform_data;
+       int ret, i;
 
        if (regulator_desc == NULL)
                return ERR_PTR(-EINVAL);
@@ -1575,6 +1739,9 @@ struct regulator_dev *regulator_register(struct regulator_desc *regulator_desc,
            !regulator_desc->type == REGULATOR_CURRENT)
                return ERR_PTR(-EINVAL);
 
+       if (!init_data)
+               return ERR_PTR(-EINVAL);
+
        rdev = kzalloc(sizeof(struct regulator_dev), GFP_KERNEL);
        if (rdev == NULL)
                return ERR_PTR(-ENOMEM);
@@ -1582,7 +1749,7 @@ struct regulator_dev *regulator_register(struct regulator_desc *regulator_desc,
        mutex_lock(&regulator_list_mutex);
 
        mutex_init(&rdev->mutex);
-       rdev->reg_data = reg_data;
+       rdev->reg_data = driver_data;
        rdev->owner = regulator_desc->owner;
        rdev->desc = regulator_desc;
        INIT_LIST_HEAD(&rdev->consumer_list);
@@ -1591,20 +1758,68 @@ struct regulator_dev *regulator_register(struct regulator_desc *regulator_desc,
        INIT_LIST_HEAD(&rdev->slist);
        BLOCKING_INIT_NOTIFIER_HEAD(&rdev->notifier);
 
+       /* preform any regulator specific init */
+       if (init_data->regulator_init) {
+               ret = init_data->regulator_init(rdev->reg_data);
+               if (ret < 0) {
+                       kfree(rdev);
+                       rdev = ERR_PTR(ret);
+                       goto out;
+               }
+       }
+
+       /* set regulator constraints */
+       ret = set_machine_constraints(rdev, &init_data->constraints);
+       if (ret < 0) {
+               kfree(rdev);
+               rdev = ERR_PTR(ret);
+               goto out;
+       }
+
+       /* register with sysfs */
        rdev->dev.class = &regulator_class;
-       device_initialize(&rdev->dev);
+       rdev->dev.parent = dev;
        snprintf(rdev->dev.bus_id, sizeof(rdev->dev.bus_id),
-                "regulator_%ld_%s",
-                (unsigned long)atomic_inc_return(&regulator_no) - 1,
-                regulator_desc->name);
-
-       ret = device_add(&rdev->dev);
-       if (ret == 0)
-               list_add(&rdev->list, &regulator_list);
-       else {
+                "regulator.%d", atomic_inc_return(&regulator_no) - 1);
+       ret = device_register(&rdev->dev);
+       if (ret != 0) {
                kfree(rdev);
                rdev = ERR_PTR(ret);
+               goto out;
+       }
+
+       dev_set_drvdata(&rdev->dev, rdev);
+
+       /* set supply regulator if it exists */
+       if (init_data->supply_regulator_dev) {
+               ret = set_supply(rdev,
+                       dev_get_drvdata(init_data->supply_regulator_dev));
+               if (ret < 0) {
+                       device_unregister(&rdev->dev);
+                       kfree(rdev);
+                       rdev = ERR_PTR(ret);
+                       goto out;
+               }
        }
+
+       /* add consumers devices */
+       for (i = 0; i < init_data->num_consumer_supplies; i++) {
+               ret = set_consumer_device_supply(rdev,
+                       init_data->consumer_supplies[i].dev,
+                       init_data->consumer_supplies[i].supply);
+               if (ret < 0) {
+                       for (--i; i >= 0; i--)
+                               unset_consumer_device_supply(rdev,
+                                       init_data->consumer_supplies[i].dev);
+                       device_unregister(&rdev->dev);
+                       kfree(rdev);
+                       rdev = ERR_PTR(ret);
+                       goto out;
+               }
+       }
+
+       list_add(&rdev->list, &regulator_list);
+out:
        mutex_unlock(&regulator_list_mutex);
        return rdev;
 }
@@ -1630,187 +1845,6 @@ void regulator_unregister(struct regulator_dev *rdev)
 }
 EXPORT_SYMBOL_GPL(regulator_unregister);
 
-/**
- * regulator_set_supply - set regulator supply regulator
- * @regulator: regulator name
- * @supply: supply regulator name
- *
- * Called by platform initialisation code to set the supply regulator for this
- * regulator. This ensures that a regulators supply will also be enabled by the
- * core if it's child is enabled.
- */
-int regulator_set_supply(const char *regulator, const char *supply)
-{
-       struct regulator_dev *rdev, *supply_rdev;
-       int err;
-
-       if (regulator == NULL || supply == NULL)
-               return -EINVAL;
-
-       mutex_lock(&regulator_list_mutex);
-
-       list_for_each_entry(rdev, &regulator_list, list) {
-               if (!strcmp(rdev->desc->name, regulator))
-                       goto found_regulator;
-       }
-       mutex_unlock(&regulator_list_mutex);
-       return -ENODEV;
-
-found_regulator:
-       list_for_each_entry(supply_rdev, &regulator_list, list) {
-               if (!strcmp(supply_rdev->desc->name, supply))
-                       goto found_supply;
-       }
-       mutex_unlock(&regulator_list_mutex);
-       return -ENODEV;
-
-found_supply:
-       err = sysfs_create_link(&rdev->dev.kobj, &supply_rdev->dev.kobj,
-                               "supply");
-       if (err) {
-               printk(KERN_ERR
-                      "%s: could not add device link %s err %d\n",
-                      __func__, supply_rdev->dev.kobj.name, err);
-                      goto out;
-       }
-       rdev->supply = supply_rdev;
-       list_add(&rdev->slist, &supply_rdev->supply_list);
-out:
-       mutex_unlock(&regulator_list_mutex);
-       return err;
-}
-EXPORT_SYMBOL_GPL(regulator_set_supply);
-
-/**
- * regulator_get_supply - get regulator supply regulator
- * @regulator: regulator name
- *
- * Returns the supply supply regulator name or NULL if no supply regulator
- * exists (i.e the regulator is supplied directly from USB, Line, Battery, etc)
- */
-const char *regulator_get_supply(const char *regulator)
-{
-       struct regulator_dev *rdev;
-
-       if (regulator == NULL)
-               return NULL;
-
-       mutex_lock(&regulator_list_mutex);
-       list_for_each_entry(rdev, &regulator_list, list) {
-               if (!strcmp(rdev->desc->name, regulator))
-                       goto found;
-       }
-       mutex_unlock(&regulator_list_mutex);
-       return NULL;
-
-found:
-       mutex_unlock(&regulator_list_mutex);
-       if (rdev->supply)
-               return rdev->supply->desc->name;
-       else
-               return NULL;
-}
-EXPORT_SYMBOL_GPL(regulator_get_supply);
-
-/**
- * regulator_set_machine_constraints - sets regulator constraints
- * @regulator: regulator source
- *
- * Allows platform initialisation code to define and constrain
- * regulator circuits e.g. valid voltage/current ranges, etc.  NOTE:
- * Constraints *must* be set by platform code in order for some
- * regulator operations to proceed i.e. set_voltage, set_current_limit,
- * set_mode.
- */
-int regulator_set_machine_constraints(const char *regulator_name,
-       struct regulation_constraints *constraints)
-{
-       struct regulator_dev *rdev;
-       int ret = 0;
-
-       if (regulator_name == NULL)
-               return -EINVAL;
-
-       mutex_lock(&regulator_list_mutex);
-
-       list_for_each_entry(rdev, &regulator_list, list) {
-               if (!strcmp(regulator_name, rdev->desc->name))
-                       goto found;
-       }
-       ret = -ENODEV;
-       goto out;
-
-found:
-       mutex_lock(&rdev->mutex);
-       rdev->constraints = constraints;
-
-       /* do we need to apply the constraint voltage */
-       if (rdev->constraints->apply_uV &&
-               rdev->constraints->min_uV == rdev->constraints->max_uV &&
-               rdev->desc->ops->set_voltage) {
-               ret = rdev->desc->ops->set_voltage(rdev,
-                       rdev->constraints->min_uV, rdev->constraints->max_uV);
-                       if (ret < 0) {
-                               printk(KERN_ERR "%s: failed to apply %duV"
-                                       " constraint\n", __func__,
-                                       rdev->constraints->min_uV);
-                               rdev->constraints = NULL;
-                               goto out;
-                       }
-       }
-
-       /* are we enabled at boot time by firmware / bootloader */
-       if (rdev->constraints->boot_on)
-               rdev->use_count = 1;
-
-       /* do we need to setup our suspend state */
-       if (constraints->initial_state)
-               ret = suspend_prepare(rdev, constraints->initial_state);
-
-       print_constraints(rdev);
-       mutex_unlock(&rdev->mutex);
-
-out:
-       mutex_unlock(&regulator_list_mutex);
-       return ret;
-}
-EXPORT_SYMBOL_GPL(regulator_set_machine_constraints);
-
-
-/**
- * regulator_set_device_supply: Bind a regulator to a symbolic supply
- * @regulator: regulator source
- * @dev:       device the supply applies to
- * @supply:    symbolic name for supply
- *
- * Allows platform initialisation code to map physical regulator
- * sources to symbolic names for supplies for use by devices.  Devices
- * should use these symbolic names to request regulators, avoiding the
- * need to provide board-specific regulator names as platform data.
- */
-int regulator_set_device_supply(const char *regulator, struct device *dev,
-                               const char *supply)
-{
-       struct regulator_map *node;
-
-       if (regulator == NULL || supply == NULL)
-               return -EINVAL;
-
-       node = kmalloc(sizeof(struct regulator_map), GFP_KERNEL);
-       if (node == NULL)
-               return -ENOMEM;
-
-       node->regulator = regulator;
-       node->dev = dev;
-       node->supply = supply;
-
-       mutex_lock(&regulator_list_mutex);
-       list_add(&node->list, &regulator_map_list);
-       mutex_unlock(&regulator_list_mutex);
-       return 0;
-}
-EXPORT_SYMBOL_GPL(regulator_set_device_supply);
-
 /**
  * regulator_suspend_prepare: prepare regulators for system wide suspend
  * @state: system suspend state
@@ -1893,6 +1927,18 @@ int rdev_get_id(struct regulator_dev *rdev)
 }
 EXPORT_SYMBOL_GPL(rdev_get_id);
 
+struct device *rdev_get_dev(struct regulator_dev *rdev)
+{
+       return &rdev->dev;
+}
+EXPORT_SYMBOL_GPL(rdev_get_dev);
+
+void *regulator_get_init_drvdata(struct regulator_init_data *reg_init_data)
+{
+       return reg_init_data->driver_data;
+}
+EXPORT_SYMBOL_GPL(regulator_get_init_drvdata);
+
 static int __init regulator_init(void)
 {
        printk(KERN_INFO "regulator: core version %s\n", REGULATOR_VERSION);
diff --git a/drivers/regulator/da903x.c b/drivers/regulator/da903x.c
new file mode 100644 (file)
index 0000000..3688e33
--- /dev/null
@@ -0,0 +1,513 @@
+/*
+ * Regulators driver for Dialog Semiconductor DA903x
+ *
+ * Copyright (C) 2006-2008 Marvell International Ltd.
+ * Copyright (C) 2008 Compulab Ltd.
+ *
+ * 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.
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/mfd/da903x.h>
+
+/* DA9030 Registers */
+#define DA9030_INVAL           (-1)
+#define DA9030_LDO1011         (0x10)
+#define DA9030_LDO15           (0x11)
+#define DA9030_LDO1416         (0x12)
+#define DA9030_LDO1819         (0x13)
+#define DA9030_LDO17           (0x14)
+#define DA9030_BUCK2DVM1       (0x15)
+#define DA9030_BUCK2DVM2       (0x16)
+#define DA9030_RCTL11          (0x17)
+#define DA9030_RCTL21          (0x18)
+#define DA9030_LDO1            (0x90)
+#define DA9030_LDO23           (0x91)
+#define DA9030_LDO45           (0x92)
+#define DA9030_LDO6            (0x93)
+#define DA9030_LDO78           (0x94)
+#define DA9030_LDO912          (0x95)
+#define DA9030_BUCK            (0x96)
+#define DA9030_RCTL12          (0x97)
+#define DA9030_RCTL22          (0x98)
+#define DA9030_LDO_UNLOCK      (0xa0)
+#define DA9030_LDO_UNLOCK_MASK (0xe0)
+#define DA9034_OVER1           (0x10)
+
+/* DA9034 Registers */
+#define DA9034_INVAL           (-1)
+#define DA9034_OVER2           (0x11)
+#define DA9034_OVER3           (0x12)
+#define DA9034_LDO643          (0x13)
+#define DA9034_LDO987          (0x14)
+#define DA9034_LDO1110         (0x15)
+#define DA9034_LDO1312         (0x16)
+#define DA9034_LDO1514         (0x17)
+#define DA9034_VCC1            (0x20)
+#define DA9034_ADTV1           (0x23)
+#define DA9034_ADTV2           (0x24)
+#define DA9034_AVRC            (0x25)
+#define DA9034_CDTV1           (0x26)
+#define DA9034_CDTV2           (0x27)
+#define DA9034_CVRC            (0x28)
+#define DA9034_SDTV1           (0x29)
+#define DA9034_SDTV2           (0x2a)
+#define DA9034_SVRC            (0x2b)
+#define DA9034_MDTV1           (0x32)
+#define DA9034_MDTV2           (0x33)
+#define DA9034_MVRC            (0x34)
+
+struct da903x_regulator_info {
+       struct regulator_desc desc;
+
+       int     min_uV;
+       int     max_uV;
+       int     step_uV;
+       int     vol_reg;
+       int     vol_shift;
+       int     vol_nbits;
+       int     update_reg;
+       int     update_bit;
+       int     enable_reg;
+       int     enable_bit;
+};
+
+static inline int check_range(struct da903x_regulator_info *info,
+                               int min_uV, int max_uV)
+{
+       if (min_uV < info->min_uV || min_uV > info->max_uV)
+               return -EINVAL;
+
+       return 0;
+}
+
+/* DA9030/DA9034 common operations */
+static int da903x_set_ldo_voltage(struct regulator_dev *rdev,
+                                 int min_uV, int max_uV)
+{
+       struct da903x_regulator_info *info = rdev_get_drvdata(rdev);
+       struct device *da9034_dev = rdev_get_dev(rdev)->parent;
+       uint8_t val, mask;
+
+       if (check_range(info, min_uV, max_uV)) {
+               pr_err("invalid voltage range (%d, %d) uV", min_uV, max_uV);
+               return -EINVAL;
+       }
+
+       val = (min_uV - info->min_uV + info->step_uV - 1) / info->step_uV;
+       val <<= info->vol_shift;
+       mask = ((1 << info->vol_nbits) - 1)  << info->vol_shift;
+
+       return da903x_update(da9034_dev, info->vol_reg, val, mask);
+}
+
+static int da903x_get_voltage(struct regulator_dev *rdev)
+{
+       struct da903x_regulator_info *info = rdev_get_drvdata(rdev);
+       struct device *da9034_dev = rdev_get_dev(rdev)->parent;
+       uint8_t val, mask;
+       int ret;
+
+       ret = da903x_read(da9034_dev, info->vol_reg, &val);
+       if (ret)
+               return ret;
+
+       mask = ((1 << info->vol_nbits) - 1)  << info->vol_shift;
+       val = (val & mask) >> info->vol_shift;
+
+       return info->min_uV + info->step_uV * val;
+}
+
+static int da903x_enable(struct regulator_dev *rdev)
+{
+       struct da903x_regulator_info *info = rdev_get_drvdata(rdev);
+       struct device *da9034_dev = rdev_get_dev(rdev)->parent;
+
+       return da903x_set_bits(da9034_dev, info->enable_reg,
+                                       1 << info->enable_bit);
+}
+
+static int da903x_disable(struct regulator_dev *rdev)
+{
+       struct da903x_regulator_info *info = rdev_get_drvdata(rdev);
+       struct device *da9034_dev = rdev_get_dev(rdev)->parent;
+
+       return da903x_clr_bits(da9034_dev, info->enable_reg,
+                                       1 << info->enable_bit);
+}
+
+static int da903x_is_enabled(struct regulator_dev *rdev)
+{
+       struct da903x_regulator_info *info = rdev_get_drvdata(rdev);
+       struct device *da9034_dev = rdev_get_dev(rdev)->parent;
+       uint8_t reg_val;
+       int ret;
+
+       ret = da903x_read(da9034_dev, info->enable_reg, &reg_val);
+       if (ret)
+               return ret;
+
+       return reg_val & (1 << info->enable_bit);
+}
+
+/* DA9030 specific operations */
+static int da9030_set_ldo1_15_voltage(struct regulator_dev *rdev,
+                                      int min_uV, int max_uV)
+{
+       struct da903x_regulator_info *info = rdev_get_drvdata(rdev);
+       struct device *da903x_dev = rdev_get_dev(rdev)->parent;
+       uint8_t val, mask;
+       int ret;
+
+       if (check_range(info, min_uV, max_uV)) {
+               pr_err("invalid voltage range (%d, %d) uV", min_uV, max_uV);
+               return -EINVAL;
+       }
+
+       val = (min_uV - info->min_uV + info->step_uV - 1) / info->step_uV;
+       val <<= info->vol_shift;
+       mask = ((1 << info->vol_nbits) - 1)  << info->vol_shift;
+       val |= DA9030_LDO_UNLOCK; /* have to set UNLOCK bits */
+       mask |= DA9030_LDO_UNLOCK_MASK;
+
+       /* write twice */
+       ret = da903x_update(da903x_dev, info->vol_reg, val, mask);
+       if (ret)
+               return ret;
+
+       return da903x_update(da903x_dev, info->vol_reg, val, mask);
+}
+
+static int da9030_set_ldo14_voltage(struct regulator_dev *rdev,
+                                 int min_uV, int max_uV)
+{
+       struct da903x_regulator_info *info = rdev_get_drvdata(rdev);
+       struct device *da903x_dev = rdev_get_dev(rdev)->parent;
+       uint8_t val, mask;
+       int thresh;
+
+       if (check_range(info, min_uV, max_uV)) {
+               pr_err("invalid voltage range (%d, %d) uV", min_uV, max_uV);
+               return -EINVAL;
+       }
+
+       thresh = (info->max_uV + info->min_uV) / 2;
+       if (min_uV < thresh) {
+               val = (thresh - min_uV + info->step_uV - 1) / info->step_uV;
+               val |= 0x4;
+       } else {
+               val = (min_uV - thresh + info->step_uV - 1) / info->step_uV;
+       }
+
+       val <<= info->vol_shift;
+       mask = ((1 << info->vol_nbits) - 1)  << info->vol_shift;
+
+       return da903x_update(da903x_dev, info->vol_reg, val, mask);
+}
+
+static int da9030_get_ldo14_voltage(struct regulator_dev *rdev)
+{
+       struct da903x_regulator_info *info = rdev_get_drvdata(rdev);
+       struct device *da903x_dev = rdev_get_dev(rdev)->parent;
+       uint8_t val, mask;
+       int ret;
+
+       ret = da903x_read(da903x_dev, info->vol_reg, &val);
+       if (ret)
+               return ret;
+
+       mask = ((1 << info->vol_nbits) - 1)  << info->vol_shift;
+       val = (val & mask) >> info->vol_shift;
+
+       if (val & 0x4)
+               return info->min_uV + info->step_uV * (3 - (val & ~0x4));
+       else
+               return (info->max_uV + info->min_uV) / 2 +
+                       info->step_uV * (val & ~0x4);
+}
+
+/* DA9034 specific operations */
+static int da9034_set_dvc_voltage(struct regulator_dev *rdev,
+                                 int min_uV, int max_uV)
+{
+       struct da903x_regulator_info *info = rdev_get_drvdata(rdev);
+       struct device *da9034_dev = rdev_get_dev(rdev)->parent;
+       uint8_t val, mask;
+       int ret;
+
+       if (check_range(info, min_uV, max_uV)) {
+               pr_err("invalid voltage range (%d, %d) uV", min_uV, max_uV);
+               return -EINVAL;
+       }
+
+       val = (min_uV - info->min_uV + info->step_uV - 1) / info->step_uV;
+       val <<= info->vol_shift;
+       mask = ((1 << info->vol_nbits) - 1)  << info->vol_shift;
+
+       ret = da903x_update(da9034_dev, info->vol_reg, val, mask);
+       if (ret)
+               return ret;
+
+       ret = da903x_set_bits(da9034_dev, info->update_reg,
+                                       1 << info->update_bit);
+       return ret;
+}
+
+static int da9034_set_ldo12_voltage(struct regulator_dev *rdev,
+                                   int min_uV, int max_uV)
+{
+       struct da903x_regulator_info *info = rdev_get_drvdata(rdev);
+       struct device *da9034_dev = rdev_get_dev(rdev)->parent;
+       uint8_t val, mask;
+
+       if (check_range(info, min_uV, max_uV)) {
+               pr_err("invalid voltage range (%d, %d) uV", min_uV, max_uV);
+               return -EINVAL;
+       }
+
+       val = (min_uV - info->min_uV + info->step_uV - 1) / info->step_uV;
+       val = (val > 7 || val < 20) ? 8 : val - 12;
+       val <<= info->vol_shift;
+       mask = ((1 << info->vol_nbits) - 1)  << info->vol_shift;
+
+       return da903x_update(da9034_dev, info->vol_reg, val, mask);
+}
+
+static int da9034_get_ldo12_voltage(struct regulator_dev *rdev)
+{
+       struct da903x_regulator_info *info = rdev_get_drvdata(rdev);
+       struct device *da9034_dev = rdev_get_dev(rdev)->parent;
+       uint8_t val, mask;
+       int ret;
+
+       ret = da903x_read(da9034_dev, info->vol_reg, &val);
+       if (ret)
+               return ret;
+
+       mask = ((1 << info->vol_nbits) - 1)  << info->vol_shift;
+       val = (val & mask) >> info->vol_shift;
+
+       if (val >= 8)
+               return 2700000 + info->step_uV * (val - 8);
+
+       return info->min_uV + info->step_uV * val;
+}
+
+static struct regulator_ops da903x_regulator_ldo_ops = {
+       .set_voltage    = da903x_set_ldo_voltage,
+       .get_voltage    = da903x_get_voltage,
+       .enable         = da903x_enable,
+       .disable        = da903x_disable,
+       .is_enabled     = da903x_is_enabled,
+};
+
+/* NOTE: this is dedicated for the insane DA9030 LDO14 */
+static struct regulator_ops da9030_regulator_ldo14_ops = {
+       .set_voltage    = da9030_set_ldo14_voltage,
+       .get_voltage    = da9030_get_ldo14_voltage,
+       .enable         = da903x_enable,
+       .disable        = da903x_disable,
+       .is_enabled     = da903x_is_enabled,
+};
+
+/* NOTE: this is dedicated for the DA9030 LDO1 and LDO15 that have locks  */
+static struct regulator_ops da9030_regulator_ldo1_15_ops = {
+       .set_voltage    = da9030_set_ldo1_15_voltage,
+       .get_voltage    = da903x_get_voltage,
+       .enable         = da903x_enable,
+       .disable        = da903x_disable,
+       .is_enabled     = da903x_is_enabled,
+};
+
+static struct regulator_ops da9034_regulator_dvc_ops = {
+       .set_voltage    = da9034_set_dvc_voltage,
+       .get_voltage    = da903x_get_voltage,
+       .enable         = da903x_enable,
+       .disable        = da903x_disable,
+       .is_enabled     = da903x_is_enabled,
+};
+
+/* NOTE: this is dedicated for the insane LDO12 */
+static struct regulator_ops da9034_regulator_ldo12_ops = {
+       .set_voltage    = da9034_set_ldo12_voltage,
+       .get_voltage    = da9034_get_ldo12_voltage,
+       .enable         = da903x_enable,
+       .disable        = da903x_disable,
+       .is_enabled     = da903x_is_enabled,
+};
+
+#define DA903x_LDO(_pmic, _id, min, max, step, vreg, shift, nbits, ereg, ebit) \
+{                                                                      \
+       .desc   = {                                                     \
+               .name   = "LDO" #_id,                                   \
+               .ops    = &da903x_regulator_ldo_ops,                    \
+               .type   = REGULATOR_VOLTAGE,                            \
+               .id     = _pmic##_ID_LDO##_id,                          \
+               .owner  = THIS_MODULE,                                  \
+       },                                                              \
+       .min_uV         = (min) * 1000,                                 \
+       .max_uV         = (max) * 1000,                                 \
+       .step_uV        = (step) * 1000,                                \
+       .vol_reg        = _pmic##_##vreg,                               \
+       .vol_shift      = (shift),                                      \
+       .vol_nbits      = (nbits),                                      \
+       .enable_reg     = _pmic##_##ereg,                               \
+       .enable_bit     = (ebit),                                       \
+}
+
+#define DA9034_DVC(_id, min, max, step, vreg, nbits, ureg, ubit, ereg, ebit) \
+{                                                                      \
+       .desc   = {                                                     \
+               .name   = #_id,                                         \
+               .ops    = &da9034_regulator_dvc_ops,                    \
+               .type   = REGULATOR_VOLTAGE,                            \
+               .id     = DA9034_ID_##_id,                              \
+               .owner  = THIS_MODULE,                                  \
+       },                                                              \
+       .min_uV         = (min) * 1000,                                 \
+       .max_uV         = (max) * 1000,                                 \
+       .step_uV        = (step) * 1000,                                \
+       .vol_reg        = DA9034_##vreg,                                \
+       .vol_shift      = (0),                                          \
+       .vol_nbits      = (nbits),                                      \
+       .update_reg     = DA9034_##ureg,                                \
+       .update_bit     = (ubit),                                       \
+       .enable_reg     = DA9034_##ereg,                                \
+       .enable_bit     = (ebit),                                       \
+}
+
+#define DA9034_LDO(_id, min, max, step, vreg, shift, nbits, ereg, ebit)        \
+       DA903x_LDO(DA9034, _id, min, max, step, vreg, shift, nbits, ereg, ebit)
+
+#define DA9030_LDO(_id, min, max, step, vreg, shift, nbits, ereg, ebit)        \
+       DA903x_LDO(DA9030, _id, min, max, step, vreg, shift, nbits, ereg, ebit)
+
+static struct da903x_regulator_info da903x_regulator_info[] = {
+       /* DA9030 */
+       DA9030_LDO( 1, 1200, 3200, 100,    LDO1, 0, 5, RCTL12, 1),
+       DA9030_LDO( 2, 1800, 3200, 100,   LDO23, 0, 4, RCTL12, 2),
+       DA9030_LDO( 3, 1800, 3200, 100,   LDO23, 4, 4, RCTL12, 3),
+       DA9030_LDO( 4, 1800, 3200, 100,   LDO45, 0, 4, RCTL12, 4),
+       DA9030_LDO( 5, 1800, 3200, 100,   LDO45, 4, 4, RCTL12, 5),
+       DA9030_LDO( 6, 1800, 3200, 100,    LDO6, 0, 4, RCTL12, 6),
+       DA9030_LDO( 7, 1800, 3200, 100,   LDO78, 0, 4, RCTL12, 7),
+       DA9030_LDO( 8, 1800, 3200, 100,   LDO78, 4, 4, RCTL22, 0),
+       DA9030_LDO( 9, 1800, 3200, 100,  LDO912, 0, 4, RCTL22, 1),
+       DA9030_LDO(10, 1800, 3200, 100, LDO1011, 0, 4, RCTL22, 2),
+       DA9030_LDO(11, 1800, 3200, 100, LDO1011, 4, 4, RCTL22, 3),
+       DA9030_LDO(12, 1800, 3200, 100,  LDO912, 4, 4, RCTL22, 4),
+       DA9030_LDO(14, 2760, 2940,  30, LDO1416, 0, 3, RCTL11, 4),
+       DA9030_LDO(15, 1100, 2650,  50,   LDO15, 0, 5, RCTL11, 5),
+       DA9030_LDO(16, 1100, 2650,  50, LDO1416, 3, 5, RCTL11, 6),
+       DA9030_LDO(17, 1800, 3200, 100,   LDO17, 0, 4, RCTL11, 7),
+       DA9030_LDO(18, 1800, 3200, 100, LDO1819, 0, 4, RCTL21, 2),
+       DA9030_LDO(19, 1800, 3200, 100, LDO1819, 4, 4, RCTL21, 1),
+       DA9030_LDO(13, 2100, 2100, 0, INVAL, 0, 0, RCTL11, 3), /* fixed @2.1V */
+
+       /* DA9034 */
+       DA9034_DVC(BUCK1, 725, 1500, 25, ADTV1, 5, VCC1, 0, OVER1, 0),
+       DA9034_DVC(BUCK2, 725, 1500, 25, CDTV1, 5, VCC1, 2, OVER1, 1),
+       DA9034_DVC(LDO2,  725, 1500, 25, SDTV1, 5, VCC1, 4, OVER1, 2),
+       DA9034_DVC(LDO1, 1700, 2075, 25, MDTV1, 4, VCC1, 6, OVER3, 4),
+
+       DA9034_LDO( 3, 1800, 3300, 100,  LDO643, 0, 4, OVER3, 5),
+       DA9034_LDO( 4, 1800, 2900,1100,  LDO643, 4, 1, OVER3, 6),
+       DA9034_LDO( 6, 2500, 2850,  50,  LDO643, 5, 3, OVER2, 0),
+       DA9034_LDO( 7, 2700, 3050,  50,  LDO987, 0, 3, OVER2, 1),
+       DA9034_LDO( 8, 2700, 2850,  50,  LDO987, 3, 2, OVER2, 2),
+       DA9034_LDO( 9, 2700, 3050,  50,  LDO987, 5, 3, OVER2, 3),
+       DA9034_LDO(10, 2700, 3050,  50, LDO1110, 0, 3, OVER2, 4),
+       DA9034_LDO(11, 1800, 3300, 100, LDO1110, 4, 4, OVER2, 5),
+       DA9034_LDO(12, 1700, 3050,  50, LDO1312, 0, 4, OVER3, 6),
+       DA9034_LDO(13, 1800, 3300, 100, LDO1312, 4, 4, OVER2, 7),
+       DA9034_LDO(14, 1800, 3300, 100, LDO1514, 0, 4, OVER3, 0),
+       DA9034_LDO(15, 1800, 3300, 100, LDO1514, 4, 4, OVER3, 1),
+       DA9034_LDO(5, 3100, 3100, 0, INVAL, 0, 0, OVER3, 7), /* fixed @3.1V */
+};
+
+static inline struct da903x_regulator_info *find_regulator_info(int id)
+{
+       struct da903x_regulator_info *ri;
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(da903x_regulator_info); i++) {
+               ri = &da903x_regulator_info[i];
+               if (ri->desc.id == id)
+                       return ri;
+       }
+       return NULL;
+}
+
+static int __devinit da903x_regulator_probe(struct platform_device *pdev)
+{
+       struct da903x_regulator_info *ri = NULL;
+       struct regulator_dev *rdev;
+
+       ri = find_regulator_info(pdev->id);
+       if (ri == NULL) {
+               dev_err(&pdev->dev, "invalid regulator ID specified\n");
+               return -EINVAL;
+       }
+
+       /* Workaround for the weird LDO12 voltage setting */
+       if (ri->desc.id == DA9034_ID_LDO12)
+               ri->desc.ops = &da9034_regulator_ldo12_ops;
+
+       if (ri->desc.id == DA9030_ID_LDO14)
+               ri->desc.ops = &da9030_regulator_ldo14_ops;
+
+       if (ri->desc.id == DA9030_ID_LDO1 || ri->desc.id == DA9030_ID_LDO15)
+               ri->desc.ops = &da9030_regulator_ldo1_15_ops;
+
+       rdev = regulator_register(&ri->desc, pdev->dev.parent, ri);
+       if (IS_ERR(rdev)) {
+               dev_err(&pdev->dev, "failed to register regulator %s\n",
+                               ri->desc.name);
+               return PTR_ERR(rdev);
+       }
+
+       platform_set_drvdata(pdev, rdev);
+       return 0;
+}
+
+static int __devexit da903x_regulator_remove(struct platform_device *pdev)
+{
+       struct regulator_dev *rdev = platform_get_drvdata(pdev);
+
+       regulator_unregister(rdev);
+       return 0;
+}
+
+static struct platform_driver da903x_regulator_driver = {
+       .driver = {
+               .name   = "da903x-regulator",
+               .owner  = THIS_MODULE,
+       },
+       .probe          = da903x_regulator_probe,
+       .remove         = da903x_regulator_remove,
+};
+
+static int __init da903x_regulator_init(void)
+{
+       return platform_driver_register(&da903x_regulator_driver);
+}
+module_init(da903x_regulator_init);
+
+static void __exit da903x_regulator_exit(void)
+{
+       platform_driver_unregister(&da903x_regulator_driver);
+}
+module_exit(da903x_regulator_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Eric Miao <eric.miao@marvell.com>"
+             "Mike Rapoport <mike@compulab.co.il>");
+MODULE_DESCRIPTION("Regulator Driver for Dialog Semiconductor DA903X PMIC");
+MODULE_ALIAS("platform:da903x-regulator");
diff --git a/drivers/regulator/wm8350-regulator.c b/drivers/regulator/wm8350-regulator.c
new file mode 100644 (file)
index 0000000..1f44b17
--- /dev/null
@@ -0,0 +1,1431 @@
+/*
+ * wm8350.c  --  Voltage and current regulation for the Wolfson WM8350 PMIC
+ *
+ * Copyright 2007, 2008 Wolfson Microelectronics PLC.
+ *
+ * Author: Liam Girdwood
+ *         linux@wolfsonmicro.com
+ *
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/bitops.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/mfd/wm8350/core.h>
+#include <linux/mfd/wm8350/pmic.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+
+/* Microamps */
+static const int isink_cur[] = {
+       4,
+       5,
+       6,
+       7,
+       8,
+       10,
+       11,
+       14,
+       16,
+       19,
+       23,
+       27,
+       32,
+       39,
+       46,
+       54,
+       65,
+       77,
+       92,
+       109,
+       130,
+       154,
+       183,
+       218,
+       259,
+       308,
+       367,
+       436,
+       518,
+       616,
+       733,
+       872,
+       1037,
+       1233,
+       1466,
+       1744,
+       2073,
+       2466,
+       2933,
+       3487,
+       4147,
+       4932,
+       5865,
+       6975,
+       8294,
+       9864,
+       11730,
+       13949,
+       16589,
+       19728,
+       23460,
+       27899,
+       33178,
+       39455,
+       46920,
+       55798,
+       66355,
+       78910,
+       93840,
+       111596,
+       132710,
+       157820,
+       187681,
+       223191
+};
+
+static int get_isink_val(int min_uA, int max_uA, u16 *setting)
+{
+       int i;
+
+       for (i = ARRAY_SIZE(isink_cur) - 1; i >= 0; i--) {
+               if (min_uA <= isink_cur[i] && max_uA >= isink_cur[i]) {
+                       *setting = i;
+                       return 0;
+               }
+       }
+       return -EINVAL;
+}
+
+static inline int wm8350_ldo_val_to_mvolts(unsigned int val)
+{
+       if (val < 16)
+               return (val * 50) + 900;
+       else
+               return ((val - 16) * 100) + 1800;
+
+}
+
+static inline unsigned int wm8350_ldo_mvolts_to_val(int mV)
+{
+       if (mV < 1800)
+               return (mV - 900) / 50;
+       else
+               return ((mV - 1800) / 100) + 16;
+}
+
+static inline int wm8350_dcdc_val_to_mvolts(unsigned int val)
+{
+       return (val * 25) + 850;
+}
+
+static inline unsigned int wm8350_dcdc_mvolts_to_val(int mV)
+{
+       return (mV - 850) / 25;
+}
+
+static int wm8350_isink_set_current(struct regulator_dev *rdev, int min_uA,
+       int max_uA)
+{
+       struct wm8350 *wm8350 = rdev_get_drvdata(rdev);
+       int isink = rdev_get_id(rdev);
+       u16 val, setting;
+       int ret;
+
+       ret = get_isink_val(min_uA, max_uA, &setting);
+       if (ret != 0)
+               return ret;
+
+       switch (isink) {
+       case WM8350_ISINK_A:
+               val = wm8350_reg_read(wm8350, WM8350_CURRENT_SINK_DRIVER_A) &
+                   ~WM8350_CS1_ISEL_MASK;
+               wm8350_reg_write(wm8350, WM8350_CURRENT_SINK_DRIVER_A,
+                                val | setting);
+               break;
+       case WM8350_ISINK_B:
+               val = wm8350_reg_read(wm8350, WM8350_CURRENT_SINK_DRIVER_B) &
+                   ~WM8350_CS1_ISEL_MASK;
+               wm8350_reg_write(wm8350, WM8350_CURRENT_SINK_DRIVER_B,
+                                val | setting);
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int wm8350_isink_get_current(struct regulator_dev *rdev)
+{
+       struct wm8350 *wm8350 = rdev_get_drvdata(rdev);
+       int isink = rdev_get_id(rdev);
+       u16 val;
+
+       switch (isink) {
+       case WM8350_ISINK_A:
+               val = wm8350_reg_read(wm8350, WM8350_CURRENT_SINK_DRIVER_A) &
+                   WM8350_CS1_ISEL_MASK;
+               break;
+       case WM8350_ISINK_B:
+               val = wm8350_reg_read(wm8350, WM8350_CURRENT_SINK_DRIVER_B) &
+                   WM8350_CS1_ISEL_MASK;
+               break;
+       default:
+               return 0;
+       }
+
+       return (isink_cur[val] + 50) / 100;
+}
+
+/* turn on ISINK followed by DCDC */
+static int wm8350_isink_enable(struct regulator_dev *rdev)
+{
+       struct wm8350 *wm8350 = rdev_get_drvdata(rdev);
+       int isink = rdev_get_id(rdev);
+
+       switch (isink) {
+       case WM8350_ISINK_A:
+               switch (wm8350->pmic.isink_A_dcdc) {
+               case WM8350_DCDC_2:
+               case WM8350_DCDC_5:
+                       wm8350_set_bits(wm8350, WM8350_POWER_MGMT_7,
+                                       WM8350_CS1_ENA);
+                       wm8350_set_bits(wm8350, WM8350_CSA_FLASH_CONTROL,
+                                       WM8350_CS1_DRIVE);
+                       wm8350_set_bits(wm8350, WM8350_DCDC_LDO_REQUESTED,
+                                       1 << (wm8350->pmic.isink_A_dcdc -
+                                             WM8350_DCDC_1));
+                       break;
+               default:
+                       return -EINVAL;
+               }
+               break;
+       case WM8350_ISINK_B:
+               switch (wm8350->pmic.isink_B_dcdc) {
+               case WM8350_DCDC_2:
+               case WM8350_DCDC_5:
+                       wm8350_set_bits(wm8350, WM8350_POWER_MGMT_7,
+                                       WM8350_CS2_ENA);
+                       wm8350_set_bits(wm8350, WM8350_CSB_FLASH_CONTROL,
+                                       WM8350_CS2_DRIVE);
+                       wm8350_set_bits(wm8350, WM8350_DCDC_LDO_REQUESTED,
+                                       1 << (wm8350->pmic.isink_B_dcdc -
+                                             WM8350_DCDC_1));
+                       break;
+               default:
+                       return -EINVAL;
+               }
+               break;
+       default:
+               return -EINVAL;
+       }
+       return 0;
+}
+
+static int wm8350_isink_disable(struct regulator_dev *rdev)
+{
+       struct wm8350 *wm8350 = rdev_get_drvdata(rdev);
+       int isink = rdev_get_id(rdev);
+
+       switch (isink) {
+       case WM8350_ISINK_A:
+               switch (wm8350->pmic.isink_A_dcdc) {
+               case WM8350_DCDC_2:
+               case WM8350_DCDC_5:
+                       wm8350_clear_bits(wm8350, WM8350_DCDC_LDO_REQUESTED,
+                                         1 << (wm8350->pmic.isink_A_dcdc -
+                                               WM8350_DCDC_1));
+                       wm8350_clear_bits(wm8350, WM8350_POWER_MGMT_7,
+                                         WM8350_CS1_ENA);
+                       break;
+               default:
+                       return -EINVAL;
+               }
+               break;
+       case WM8350_ISINK_B:
+               switch (wm8350->pmic.isink_B_dcdc) {
+               case WM8350_DCDC_2:
+               case WM8350_DCDC_5:
+                       wm8350_clear_bits(wm8350, WM8350_DCDC_LDO_REQUESTED,
+                                         1 << (wm8350->pmic.isink_B_dcdc -
+                                               WM8350_DCDC_1));
+                       wm8350_clear_bits(wm8350, WM8350_POWER_MGMT_7,
+                                         WM8350_CS2_ENA);
+                       break;
+               default:
+                       return -EINVAL;
+               }
+               break;
+       default:
+               return -EINVAL;
+       }
+       return 0;
+}
+
+static int wm8350_isink_is_enabled(struct regulator_dev *rdev)
+{
+       struct wm8350 *wm8350 = rdev_get_drvdata(rdev);
+       int isink = rdev_get_id(rdev);
+
+       switch (isink) {
+       case WM8350_ISINK_A:
+               return wm8350_reg_read(wm8350, WM8350_CURRENT_SINK_DRIVER_A) &
+                   0x8000;
+       case WM8350_ISINK_B:
+               return wm8350_reg_read(wm8350, WM8350_CURRENT_SINK_DRIVER_B) &
+                   0x8000;
+       }
+       return -EINVAL;
+}
+
+int wm8350_isink_set_flash(struct wm8350 *wm8350, int isink, u16 mode,
+                          u16 trigger, u16 duration, u16 on_ramp, u16 off_ramp,
+                          u16 drive)
+{
+       switch (isink) {
+       case WM8350_ISINK_A:
+               wm8350_reg_write(wm8350, WM8350_CSA_FLASH_CONTROL,
+                                (mode ? WM8350_CS1_FLASH_MODE : 0) |
+                                (trigger ? WM8350_CS1_TRIGSRC : 0) |
+                                duration | on_ramp | off_ramp | drive);
+               break;
+       case WM8350_ISINK_B:
+               wm8350_reg_write(wm8350, WM8350_CSB_FLASH_CONTROL,
+                                (mode ? WM8350_CS2_FLASH_MODE : 0) |
+                                (trigger ? WM8350_CS2_TRIGSRC : 0) |
+                                duration | on_ramp | off_ramp | drive);
+               break;
+       default:
+               return -EINVAL;
+       }
+       return 0;
+}
+EXPORT_SYMBOL_GPL(wm8350_isink_set_flash);
+
+static int wm8350_dcdc_set_voltage(struct regulator_dev *rdev, int min_uV,
+       int max_uV)
+{
+       struct wm8350 *wm8350 = rdev_get_drvdata(rdev);
+       int volt_reg, dcdc = rdev_get_id(rdev), mV,
+               min_mV = min_uV / 1000, max_mV = max_uV / 1000;
+       u16 val;
+
+       if (min_mV < 850 || min_mV > 4025)
+               return -EINVAL;
+       if (max_mV < 850 || max_mV > 4025)
+               return -EINVAL;
+
+       /* step size is 25mV */
+       mV = (min_mV - 826) / 25;
+       if (wm8350_dcdc_val_to_mvolts(mV) > max_mV)
+               return -EINVAL;
+       BUG_ON(wm8350_dcdc_val_to_mvolts(mV) < min_mV);
+
+       switch (dcdc) {
+       case WM8350_DCDC_1:
+               volt_reg = WM8350_DCDC1_CONTROL;
+               break;
+       case WM8350_DCDC_3:
+               volt_reg = WM8350_DCDC3_CONTROL;
+               break;
+       case WM8350_DCDC_4:
+               volt_reg = WM8350_DCDC4_CONTROL;
+               break;
+       case WM8350_DCDC_6:
+               volt_reg = WM8350_DCDC6_CONTROL;
+               break;
+       case WM8350_DCDC_2:
+       case WM8350_DCDC_5:
+       default:
+               return -EINVAL;
+       }
+
+       /* all DCDCs have same mV bits */
+       val = wm8350_reg_read(wm8350, volt_reg) & ~WM8350_DC1_VSEL_MASK;
+       wm8350_reg_write(wm8350, volt_reg, val | mV);
+       return 0;
+}
+
+static int wm8350_dcdc_get_voltage(struct regulator_dev *rdev)
+{
+       struct wm8350 *wm8350 = rdev_get_drvdata(rdev);
+       int volt_reg, dcdc = rdev_get_id(rdev);
+       u16 val;
+
+       switch (dcdc) {
+       case WM8350_DCDC_1:
+               volt_reg = WM8350_DCDC1_CONTROL;
+               break;
+       case WM8350_DCDC_3:
+               volt_reg = WM8350_DCDC3_CONTROL;
+               break;
+       case WM8350_DCDC_4:
+               volt_reg = WM8350_DCDC4_CONTROL;
+               break;
+       case WM8350_DCDC_6:
+               volt_reg = WM8350_DCDC6_CONTROL;
+               break;
+       case WM8350_DCDC_2:
+       case WM8350_DCDC_5:
+       default:
+               return -EINVAL;
+       }
+
+       /* all DCDCs have same mV bits */
+       val = wm8350_reg_read(wm8350, volt_reg) & WM8350_DC1_VSEL_MASK;
+       return wm8350_dcdc_val_to_mvolts(val) * 1000;
+}
+
+static int wm8350_dcdc_set_suspend_voltage(struct regulator_dev *rdev, int uV)
+{
+       struct wm8350 *wm8350 = rdev_get_drvdata(rdev);
+       int volt_reg, mV = uV / 1000, dcdc = rdev_get_id(rdev);
+       u16 val;
+
+       dev_dbg(wm8350->dev, "%s %d mV %d\n", __func__, dcdc, mV);
+
+       if (mV && (mV < 850 || mV > 4025)) {
+               dev_err(wm8350->dev,
+                       "DCDC%d suspend voltage %d mV out of range\n",
+                       dcdc, mV);
+               return -EINVAL;
+       }
+       if (mV == 0)
+               mV = 850;
+
+       switch (dcdc) {
+       case WM8350_DCDC_1:
+               volt_reg = WM8350_DCDC1_LOW_POWER;
+               break;
+       case WM8350_DCDC_3:
+               volt_reg = WM8350_DCDC3_LOW_POWER;
+               break;
+       case WM8350_DCDC_4:
+               volt_reg = WM8350_DCDC4_LOW_POWER;
+               break;
+       case WM8350_DCDC_6:
+               volt_reg = WM8350_DCDC6_LOW_POWER;
+               break;
+       case WM8350_DCDC_2:
+       case WM8350_DCDC_5:
+       default:
+               return -EINVAL;
+       }
+
+       /* all DCDCs have same mV bits */
+       val = wm8350_reg_read(wm8350, volt_reg) & ~WM8350_DC1_VSEL_MASK;
+       wm8350_reg_write(wm8350, volt_reg,
+                        val | wm8350_dcdc_mvolts_to_val(mV));
+       return 0;
+}
+
+static int wm8350_dcdc_set_suspend_enable(struct regulator_dev *rdev)
+{
+       struct wm8350 *wm8350 = rdev_get_drvdata(rdev);
+       int dcdc = rdev_get_id(rdev);
+       u16 val;
+
+       switch (dcdc) {
+       case WM8350_DCDC_1:
+               val = wm8350_reg_read(wm8350, WM8350_DCDC1_LOW_POWER)
+                       & ~WM8350_DCDC_HIB_MODE_MASK;
+               wm8350_reg_write(wm8350, WM8350_DCDC1_LOW_POWER,
+                       wm8350->pmic.dcdc1_hib_mode);
+               break;
+       case WM8350_DCDC_3:
+               val = wm8350_reg_read(wm8350, WM8350_DCDC3_LOW_POWER)
+                       & ~WM8350_DCDC_HIB_MODE_MASK;
+               wm8350_reg_write(wm8350, WM8350_DCDC3_LOW_POWER,
+                       wm8350->pmic.dcdc3_hib_mode);
+               break;
+       case WM8350_DCDC_4:
+               val = wm8350_reg_read(wm8350, WM8350_DCDC4_LOW_POWER)
+                       & ~WM8350_DCDC_HIB_MODE_MASK;
+               wm8350_reg_write(wm8350, WM8350_DCDC4_LOW_POWER,
+                       wm8350->pmic.dcdc4_hib_mode);
+               break;
+       case WM8350_DCDC_6:
+               val = wm8350_reg_read(wm8350, WM8350_DCDC6_LOW_POWER)
+                       & ~WM8350_DCDC_HIB_MODE_MASK;
+               wm8350_reg_write(wm8350, WM8350_DCDC6_LOW_POWER,
+                       wm8350->pmic.dcdc6_hib_mode);
+               break;
+       case WM8350_DCDC_2:
+       case WM8350_DCDC_5:
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int wm8350_dcdc_set_suspend_disable(struct regulator_dev *rdev)
+{
+       struct wm8350 *wm8350 = rdev_get_drvdata(rdev);
+       int dcdc = rdev_get_id(rdev);
+       u16 val;
+
+       switch (dcdc) {
+       case WM8350_DCDC_1:
+               val = wm8350_reg_read(wm8350, WM8350_DCDC1_LOW_POWER);
+               wm8350->pmic.dcdc1_hib_mode = val & WM8350_DCDC_HIB_MODE_MASK;
+               wm8350_reg_write(wm8350, WM8350_DCDC1_LOW_POWER,
+                       WM8350_DCDC_HIB_MODE_DIS);
+               break;
+       case WM8350_DCDC_3:
+               val = wm8350_reg_read(wm8350, WM8350_DCDC3_LOW_POWER);
+               wm8350->pmic.dcdc3_hib_mode = val & WM8350_DCDC_HIB_MODE_MASK;
+               wm8350_reg_write(wm8350, WM8350_DCDC3_LOW_POWER,
+                       WM8350_DCDC_HIB_MODE_DIS);
+               break;
+       case WM8350_DCDC_4:
+               val = wm8350_reg_read(wm8350, WM8350_DCDC4_LOW_POWER);
+               wm8350->pmic.dcdc4_hib_mode = val & WM8350_DCDC_HIB_MODE_MASK;
+               wm8350_reg_write(wm8350, WM8350_DCDC4_LOW_POWER,
+                       WM8350_DCDC_HIB_MODE_DIS);
+               break;
+       case WM8350_DCDC_6:
+               val = wm8350_reg_read(wm8350, WM8350_DCDC6_LOW_POWER);
+               wm8350->pmic.dcdc6_hib_mode = val & WM8350_DCDC_HIB_MODE_MASK;
+               wm8350_reg_write(wm8350, WM8350_DCDC6_LOW_POWER,
+                       WM8350_DCDC_HIB_MODE_DIS);
+               break;
+       case WM8350_DCDC_2:
+       case WM8350_DCDC_5:
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int wm8350_dcdc25_set_suspend_enable(struct regulator_dev *rdev)
+{
+       struct wm8350 *wm8350 = rdev_get_drvdata(rdev);
+       int dcdc = rdev_get_id(rdev);
+       u16 val;
+
+       switch (dcdc) {
+       case WM8350_DCDC_2:
+               val = wm8350_reg_read(wm8350, WM8350_DCDC2_CONTROL)
+                   & ~WM8350_DC2_HIB_MODE_MASK;
+               wm8350_reg_write(wm8350, WM8350_DCDC2_CONTROL, val |
+                                WM8350_DC2_HIB_MODE_ACTIVE);
+               break;
+       case WM8350_DCDC_5:
+               val = wm8350_reg_read(wm8350, WM8350_DCDC5_CONTROL)
+                   & ~WM8350_DC2_HIB_MODE_MASK;
+               wm8350_reg_write(wm8350, WM8350_DCDC5_CONTROL, val |
+                                WM8350_DC5_HIB_MODE_ACTIVE);
+               break;
+       default:
+               return -EINVAL;
+       }
+       return 0;
+}
+
+static int wm8350_dcdc25_set_suspend_disable(struct regulator_dev *rdev)
+{
+       struct wm8350 *wm8350 = rdev_get_drvdata(rdev);
+       int dcdc = rdev_get_id(rdev);
+       u16 val;
+
+       switch (dcdc) {
+       case WM8350_DCDC_2:
+               val = wm8350_reg_read(wm8350, WM8350_DCDC2_CONTROL)
+                   & ~WM8350_DC2_HIB_MODE_MASK;
+               wm8350_reg_write(wm8350, WM8350_DCDC2_CONTROL, val |
+                                WM8350_DC2_HIB_MODE_DISABLE);
+               break;
+       case WM8350_DCDC_5:
+               val = wm8350_reg_read(wm8350, WM8350_DCDC5_CONTROL)
+                   & ~WM8350_DC2_HIB_MODE_MASK;
+               wm8350_reg_write(wm8350, WM8350_DCDC5_CONTROL, val |
+                                WM8350_DC2_HIB_MODE_DISABLE);
+               break;
+       default:
+               return -EINVAL;
+       }
+       return 0;
+}
+
+static int wm8350_dcdc_set_suspend_mode(struct regulator_dev *rdev,
+       unsigned int mode)
+{
+       struct wm8350 *wm8350 = rdev_get_drvdata(rdev);
+       int dcdc = rdev_get_id(rdev);
+       u16 *hib_mode;
+
+       switch (dcdc) {
+       case WM8350_DCDC_1:
+               hib_mode = &wm8350->pmic.dcdc1_hib_mode;
+               break;
+       case WM8350_DCDC_3:
+               hib_mode = &wm8350->pmic.dcdc3_hib_mode;
+               break;
+       case WM8350_DCDC_4:
+               hib_mode = &wm8350->pmic.dcdc4_hib_mode;
+               break;
+       case WM8350_DCDC_6:
+               hib_mode = &wm8350->pmic.dcdc6_hib_mode;
+               break;
+       case WM8350_DCDC_2:
+       case WM8350_DCDC_5:
+       default:
+               return -EINVAL;
+       }
+
+       switch (mode) {
+       case REGULATOR_MODE_NORMAL:
+               *hib_mode = WM8350_DCDC_HIB_MODE_IMAGE;
+               break;
+       case REGULATOR_MODE_IDLE:
+               *hib_mode = WM8350_DCDC_HIB_MODE_STANDBY;
+               break;
+       case REGULATOR_MODE_STANDBY:
+               *hib_mode = WM8350_DCDC_HIB_MODE_LDO_IM;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int wm8350_ldo_set_suspend_voltage(struct regulator_dev *rdev, int uV)
+{
+       struct wm8350 *wm8350 = rdev_get_drvdata(rdev);
+       int volt_reg, mV = uV / 1000, ldo = rdev_get_id(rdev);
+       u16 val;
+
+       dev_dbg(wm8350->dev, "%s %d mV %d\n", __func__, ldo, mV);
+
+       if (mV < 900 || mV > 3300) {
+               dev_err(wm8350->dev, "LDO%d voltage %d mV out of range\n",
+                       ldo, mV);
+               return -EINVAL;
+       }
+
+       switch (ldo) {
+       case WM8350_LDO_1:
+               volt_reg = WM8350_LDO1_LOW_POWER;
+               break;
+       case WM8350_LDO_2:
+               volt_reg = WM8350_LDO2_LOW_POWER;
+               break;
+       case WM8350_LDO_3:
+               volt_reg = WM8350_LDO3_LOW_POWER;
+               break;
+       case WM8350_LDO_4:
+               volt_reg = WM8350_LDO4_LOW_POWER;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       /* all LDOs have same mV bits */
+       val = wm8350_reg_read(wm8350, volt_reg) & ~WM8350_LDO1_VSEL_MASK;
+       wm8350_reg_write(wm8350, volt_reg,
+                        val | wm8350_ldo_mvolts_to_val(mV));
+       return 0;
+}
+
+static int wm8350_ldo_set_suspend_enable(struct regulator_dev *rdev)
+{
+       struct wm8350 *wm8350 = rdev_get_drvdata(rdev);
+       int volt_reg, ldo = rdev_get_id(rdev);
+       u16 val;
+
+       switch (ldo) {
+       case WM8350_LDO_1:
+               volt_reg = WM8350_LDO1_LOW_POWER;
+               break;
+       case WM8350_LDO_2:
+               volt_reg = WM8350_LDO2_LOW_POWER;
+               break;
+       case WM8350_LDO_3:
+               volt_reg = WM8350_LDO3_LOW_POWER;
+               break;
+       case WM8350_LDO_4:
+               volt_reg = WM8350_LDO4_LOW_POWER;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       /* all LDOs have same mV bits */
+       val = wm8350_reg_read(wm8350, volt_reg) & ~WM8350_LDO1_HIB_MODE_MASK;
+       wm8350_reg_write(wm8350, volt_reg, val);
+       return 0;
+}
+
+static int wm8350_ldo_set_suspend_disable(struct regulator_dev *rdev)
+{
+       struct wm8350 *wm8350 = rdev_get_drvdata(rdev);
+       int volt_reg, ldo = rdev_get_id(rdev);
+       u16 val;
+
+       switch (ldo) {
+       case WM8350_LDO_1:
+               volt_reg = WM8350_LDO1_LOW_POWER;
+               break;
+       case WM8350_LDO_2:
+               volt_reg = WM8350_LDO2_LOW_POWER;
+               break;
+       case WM8350_LDO_3:
+               volt_reg = WM8350_LDO3_LOW_POWER;
+               break;
+       case WM8350_LDO_4:
+               volt_reg = WM8350_LDO4_LOW_POWER;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       /* all LDOs have same mV bits */
+       val = wm8350_reg_read(wm8350, volt_reg) & ~WM8350_LDO1_HIB_MODE_MASK;
+       wm8350_reg_write(wm8350, volt_reg, WM8350_LDO1_HIB_MODE_DIS);
+       return 0;
+}
+
+static int wm8350_ldo_set_voltage(struct regulator_dev *rdev, int min_uV,
+       int max_uV)
+{
+       struct wm8350 *wm8350 = rdev_get_drvdata(rdev);
+       int volt_reg, ldo = rdev_get_id(rdev), mV, min_mV = min_uV / 1000,
+               max_mV = max_uV / 1000;
+       u16 val;
+
+       if (min_mV < 900 || min_mV > 3300)
+               return -EINVAL;
+       if (max_mV < 900 || max_mV > 3300)
+               return -EINVAL;
+
+       if (min_mV < 1800) {
+               /* step size is 50mV < 1800mV */
+               mV = (min_mV - 851) / 50;
+               if (wm8350_ldo_val_to_mvolts(mV) > max_mV)
+                       return -EINVAL;
+               BUG_ON(wm8350_ldo_val_to_mvolts(mV) < min_mV);
+       } else {
+               /* step size is 100mV > 1800mV */
+               mV = ((min_mV - 1701) / 100) + 16;
+               if (wm8350_ldo_val_to_mvolts(mV) > max_mV)
+                       return -EINVAL;
+               BUG_ON(wm8350_ldo_val_to_mvolts(mV) < min_mV);
+       }
+
+       switch (ldo) {
+       case WM8350_LDO_1:
+               volt_reg = WM8350_LDO1_CONTROL;
+               break;
+       case WM8350_LDO_2:
+               volt_reg = WM8350_LDO2_CONTROL;
+               break;
+       case WM8350_LDO_3:
+               volt_reg = WM8350_LDO3_CONTROL;
+               break;
+       case WM8350_LDO_4:
+               volt_reg = WM8350_LDO4_CONTROL;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       /* all LDOs have same mV bits */
+       val = wm8350_reg_read(wm8350, volt_reg) & ~WM8350_LDO1_VSEL_MASK;
+       wm8350_reg_write(wm8350, volt_reg, val | mV);
+       return 0;
+}
+
+static int wm8350_ldo_get_voltage(struct regulator_dev *rdev)
+{
+       struct wm8350 *wm8350 = rdev_get_drvdata(rdev);
+       int volt_reg, ldo = rdev_get_id(rdev);
+       u16 val;
+
+       switch (ldo) {
+       case WM8350_LDO_1:
+               volt_reg = WM8350_LDO1_CONTROL;
+               break;
+       case WM8350_LDO_2:
+               volt_reg = WM8350_LDO2_CONTROL;
+               break;
+       case WM8350_LDO_3:
+               volt_reg = WM8350_LDO3_CONTROL;
+               break;
+       case WM8350_LDO_4:
+               volt_reg = WM8350_LDO4_CONTROL;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       /* all LDOs have same mV bits */
+       val = wm8350_reg_read(wm8350, volt_reg) & WM8350_LDO1_VSEL_MASK;
+       return wm8350_ldo_val_to_mvolts(val) * 1000;
+}
+
+int wm8350_dcdc_set_slot(struct wm8350 *wm8350, int dcdc, u16 start,
+                        u16 stop, u16 fault)
+{
+       int slot_reg;
+       u16 val;
+
+       dev_dbg(wm8350->dev, "%s %d start %d stop %d\n",
+               __func__, dcdc, start, stop);
+
+       /* slot valid ? */
+       if (start > 15 || stop > 15)
+               return -EINVAL;
+
+       switch (dcdc) {
+       case WM8350_DCDC_1:
+               slot_reg = WM8350_DCDC1_TIMEOUTS;
+               break;
+       case WM8350_DCDC_2:
+               slot_reg = WM8350_DCDC2_TIMEOUTS;
+               break;
+       case WM8350_DCDC_3:
+               slot_reg = WM8350_DCDC3_TIMEOUTS;
+               break;
+       case WM8350_DCDC_4:
+               slot_reg = WM8350_DCDC4_TIMEOUTS;
+               break;
+       case WM8350_DCDC_5:
+               slot_reg = WM8350_DCDC5_TIMEOUTS;
+               break;
+       case WM8350_DCDC_6:
+               slot_reg = WM8350_DCDC6_TIMEOUTS;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       val = wm8350_reg_read(wm8350, slot_reg) &
+           ~(WM8350_DC1_ENSLOT_MASK | WM8350_DC1_SDSLOT_MASK |
+             WM8350_DC1_ERRACT_MASK);
+       wm8350_reg_write(wm8350, slot_reg,
+                        val | (start << WM8350_DC1_ENSLOT_SHIFT) |
+                        (stop << WM8350_DC1_SDSLOT_SHIFT) |
+                        (fault << WM8350_DC1_ERRACT_SHIFT));
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(wm8350_dcdc_set_slot);
+
+int wm8350_ldo_set_slot(struct wm8350 *wm8350, int ldo, u16 start, u16 stop)
+{
+       int slot_reg;
+       u16 val;
+
+       dev_dbg(wm8350->dev, "%s %d start %d stop %d\n",
+               __func__, ldo, start, stop);
+
+       /* slot valid ? */
+       if (start > 15 || stop > 15)
+               return -EINVAL;
+
+       switch (ldo) {
+       case WM8350_LDO_1:
+               slot_reg = WM8350_LDO1_TIMEOUTS;
+               break;
+       case WM8350_LDO_2:
+               slot_reg = WM8350_LDO2_TIMEOUTS;
+               break;
+       case WM8350_LDO_3:
+               slot_reg = WM8350_LDO3_TIMEOUTS;
+               break;
+       case WM8350_LDO_4:
+               slot_reg = WM8350_LDO4_TIMEOUTS;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       val = wm8350_reg_read(wm8350, slot_reg) & ~WM8350_LDO1_SDSLOT_MASK;
+       wm8350_reg_write(wm8350, slot_reg, val | ((start << 10) | (stop << 6)));
+       return 0;
+}
+EXPORT_SYMBOL_GPL(wm8350_ldo_set_slot);
+
+int wm8350_dcdc25_set_mode(struct wm8350 *wm8350, int dcdc, u16 mode,
+                          u16 ilim, u16 ramp, u16 feedback)
+{
+       u16 val;
+
+       dev_dbg(wm8350->dev, "%s %d mode: %s %s\n", __func__, dcdc,
+               mode ? "normal" : "boost", ilim ? "low" : "normal");
+
+       switch (dcdc) {
+       case WM8350_DCDC_2:
+               val = wm8350_reg_read(wm8350, WM8350_DCDC2_CONTROL)
+                   & ~(WM8350_DC2_MODE_MASK | WM8350_DC2_ILIM_MASK |
+                       WM8350_DC2_RMP_MASK | WM8350_DC2_FBSRC_MASK);
+               wm8350_reg_write(wm8350, WM8350_DCDC2_CONTROL, val |
+                                (mode << WM8350_DC2_MODE_SHIFT) |
+                                (ilim << WM8350_DC2_ILIM_SHIFT) |
+                                (ramp << WM8350_DC2_RMP_SHIFT) |
+                                (feedback << WM8350_DC2_FBSRC_SHIFT));
+               break;
+       case WM8350_DCDC_5:
+               val = wm8350_reg_read(wm8350, WM8350_DCDC5_CONTROL)
+                   & ~(WM8350_DC5_MODE_MASK | WM8350_DC5_ILIM_MASK |
+                       WM8350_DC5_RMP_MASK | WM8350_DC5_FBSRC_MASK);
+               wm8350_reg_write(wm8350, WM8350_DCDC5_CONTROL, val |
+                                (mode << WM8350_DC5_MODE_SHIFT) |
+                                (ilim << WM8350_DC5_ILIM_SHIFT) |
+                                (ramp << WM8350_DC5_RMP_SHIFT) |
+                                (feedback << WM8350_DC5_FBSRC_SHIFT));
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(wm8350_dcdc25_set_mode);
+
+static int wm8350_dcdc_enable(struct regulator_dev *rdev)
+{
+       struct wm8350 *wm8350 = rdev_get_drvdata(rdev);
+       int dcdc = rdev_get_id(rdev);
+       u16 shift;
+
+       if (dcdc < WM8350_DCDC_1 || dcdc > WM8350_DCDC_6)
+               return -EINVAL;
+
+       shift = dcdc - WM8350_DCDC_1;
+       wm8350_set_bits(wm8350, WM8350_DCDC_LDO_REQUESTED, 1 << shift);
+       return 0;
+}
+
+static int wm8350_dcdc_disable(struct regulator_dev *rdev)
+{
+       struct wm8350 *wm8350 = rdev_get_drvdata(rdev);
+       int dcdc = rdev_get_id(rdev);
+       u16 shift;
+
+       if (dcdc < WM8350_DCDC_1 || dcdc > WM8350_DCDC_6)
+               return -EINVAL;
+
+       shift = dcdc - WM8350_DCDC_1;
+       wm8350_clear_bits(wm8350, WM8350_DCDC_LDO_REQUESTED, 1 << shift);
+
+       return 0;
+}
+
+static int wm8350_ldo_enable(struct regulator_dev *rdev)
+{
+       struct wm8350 *wm8350 = rdev_get_drvdata(rdev);
+       int ldo = rdev_get_id(rdev);
+       u16 shift;
+
+       if (ldo < WM8350_LDO_1 || ldo > WM8350_LDO_4)
+               return -EINVAL;
+
+       shift = (ldo - WM8350_LDO_1) + 8;
+       wm8350_set_bits(wm8350, WM8350_DCDC_LDO_REQUESTED, 1 << shift);
+       return 0;
+}
+
+static int wm8350_ldo_disable(struct regulator_dev *rdev)
+{
+       struct wm8350 *wm8350 = rdev_get_drvdata(rdev);
+       int ldo = rdev_get_id(rdev);
+       u16 shift;
+
+       if (ldo < WM8350_LDO_1 || ldo > WM8350_LDO_4)
+               return -EINVAL;
+
+       shift = (ldo - WM8350_LDO_1) + 8;
+       wm8350_clear_bits(wm8350, WM8350_DCDC_LDO_REQUESTED, 1 << shift);
+       return 0;
+}
+
+static int force_continuous_enable(struct wm8350 *wm8350, int dcdc, int enable)
+{
+       int reg = 0, ret;
+
+       switch (dcdc) {
+       case WM8350_DCDC_1:
+               reg = WM8350_DCDC1_FORCE_PWM;
+               break;
+       case WM8350_DCDC_3:
+               reg = WM8350_DCDC3_FORCE_PWM;
+               break;
+       case WM8350_DCDC_4:
+               reg = WM8350_DCDC4_FORCE_PWM;
+               break;
+       case WM8350_DCDC_6:
+               reg = WM8350_DCDC6_FORCE_PWM;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       if (enable)
+               ret = wm8350_set_bits(wm8350, reg,
+                       WM8350_DCDC1_FORCE_PWM_ENA);
+       else
+               ret = wm8350_clear_bits(wm8350, reg,
+                       WM8350_DCDC1_FORCE_PWM_ENA);
+       return ret;
+}
+
+static int wm8350_dcdc_set_mode(struct regulator_dev *rdev, unsigned int mode)
+{
+       struct wm8350 *wm8350 = rdev_get_drvdata(rdev);
+       int dcdc = rdev_get_id(rdev);
+       u16 val;
+
+       if (dcdc < WM8350_DCDC_1 || dcdc > WM8350_DCDC_6)
+               return -EINVAL;
+
+       if (dcdc == WM8350_DCDC_2 || dcdc == WM8350_DCDC_5)
+               return -EINVAL;
+
+       val = 1 << (dcdc - WM8350_DCDC_1);
+
+       switch (mode) {
+       case REGULATOR_MODE_FAST:
+               /* force continuous mode */
+               wm8350_set_bits(wm8350, WM8350_DCDC_ACTIVE_OPTIONS, val);
+               wm8350_clear_bits(wm8350, WM8350_DCDC_SLEEP_OPTIONS, val);
+               force_continuous_enable(wm8350, dcdc, 1);
+               break;
+       case REGULATOR_MODE_NORMAL:
+               /* active / pulse skipping */
+               wm8350_set_bits(wm8350, WM8350_DCDC_ACTIVE_OPTIONS, val);
+               wm8350_clear_bits(wm8350, WM8350_DCDC_SLEEP_OPTIONS, val);
+               force_continuous_enable(wm8350, dcdc, 0);
+               break;
+       case REGULATOR_MODE_IDLE:
+               /* standby mode */
+               force_continuous_enable(wm8350, dcdc, 0);
+               wm8350_clear_bits(wm8350, WM8350_DCDC_SLEEP_OPTIONS, val);
+               wm8350_clear_bits(wm8350, WM8350_DCDC_ACTIVE_OPTIONS, val);
+               break;
+       case REGULATOR_MODE_STANDBY:
+               /* LDO mode */
+               force_continuous_enable(wm8350, dcdc, 0);
+               wm8350_set_bits(wm8350, WM8350_DCDC_SLEEP_OPTIONS, val);
+               break;
+       }
+
+       return 0;
+}
+
+static unsigned int wm8350_dcdc_get_mode(struct regulator_dev *rdev)
+{
+       struct wm8350 *wm8350 = rdev_get_drvdata(rdev);
+       int dcdc = rdev_get_id(rdev);
+       u16 mask, sleep, active, force;
+       int mode = REGULATOR_MODE_NORMAL;
+
+       if (dcdc < WM8350_DCDC_1 || dcdc > WM8350_DCDC_6)
+               return -EINVAL;
+
+       if (dcdc == WM8350_DCDC_2 || dcdc == WM8350_DCDC_5)
+               return -EINVAL;
+
+       mask = 1 << (dcdc - WM8350_DCDC_1);
+       active = wm8350_reg_read(wm8350, WM8350_DCDC_ACTIVE_OPTIONS) & mask;
+       sleep = wm8350_reg_read(wm8350, WM8350_DCDC_SLEEP_OPTIONS) & mask;
+       force = wm8350_reg_read(wm8350, WM8350_DCDC1_FORCE_PWM)
+           & WM8350_DCDC1_FORCE_PWM_ENA;
+       dev_dbg(wm8350->dev, "mask %x active %x sleep %x force %x",
+               mask, active, sleep, force);
+
+       if (active && !sleep) {
+               if (force)
+                       mode = REGULATOR_MODE_FAST;
+               else
+                       mode = REGULATOR_MODE_NORMAL;
+       } else if (!active && !sleep)
+               mode = REGULATOR_MODE_IDLE;
+       else if (!sleep)
+               mode = REGULATOR_MODE_STANDBY;
+
+       return mode;
+}
+
+static unsigned int wm8350_ldo_get_mode(struct regulator_dev *rdev)
+{
+       return REGULATOR_MODE_NORMAL;
+}
+
+struct wm8350_dcdc_efficiency {
+       int uA_load_min;
+       int uA_load_max;
+       unsigned int mode;
+};
+
+static const struct wm8350_dcdc_efficiency dcdc1_6_efficiency[] = {
+       {0, 10000, REGULATOR_MODE_STANDBY},       /* 0 - 10mA - LDO */
+       {10000, 100000, REGULATOR_MODE_IDLE},     /* 10mA - 100mA - Standby */
+       {100000, 1000000, REGULATOR_MODE_NORMAL}, /* > 100mA - Active */
+       {-1, -1, REGULATOR_MODE_NORMAL},
+};
+
+static const struct wm8350_dcdc_efficiency dcdc3_4_efficiency[] = {
+       {0, 10000, REGULATOR_MODE_STANDBY},      /* 0 - 10mA - LDO */
+       {10000, 100000, REGULATOR_MODE_IDLE},    /* 10mA - 100mA - Standby */
+       {100000, 800000, REGULATOR_MODE_NORMAL}, /* > 100mA - Active */
+       {-1, -1, REGULATOR_MODE_NORMAL},
+};
+
+static unsigned int get_mode(int uA, const struct wm8350_dcdc_efficiency *eff)
+{
+       int i = 0;
+
+       while (eff[i].uA_load_min != -1) {
+               if (uA >= eff[i].uA_load_min && uA <= eff[i].uA_load_max)
+                       return eff[i].mode;
+       }
+       return REGULATOR_MODE_NORMAL;
+}
+
+/* Query the regulator for it's most efficient mode @ uV,uA
+ * WM8350 regulator efficiency is pretty similar over
+ * different input and output uV.
+ */
+static unsigned int wm8350_dcdc_get_optimum_mode(struct regulator_dev *rdev,
+                                                int input_uV, int output_uV,
+                                                int output_uA)
+{
+       int dcdc = rdev_get_id(rdev), mode;
+
+       switch (dcdc) {
+       case WM8350_DCDC_1:
+       case WM8350_DCDC_6:
+               mode = get_mode(output_uA, dcdc1_6_efficiency);
+               break;
+       case WM8350_DCDC_3:
+       case WM8350_DCDC_4:
+               mode = get_mode(output_uA, dcdc3_4_efficiency);
+               break;
+       default:
+               mode = REGULATOR_MODE_NORMAL;
+               break;
+       }
+       return mode;
+}
+
+static int wm8350_dcdc_is_enabled(struct regulator_dev *rdev)
+{
+       struct wm8350 *wm8350 = rdev_get_drvdata(rdev);
+       int dcdc = rdev_get_id(rdev), shift;
+
+       if (dcdc < WM8350_DCDC_1 || dcdc > WM8350_DCDC_6)
+               return -EINVAL;
+
+       shift = dcdc - WM8350_DCDC_1;
+       return wm8350_reg_read(wm8350, WM8350_DCDC_LDO_REQUESTED)
+           & (1 << shift);
+}
+
+static int wm8350_ldo_is_enabled(struct regulator_dev *rdev)
+{
+       struct wm8350 *wm8350 = rdev_get_drvdata(rdev);
+       int ldo = rdev_get_id(rdev), shift;
+
+       if (ldo < WM8350_LDO_1 || ldo > WM8350_LDO_4)
+               return -EINVAL;
+
+       shift = (ldo - WM8350_LDO_1) + 8;
+       return wm8350_reg_read(wm8350, WM8350_DCDC_LDO_REQUESTED)
+           & (1 << shift);
+}
+
+static struct regulator_ops wm8350_dcdc_ops = {
+       .set_voltage = wm8350_dcdc_set_voltage,
+       .get_voltage = wm8350_dcdc_get_voltage,
+       .enable = wm8350_dcdc_enable,
+       .disable = wm8350_dcdc_disable,
+       .get_mode = wm8350_dcdc_get_mode,
+       .set_mode = wm8350_dcdc_set_mode,
+       .get_optimum_mode = wm8350_dcdc_get_optimum_mode,
+       .is_enabled = wm8350_dcdc_is_enabled,
+       .set_suspend_voltage = wm8350_dcdc_set_suspend_voltage,
+       .set_suspend_enable = wm8350_dcdc_set_suspend_enable,
+       .set_suspend_disable = wm8350_dcdc_set_suspend_disable,
+       .set_suspend_mode = wm8350_dcdc_set_suspend_mode,
+};
+
+static struct regulator_ops wm8350_dcdc2_5_ops = {
+       .enable = wm8350_dcdc_enable,
+       .disable = wm8350_dcdc_disable,
+       .is_enabled = wm8350_dcdc_is_enabled,
+       .set_suspend_enable = wm8350_dcdc25_set_suspend_enable,
+       .set_suspend_disable = wm8350_dcdc25_set_suspend_disable,
+};
+
+static struct regulator_ops wm8350_ldo_ops = {
+       .set_voltage = wm8350_ldo_set_voltage,
+       .get_voltage = wm8350_ldo_get_voltage,
+       .enable = wm8350_ldo_enable,
+       .disable = wm8350_ldo_disable,
+       .is_enabled = wm8350_ldo_is_enabled,
+       .get_mode = wm8350_ldo_get_mode,
+       .set_suspend_voltage = wm8350_ldo_set_suspend_voltage,
+       .set_suspend_enable = wm8350_ldo_set_suspend_enable,
+       .set_suspend_disable = wm8350_ldo_set_suspend_disable,
+};
+
+static struct regulator_ops wm8350_isink_ops = {
+       .set_current_limit = wm8350_isink_set_current,
+       .get_current_limit = wm8350_isink_get_current,
+       .enable = wm8350_isink_enable,
+       .disable = wm8350_isink_disable,
+       .is_enabled = wm8350_isink_is_enabled,
+};
+
+static struct regulator_desc wm8350_reg[NUM_WM8350_REGULATORS] = {
+       {
+               .name = "DCDC1",
+               .id = WM8350_DCDC_1,
+               .ops = &wm8350_dcdc_ops,
+               .irq = WM8350_IRQ_UV_DC1,
+               .type = REGULATOR_VOLTAGE,
+               .owner = THIS_MODULE,
+       },
+       {
+               .name = "DCDC2",
+               .id = WM8350_DCDC_2,
+               .ops = &wm8350_dcdc2_5_ops,
+               .irq = WM8350_IRQ_UV_DC2,
+               .type = REGULATOR_VOLTAGE,
+               .owner = THIS_MODULE,
+       },
+       {
+               .name = "DCDC3",
+               .id = WM8350_DCDC_3,
+               .ops = &wm8350_dcdc_ops,
+               .irq = WM8350_IRQ_UV_DC3,
+               .type = REGULATOR_VOLTAGE,
+               .owner = THIS_MODULE,
+       },
+       {
+               .name = "DCDC4",
+               .id = WM8350_DCDC_4,
+               .ops = &wm8350_dcdc_ops,
+               .irq = WM8350_IRQ_UV_DC4,
+               .type = REGULATOR_VOLTAGE,
+               .owner = THIS_MODULE,
+       },
+       {
+               .name = "DCDC5",
+               .id = WM8350_DCDC_5,
+               .ops = &wm8350_dcdc2_5_ops,
+               .irq = WM8350_IRQ_UV_DC5,
+               .type = REGULATOR_VOLTAGE,
+               .owner = THIS_MODULE,
+        },
+       {
+               .name = "DCDC6",
+               .id = WM8350_DCDC_6,
+               .ops = &wm8350_dcdc_ops,
+               .irq = WM8350_IRQ_UV_DC6,
+               .type = REGULATOR_VOLTAGE,
+               .owner = THIS_MODULE,
+       },
+       {
+               .name = "LDO1",
+               .id = WM8350_LDO_1,
+               .ops = &wm8350_ldo_ops,
+               .irq = WM8350_IRQ_UV_LDO1,
+               .type = REGULATOR_VOLTAGE,
+               .owner = THIS_MODULE,
+       },
+       {
+               .name = "LDO2",
+               .id = WM8350_LDO_2,
+               .ops = &wm8350_ldo_ops,
+               .irq = WM8350_IRQ_UV_LDO2,
+               .type = REGULATOR_VOLTAGE,
+               .owner = THIS_MODULE,
+       },
+       {
+               .name = "LDO3",
+               .id = WM8350_LDO_3,
+               .ops = &wm8350_ldo_ops,
+               .irq = WM8350_IRQ_UV_LDO3,
+               .type = REGULATOR_VOLTAGE,
+               .owner = THIS_MODULE,
+       },
+       {
+               .name = "LDO4",
+               .id = WM8350_LDO_4,
+               .ops = &wm8350_ldo_ops,
+               .irq = WM8350_IRQ_UV_LDO4,
+               .type = REGULATOR_VOLTAGE,
+               .owner = THIS_MODULE,
+       },
+       {
+               .name = "ISINKA",
+               .id = WM8350_ISINK_A,
+               .ops = &wm8350_isink_ops,
+               .irq = WM8350_IRQ_CS1,
+               .type = REGULATOR_CURRENT,
+               .owner = THIS_MODULE,
+        },
+       {
+               .name = "ISINKB",
+               .id = WM8350_ISINK_B,
+               .ops = &wm8350_isink_ops,
+               .irq = WM8350_IRQ_CS2,
+               .type = REGULATOR_CURRENT,
+               .owner = THIS_MODULE,
+        },
+};
+
+static void pmic_uv_handler(struct wm8350 *wm8350, int irq, void *data)
+{
+       struct regulator_dev *rdev = (struct regulator_dev *)data;
+
+       if (irq == WM8350_IRQ_CS1 || irq == WM8350_IRQ_CS2)
+               regulator_notifier_call_chain(rdev,
+                                             REGULATOR_EVENT_REGULATION_OUT,
+                                             wm8350);
+       else
+               regulator_notifier_call_chain(rdev,
+                                             REGULATOR_EVENT_UNDER_VOLTAGE,
+                                             wm8350);
+}
+
+static int wm8350_regulator_probe(struct platform_device *pdev)
+{
+       struct wm8350 *wm8350 = dev_get_drvdata(&pdev->dev);
+       struct regulator_dev *rdev;
+       int ret;
+       u16 val;
+
+       if (pdev->id < WM8350_DCDC_1 || pdev->id > WM8350_ISINK_B)
+               return -ENODEV;
+
+       /* do any regulatior specific init */
+       switch (pdev->id) {
+       case WM8350_DCDC_1:
+               val = wm8350_reg_read(wm8350, WM8350_DCDC1_LOW_POWER);
+               wm8350->pmic.dcdc1_hib_mode = val & WM8350_DCDC_HIB_MODE_MASK;
+               break;
+       case WM8350_DCDC_3:
+               val = wm8350_reg_read(wm8350, WM8350_DCDC3_LOW_POWER);
+               wm8350->pmic.dcdc3_hib_mode = val & WM8350_DCDC_HIB_MODE_MASK;
+               break;
+       case WM8350_DCDC_4:
+               val = wm8350_reg_read(wm8350, WM8350_DCDC4_LOW_POWER);
+               wm8350->pmic.dcdc4_hib_mode = val & WM8350_DCDC_HIB_MODE_MASK;
+               break;
+       case WM8350_DCDC_6:
+               val = wm8350_reg_read(wm8350, WM8350_DCDC6_LOW_POWER);
+               wm8350->pmic.dcdc6_hib_mode = val & WM8350_DCDC_HIB_MODE_MASK;
+               break;
+       }
+
+
+       /* register regulator */
+       rdev = regulator_register(&wm8350_reg[pdev->id], &pdev->dev,
+                                 dev_get_drvdata(&pdev->dev));
+       if (IS_ERR(rdev)) {
+               dev_err(&pdev->dev, "failed to register %s\n",
+                       wm8350_reg[pdev->id].name);
+               return PTR_ERR(rdev);
+       }
+
+       /* register regulator IRQ */
+       ret = wm8350_register_irq(wm8350, wm8350_reg[pdev->id].irq,
+                                 pmic_uv_handler, rdev);
+       if (ret < 0) {
+               regulator_unregister(rdev);
+               dev_err(&pdev->dev, "failed to register regulator %s IRQ\n",
+                       wm8350_reg[pdev->id].name);
+               return ret;
+       }
+
+       wm8350_unmask_irq(wm8350, wm8350_reg[pdev->id].irq);
+
+       return 0;
+}
+
+static int wm8350_regulator_remove(struct platform_device *pdev)
+{
+       struct regulator_dev *rdev = platform_get_drvdata(pdev);
+       struct wm8350 *wm8350 = rdev_get_drvdata(rdev);
+
+       wm8350_mask_irq(wm8350, wm8350_reg[pdev->id].irq);
+       wm8350_free_irq(wm8350, wm8350_reg[pdev->id].irq);
+
+       regulator_unregister(rdev);
+
+       return 0;
+}
+
+int wm8350_register_regulator(struct wm8350 *wm8350, int reg,
+                             struct regulator_init_data *initdata)
+{
+       struct platform_device *pdev;
+       int ret;
+
+       if (wm8350->pmic.pdev[reg])
+               return -EBUSY;
+
+       pdev = platform_device_alloc("wm8350-regulator", reg);
+       if (!pdev)
+               return -ENOMEM;
+
+       wm8350->pmic.pdev[reg] = pdev;
+
+       initdata->driver_data = wm8350;
+
+       pdev->dev.platform_data = initdata;
+       pdev->dev.parent = wm8350->dev;
+       platform_set_drvdata(pdev, wm8350);
+
+       ret = platform_device_add(pdev);
+
+       if (ret != 0) {
+               dev_err(wm8350->dev, "Failed to register regulator %d: %d\n",
+                       reg, ret);
+               platform_device_del(pdev);
+               wm8350->pmic.pdev[reg] = NULL;
+       }
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(wm8350_register_regulator);
+
+static struct platform_driver wm8350_regulator_driver = {
+       .probe = wm8350_regulator_probe,
+       .remove = wm8350_regulator_remove,
+       .driver         = {
+               .name   = "wm8350-regulator",
+       },
+};
+
+static int __init wm8350_regulator_init(void)
+{
+       return platform_driver_register(&wm8350_regulator_driver);
+}
+subsys_initcall(wm8350_regulator_init);
+
+static void __exit wm8350_regulator_exit(void)
+{
+       platform_driver_unregister(&wm8350_regulator_driver);
+}
+module_exit(wm8350_regulator_exit);
+
+/* Module information */
+MODULE_AUTHOR("Liam Girdwood");
+MODULE_DESCRIPTION("WM8350 voltage and current regulator driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/regulator/wm8400-regulator.c b/drivers/regulator/wm8400-regulator.c
new file mode 100644 (file)
index 0000000..48b372e
--- /dev/null
@@ -0,0 +1,368 @@
+/*
+ * Regulator support for WM8400
+ *
+ * Copyright 2008 Wolfson Microelectronics PLC.
+ *
+ * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ */
+
+#include <linux/bug.h>
+#include <linux/err.h>
+#include <linux/kernel.h>
+#include <linux/regulator/driver.h>
+#include <linux/mfd/wm8400-private.h>
+
+static int wm8400_ldo_is_enabled(struct regulator_dev *dev)
+{
+       struct wm8400 *wm8400 = rdev_get_drvdata(dev);
+       u16 val;
+
+       val = wm8400_reg_read(wm8400, WM8400_LDO1_CONTROL + rdev_get_id(dev));
+       return (val & WM8400_LDO1_ENA) != 0;
+}
+
+static int wm8400_ldo_enable(struct regulator_dev *dev)
+{
+       struct wm8400 *wm8400 = rdev_get_drvdata(dev);
+
+       return wm8400_set_bits(wm8400, WM8400_LDO1_CONTROL + rdev_get_id(dev),
+                              WM8400_LDO1_ENA, WM8400_LDO1_ENA);
+}
+
+static int wm8400_ldo_disable(struct regulator_dev *dev)
+{
+       struct wm8400 *wm8400 = rdev_get_drvdata(dev);
+
+       return wm8400_set_bits(wm8400, WM8400_LDO1_CONTROL + rdev_get_id(dev),
+                              WM8400_LDO1_ENA, 0);
+}
+
+static int wm8400_ldo_get_voltage(struct regulator_dev *dev)
+{
+       struct wm8400 *wm8400 = rdev_get_drvdata(dev);
+       u16 val;
+
+       val = wm8400_reg_read(wm8400, WM8400_LDO1_CONTROL + rdev_get_id(dev));
+       val &= WM8400_LDO1_VSEL_MASK;
+
+       if (val < 15)
+               return 900000 + (val * 50000);
+       else
+               return 1600000 + ((val - 14) * 100000);
+}
+
+static int wm8400_ldo_set_voltage(struct regulator_dev *dev,
+                                 int min_uV, int max_uV)
+{
+       struct wm8400 *wm8400 = rdev_get_drvdata(dev);
+       u16 val;
+
+       if (min_uV < 900000 || min_uV > 3300000)
+               return -EINVAL;
+
+       if (min_uV < 1700000) {
+               /* Steps of 50mV from 900mV;  */
+               val = (min_uV - 850001) / 50000;
+
+               if ((val * 50000) + 900000 > max_uV)
+                       return -EINVAL;
+               BUG_ON((val * 50000) + 900000 < min_uV);
+       } else {
+               /* Steps of 100mV from 1700mV */
+               val = ((min_uV - 1600001) / 100000);
+
+               if ((val * 100000) + 1700000 > max_uV)
+                       return -EINVAL;
+               BUG_ON((val * 100000) + 1700000 < min_uV);
+
+               val += 0xf;
+       }
+
+       return wm8400_set_bits(wm8400, WM8400_LDO1_CONTROL + rdev_get_id(dev),
+                              WM8400_LDO1_VSEL_MASK, val);
+}
+
+static struct regulator_ops wm8400_ldo_ops = {
+       .is_enabled = wm8400_ldo_is_enabled,
+       .enable = wm8400_ldo_enable,
+       .disable = wm8400_ldo_disable,
+       .get_voltage = wm8400_ldo_get_voltage,
+       .set_voltage = wm8400_ldo_set_voltage,
+};
+
+static int wm8400_dcdc_is_enabled(struct regulator_dev *dev)
+{
+       struct wm8400 *wm8400 = rdev_get_drvdata(dev);
+       int offset = (rdev_get_id(dev) - WM8400_DCDC1) * 2;
+       u16 val;
+
+       val = wm8400_reg_read(wm8400, WM8400_DCDC1_CONTROL_1 + offset);
+       return (val & WM8400_DC1_ENA) != 0;
+}
+
+static int wm8400_dcdc_enable(struct regulator_dev *dev)
+{
+       struct wm8400 *wm8400 = rdev_get_drvdata(dev);
+       int offset = (rdev_get_id(dev) - WM8400_DCDC1) * 2;
+
+       return wm8400_set_bits(wm8400, WM8400_DCDC1_CONTROL_1 + offset,
+                              WM8400_DC1_ENA, WM8400_DC1_ENA);
+}
+
+static int wm8400_dcdc_disable(struct regulator_dev *dev)
+{
+       struct wm8400 *wm8400 = rdev_get_drvdata(dev);
+       int offset = (rdev_get_id(dev) - WM8400_DCDC1) * 2;
+
+       return wm8400_set_bits(wm8400, WM8400_DCDC1_CONTROL_1 + offset,
+                              WM8400_DC1_ENA, 0);
+}
+
+static int wm8400_dcdc_get_voltage(struct regulator_dev *dev)
+{
+       struct wm8400 *wm8400 = rdev_get_drvdata(dev);
+       u16 val;
+       int offset = (rdev_get_id(dev) - WM8400_DCDC1) * 2;
+
+       val = wm8400_reg_read(wm8400, WM8400_DCDC1_CONTROL_1 + offset);
+       val &= WM8400_DC1_VSEL_MASK;
+
+       return 850000 + (25000 * val);
+}
+
+static int wm8400_dcdc_set_voltage(struct regulator_dev *dev,
+                                 int min_uV, int max_uV)
+{
+       struct wm8400 *wm8400 = rdev_get_drvdata(dev);
+       u16 val;
+       int offset = (rdev_get_id(dev) - WM8400_DCDC1) * 2;
+
+       if (min_uV < 850000)
+               return -EINVAL;
+
+       val = (min_uV - 825001) / 25000;
+
+       if (850000 + (25000 * val) > max_uV)
+               return -EINVAL;
+       BUG_ON(850000 + (25000 * val) < min_uV);
+
+       return wm8400_set_bits(wm8400, WM8400_DCDC1_CONTROL_1 + offset,
+                              WM8400_DC1_VSEL_MASK, val);
+}
+
+static unsigned int wm8400_dcdc_get_mode(struct regulator_dev *dev)
+{
+       struct wm8400 *wm8400 = rdev_get_drvdata(dev);
+       int offset = (rdev_get_id(dev) - WM8400_DCDC1) * 2;
+       u16 data[2];
+       int ret;
+
+       ret = wm8400_block_read(wm8400, WM8400_DCDC1_CONTROL_1 + offset, 2,
+                               data);
+       if (ret != 0)
+               return 0;
+
+       /* Datasheet: hibernate */
+       if (data[0] & WM8400_DC1_SLEEP)
+               return REGULATOR_MODE_STANDBY;
+
+       /* Datasheet: standby */
+       if (!(data[0] & WM8400_DC1_ACTIVE))
+               return REGULATOR_MODE_IDLE;
+
+       /* Datasheet: active with or without force PWM */
+       if (data[1] & WM8400_DC1_FRC_PWM)
+               return REGULATOR_MODE_FAST;
+       else
+               return REGULATOR_MODE_NORMAL;
+}
+
+static int wm8400_dcdc_set_mode(struct regulator_dev *dev, unsigned int mode)
+{
+       struct wm8400 *wm8400 = rdev_get_drvdata(dev);
+       int offset = (rdev_get_id(dev) - WM8400_DCDC1) * 2;
+       int ret;
+
+       switch (mode) {
+       case REGULATOR_MODE_FAST:
+               /* Datasheet: active with force PWM */
+               ret = wm8400_set_bits(wm8400, WM8400_DCDC1_CONTROL_2 + offset,
+                                     WM8400_DC1_FRC_PWM, WM8400_DC1_FRC_PWM);
+               if (ret != 0)
+                       return ret;
+
+               return wm8400_set_bits(wm8400, WM8400_DCDC1_CONTROL_1 + offset,
+                                      WM8400_DC1_ACTIVE | WM8400_DC1_SLEEP,
+                                      WM8400_DC1_ACTIVE);
+
+       case REGULATOR_MODE_NORMAL:
+               /* Datasheet: active */
+               ret = wm8400_set_bits(wm8400, WM8400_DCDC1_CONTROL_2 + offset,
+                                     WM8400_DC1_FRC_PWM, 0);
+               if (ret != 0)
+                       return ret;
+
+               return wm8400_set_bits(wm8400, WM8400_DCDC1_CONTROL_1 + offset,
+                                      WM8400_DC1_ACTIVE | WM8400_DC1_SLEEP,
+                                      WM8400_DC1_ACTIVE);
+
+       case REGULATOR_MODE_IDLE:
+               /* Datasheet: standby */
+               ret = wm8400_set_bits(wm8400, WM8400_DCDC1_CONTROL_1 + offset,
+                                     WM8400_DC1_ACTIVE, 0);
+               if (ret != 0)
+                       return ret;
+               return wm8400_set_bits(wm8400, WM8400_DCDC1_CONTROL_1 + offset,
+                                      WM8400_DC1_SLEEP, 0);
+
+       default:
+               return -EINVAL;
+       }
+}
+
+static unsigned int wm8400_dcdc_get_optimum_mode(struct regulator_dev *dev,
+                                                int input_uV, int output_uV,
+                                                int load_uA)
+{
+       return REGULATOR_MODE_NORMAL;
+}
+
+static struct regulator_ops wm8400_dcdc_ops = {
+       .is_enabled = wm8400_dcdc_is_enabled,
+       .enable = wm8400_dcdc_enable,
+       .disable = wm8400_dcdc_disable,
+       .get_voltage = wm8400_dcdc_get_voltage,
+       .set_voltage = wm8400_dcdc_set_voltage,
+       .get_mode = wm8400_dcdc_get_mode,
+       .set_mode = wm8400_dcdc_set_mode,
+       .get_optimum_mode = wm8400_dcdc_get_optimum_mode,
+};
+
+static struct regulator_desc regulators[] = {
+       {
+               .name = "LDO1",
+               .id = WM8400_LDO1,
+               .ops = &wm8400_ldo_ops,
+               .type = REGULATOR_VOLTAGE,
+               .owner = THIS_MODULE,
+       },
+       {
+               .name = "LDO2",
+               .id = WM8400_LDO2,
+               .ops = &wm8400_ldo_ops,
+               .type = REGULATOR_VOLTAGE,
+               .owner = THIS_MODULE,
+       },
+       {
+               .name = "LDO3",
+               .id = WM8400_LDO3,
+               .ops = &wm8400_ldo_ops,
+               .type = REGULATOR_VOLTAGE,
+               .owner = THIS_MODULE,
+       },
+       {
+               .name = "LDO4",
+               .id = WM8400_LDO4,
+               .ops = &wm8400_ldo_ops,
+               .type = REGULATOR_VOLTAGE,
+               .owner = THIS_MODULE,
+       },
+       {
+               .name = "DCDC1",
+               .id = WM8400_DCDC1,
+               .ops = &wm8400_dcdc_ops,
+               .type = REGULATOR_VOLTAGE,
+               .owner = THIS_MODULE,
+       },
+       {
+               .name = "DCDC2",
+               .id = WM8400_DCDC2,
+               .ops = &wm8400_dcdc_ops,
+               .type = REGULATOR_VOLTAGE,
+               .owner = THIS_MODULE,
+       },
+};
+
+static int __init wm8400_regulator_probe(struct platform_device *pdev)
+{
+       struct regulator_dev *rdev;
+
+       rdev = regulator_register(&regulators[pdev->id], &pdev->dev,
+               pdev->dev.driver_data);
+
+       if (IS_ERR(rdev))
+               return PTR_ERR(rdev);
+
+       return 0;
+}
+
+static int __devexit wm8400_regulator_remove(struct platform_device *pdev)
+{
+       struct regulator_dev *rdev = platform_get_drvdata(pdev);
+
+       regulator_unregister(rdev);
+
+       return 0;
+}
+
+static struct platform_driver wm8400_regulator_driver = {
+       .driver = {
+               .name = "wm8400-regulator",
+       },
+       .probe = wm8400_regulator_probe,
+       .remove = __devexit_p(wm8400_regulator_remove),
+};
+
+/**
+ * wm8400_register_regulator - enable software control of a WM8400 regulator
+ *
+ * This function enables software control of a WM8400 regulator via
+ * the regulator API.  It is intended to be called from the
+ * platform_init() callback of the WM8400 MFD driver.
+ *
+ * @param dev      The WM8400 device to operate on.
+ * @param reg      The regulator to control.
+ * @param initdata Regulator initdata for the regulator.
+ */
+int wm8400_register_regulator(struct device *dev, int reg,
+                             struct regulator_init_data *initdata)
+{
+       struct wm8400 *wm8400 = dev->driver_data;
+
+       if (wm8400->regulators[reg].name)
+               return -EBUSY;
+
+       initdata->driver_data = wm8400;
+
+       wm8400->regulators[reg].name = "wm8400-regulator";
+       wm8400->regulators[reg].id = reg;
+       wm8400->regulators[reg].dev.parent = dev;
+       wm8400->regulators[reg].dev.driver_data = wm8400;
+       wm8400->regulators[reg].dev.platform_data = initdata;
+
+       return platform_device_register(&wm8400->regulators[reg]);
+}
+EXPORT_SYMBOL_GPL(wm8400_register_regulator);
+
+static int __init wm8400_regulator_init(void)
+{
+       return platform_driver_register(&wm8400_regulator_driver);
+}
+module_init(wm8400_regulator_init);
+
+static void __exit wm8400_regulator_exit(void)
+{
+       platform_driver_unregister(&wm8400_regulator_driver);
+}
+module_exit(wm8400_regulator_exit);
+
+MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
+MODULE_DESCRIPTION("WM8400 regulator driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:wm8400-regulator");
index b23af0c2a869181e5b4fb5480546b93103d93d40..963ad0b6a4e9da64ff911cba52508e5c39b8085a 100644 (file)
@@ -913,6 +913,92 @@ static inline int cmos_poweroff(struct device *dev)
  * predate even PNPBIOS should set up platform_bus devices.
  */
 
+#ifdef CONFIG_ACPI
+
+#include <linux/acpi.h>
+
+#ifdef CONFIG_PM
+static u32 rtc_handler(void *context)
+{
+       acpi_clear_event(ACPI_EVENT_RTC);
+       acpi_disable_event(ACPI_EVENT_RTC, 0);
+       return ACPI_INTERRUPT_HANDLED;
+}
+
+static inline void rtc_wake_setup(void)
+{
+       acpi_install_fixed_event_handler(ACPI_EVENT_RTC, rtc_handler, NULL);
+       /*
+        * After the RTC handler is installed, the Fixed_RTC event should
+        * be disabled. Only when the RTC alarm is set will it be enabled.
+        */
+       acpi_clear_event(ACPI_EVENT_RTC);
+       acpi_disable_event(ACPI_EVENT_RTC, 0);
+}
+
+static void rtc_wake_on(struct device *dev)
+{
+       acpi_clear_event(ACPI_EVENT_RTC);
+       acpi_enable_event(ACPI_EVENT_RTC, 0);
+}
+
+static void rtc_wake_off(struct device *dev)
+{
+       acpi_disable_event(ACPI_EVENT_RTC, 0);
+}
+#else
+#define rtc_wake_setup()       do{}while(0)
+#define rtc_wake_on            NULL
+#define rtc_wake_off           NULL
+#endif
+
+/* Every ACPI platform has a mc146818 compatible "cmos rtc".  Here we find
+ * its device node and pass extra config data.  This helps its driver use
+ * capabilities that the now-obsolete mc146818 didn't have, and informs it
+ * that this board's RTC is wakeup-capable (per ACPI spec).
+ */
+static struct cmos_rtc_board_info acpi_rtc_info;
+
+static void __devinit
+cmos_wake_setup(struct device *dev)
+{
+       if (acpi_disabled)
+               return;
+
+       rtc_wake_setup();
+       acpi_rtc_info.wake_on = rtc_wake_on;
+       acpi_rtc_info.wake_off = rtc_wake_off;
+
+       /* workaround bug in some ACPI tables */
+       if (acpi_gbl_FADT.month_alarm && !acpi_gbl_FADT.day_alarm) {
+               dev_dbg(dev, "bogus FADT month_alarm (%d)\n",
+                       acpi_gbl_FADT.month_alarm);
+               acpi_gbl_FADT.month_alarm = 0;
+       }
+
+       acpi_rtc_info.rtc_day_alarm = acpi_gbl_FADT.day_alarm;
+       acpi_rtc_info.rtc_mon_alarm = acpi_gbl_FADT.month_alarm;
+       acpi_rtc_info.rtc_century = acpi_gbl_FADT.century;
+
+       /* NOTE:  S4_RTC_WAKE is NOT currently useful to Linux */
+       if (acpi_gbl_FADT.flags & ACPI_FADT_S4_RTC_WAKE)
+               dev_info(dev, "RTC can wake from S4\n");
+
+       dev->platform_data = &acpi_rtc_info;
+
+       /* RTC always wakes from S1/S2/S3, and often S4/STD */
+       device_init_wakeup(dev, 1);
+}
+
+#else
+
+static void __devinit
+cmos_wake_setup(struct device *dev)
+{
+}
+
+#endif
+
 #ifdef CONFIG_PNP
 
 #include <linux/pnp.h>
@@ -920,6 +1006,8 @@ static inline int cmos_poweroff(struct device *dev)
 static int __devinit
 cmos_pnp_probe(struct pnp_dev *pnp, const struct pnp_device_id *id)
 {
+       cmos_wake_setup(&pnp->dev);
+
        if (pnp_port_start(pnp,0) == 0x70 && !pnp_irq_valid(pnp,0))
                /* Some machines contain a PNP entry for the RTC, but
                 * don't define the IRQ. It should always be safe to
@@ -997,6 +1085,7 @@ static struct pnp_driver cmos_pnp_driver = {
 
 static int __init cmos_platform_probe(struct platform_device *pdev)
 {
+       cmos_wake_setup(&pdev->dev);
        return cmos_do_probe(&pdev->dev,
                        platform_get_resource(pdev, IORESOURCE_IO, 0),
                        platform_get_irq(pdev, 0));
@@ -1031,29 +1120,32 @@ static struct platform_driver cmos_platform_driver = {
 
 static int __init cmos_init(void)
 {
+       int retval = 0;
+
 #ifdef CONFIG_PNP
-       if (pnp_platform_devices)
-               return pnp_register_driver(&cmos_pnp_driver);
-       else
-               return platform_driver_probe(&cmos_platform_driver,
-                       cmos_platform_probe);
-#else
-       return platform_driver_probe(&cmos_platform_driver,
-                       cmos_platform_probe);
-#endif /* CONFIG_PNP */
+       pnp_register_driver(&cmos_pnp_driver);
+#endif
+
+       if (!cmos_rtc.dev)
+               retval = platform_driver_probe(&cmos_platform_driver,
+                                              cmos_platform_probe);
+
+       if (retval == 0)
+               return 0;
+
+#ifdef CONFIG_PNP
+       pnp_unregister_driver(&cmos_pnp_driver);
+#endif
+       return retval;
 }
 module_init(cmos_init);
 
 static void __exit cmos_exit(void)
 {
 #ifdef CONFIG_PNP
-       if (pnp_platform_devices)
-               pnp_unregister_driver(&cmos_pnp_driver);
-       else
-               platform_driver_unregister(&cmos_platform_driver);
-#else
+       pnp_unregister_driver(&cmos_pnp_driver);
+#endif
        platform_driver_unregister(&cmos_platform_driver);
-#endif /* CONFIG_PNP */
 }
 module_exit(cmos_exit);
 
index 8f83fc994f508ef6069a1a9cc67eb30b196e945d..f5e618562c5ff9b1be02d014a022ca13e98e10ef 100644 (file)
@@ -2913,7 +2913,7 @@ claw_new_device(struct ccwgroup_device *cgdev)
        if (ret != 0) {
                printk(KERN_WARNING
                        "claw: ccw_device_set_online %s WRITE failed "
-                      "with ret = %d\n", dev_name(&cgdev->cdev[WRITE]->dev)
+                      "with ret = %d\n", dev_name(&cgdev->cdev[WRITE]->dev),
                       ret);
                goto out;
        }
index d3b211af4e1cd259a4d38bec0193a7792090d991..403ecad48d4b95ba5bdb5897803e496862c6b09f 100644 (file)
@@ -1640,6 +1640,7 @@ config ATARI_SCSI
        tristate "Atari native SCSI support"
        depends on ATARI && SCSI
        select SCSI_SPI_ATTRS
+       select NVRAM
        ---help---
          If you have an Atari with built-in NCR5380 SCSI controller (TT,
          Falcon, ...) say Y to get it supported. Of course also, if you have
@@ -1670,14 +1671,6 @@ config ATARI_SCSI_RESET_BOOT
          boot process fractionally longer but may assist recovery from errors
          that leave the devices with SCSI operations partway completed.
 
-config TT_DMA_EMUL
-       bool "Hades SCSI DMA emulator"
-       depends on ATARI_SCSI && HADES
-       help
-         This option enables code which emulates the TT SCSI DMA chip on the
-         Hades. This increases the SCSI transfer rates at least ten times
-         compared to PIO transfers.
-
 config MAC_SCSI
        bool "Macintosh NCR5380 SCSI"
        depends on MAC && SCSI=y
diff --git a/drivers/scsi/atari_dma_emul.c b/drivers/scsi/atari_dma_emul.c
deleted file mode 100644 (file)
index cdc710e..0000000
+++ /dev/null
@@ -1,468 +0,0 @@
-/*
- * atari_dma_emul.c -- TT SCSI DMA emulator for the Hades.
- *
- * Copyright 1997 Wout Klaren <W.Klaren@inter.nl.net>
- *
- * This file is subject to the terms and conditions of the GNU General Public
- * License.  See the file COPYING in the main directory of this archive
- * for more details.
- *
- * This code was written using the Hades TOS source code as a
- * reference. This source code can be found on the home page
- * of Medusa Computer Systems.
- *
- * Version 0.1, 1997-09-24.
- * 
- * This code should be considered experimental. It has only been
- * tested on a Hades with a 68060. It might not work on a Hades
- * with a 68040. Make backups of your hard drives before using
- * this code.
- */
-
-#include <linux/compiler.h>
-#include <asm/thread_info.h>
-#include <asm/uaccess.h>
-
-#define hades_dma_ctrl         (*(unsigned char *) 0xffff8717)
-#define hades_psdm_reg         (*(unsigned char *) 0xffff8741)
-
-#define TRANSFER_SIZE          16
-
-struct m68040_frame {
-       unsigned long  effaddr;  /* effective address */
-       unsigned short ssw;      /* special status word */
-       unsigned short wb3s;     /* write back 3 status */
-       unsigned short wb2s;     /* write back 2 status */
-       unsigned short wb1s;     /* write back 1 status */
-       unsigned long  faddr;    /* fault address */
-       unsigned long  wb3a;     /* write back 3 address */
-       unsigned long  wb3d;     /* write back 3 data */
-       unsigned long  wb2a;     /* write back 2 address */
-       unsigned long  wb2d;     /* write back 2 data */
-       unsigned long  wb1a;     /* write back 1 address */
-       unsigned long  wb1dpd0;  /* write back 1 data/push data 0*/
-       unsigned long  pd1;      /* push data 1*/
-       unsigned long  pd2;      /* push data 2*/
-       unsigned long  pd3;      /* push data 3*/
-};
-
-static void writeback (unsigned short wbs, unsigned long wba,
-                      unsigned long wbd, void *old_buserr)
-{
-       mm_segment_t fs = get_fs();
-       static void *save_buserr;
-
-       __asm__ __volatile__ ("movec.l  %%vbr,%%a0\n\t"
-                             "move.l   %0,8(%%a0)\n\t"
-                             :
-                             : "r" (&&bus_error)
-                             : "a0" );
-
-       save_buserr = old_buserr;
-
-       set_fs (MAKE_MM_SEG(wbs & WBTM_040));
-
-       switch (wbs & WBSIZ_040) {
-           case BA_SIZE_BYTE:
-               put_user (wbd & 0xff, (char *)wba);
-               break;
-           case BA_SIZE_WORD:
-               put_user (wbd & 0xffff, (short *)wba);
-               break;
-           case BA_SIZE_LONG:
-               put_user (wbd, (int *)wba);
-               break;
-       }
-
-       set_fs (fs);
-       return;
-
-bus_error:
-       __asm__ __volatile__ ("cmp.l    %0,2(%%sp)\n\t"
-                             "bcs.s    .jump_old\n\t"
-                             "cmp.l    %1,2(%%sp)\n\t"
-                             "bls.s    .restore_old\n"
-                       ".jump_old:\n\t"
-                             "move.l   %2,-(%%sp)\n\t"
-                             "rts\n"
-                       ".restore_old:\n\t"
-                             "move.l   %%a0,-(%%sp)\n\t"
-                             "movec.l  %%vbr,%%a0\n\t"
-                             "move.l   %2,8(%%a0)\n\t"
-                             "move.l   (%%sp)+,%%a0\n\t"
-                             "rte\n\t"
-                             :
-                             : "i" (writeback), "i" (&&bus_error),
-                               "m" (save_buserr) );
-}
-
-/*
- * static inline void set_restdata_reg(unsigned char *cur_addr)
- *
- * Set the rest data register if necessary.
- */
-
-static inline void set_restdata_reg(unsigned char *cur_addr)
-{
-       if (((long) cur_addr & ~3) != 0)
-               tt_scsi_dma.dma_restdata =
-                       *((unsigned long *) ((long) cur_addr & ~3));
-}
-
-/*
- * void hades_dma_emulator(int irq, void *dummy)
- * 
- * This code emulates TT SCSI DMA on the Hades.
- * 
- * Note the following:
- * 
- * 1. When there is no byte available to read from the SCSI bus, or
- *    when a byte cannot yet bet written to the SCSI bus, a bus
- *    error occurs when reading or writing the pseudo DMA data
- *    register (hades_psdm_reg). We have to catch this bus error
- *    and try again to read or write the byte. If after several tries
- *    we still get a bus error, the interrupt handler is left. When
- *    the byte can be read or written, the interrupt handler is
- *    called again.
- * 
- * 2. The SCSI interrupt must be disabled in this interrupt handler.
- * 
- * 3. If we set the EOP signal, the SCSI controller still expects one
- *    byte to be read or written. Therefore the last byte is transferred
- *    separately, after setting the EOP signal.
- * 
- * 4. When this function is left, the address pointer (start_addr) is
- *    converted to a physical address. Because it points one byte
- *    further than the last transferred byte, it can point outside the
- *    current page. If virt_to_phys() is called with this address we
- *    might get an access error. Therefore virt_to_phys() is called with
- *    start_addr - 1 if the count has reached zero. The result is
- *    increased with one.
- */
-
-static irqreturn_t hades_dma_emulator(int irq, void *dummy)
-{
-       unsigned long dma_base;
-       register unsigned long dma_cnt asm ("d3");
-       static long save_buserr;
-       register unsigned long save_sp asm ("d4");
-       register int tries asm ("d5");
-       register unsigned char *start_addr asm ("a3"), *end_addr asm ("a4");
-       register unsigned char *eff_addr;
-       register unsigned char *psdm_reg;
-       unsigned long rem;
-
-       atari_disable_irq(IRQ_TT_MFP_SCSI);
-
-       /*
-        * Read the dma address and count registers.
-        */
-
-       dma_base = SCSI_DMA_READ_P(dma_addr);
-       dma_cnt = SCSI_DMA_READ_P(dma_cnt);
-
-       /*
-        * Check if DMA is still enabled.
-        */
-
-       if ((tt_scsi_dma.dma_ctrl & 2) == 0)
-       {
-               atari_enable_irq(IRQ_TT_MFP_SCSI);
-               return IRQ_HANDLED;
-       }
-
-       if (dma_cnt == 0)
-       {
-               printk(KERN_NOTICE "DMA emulation: count is zero.\n");
-               tt_scsi_dma.dma_ctrl &= 0xfd;   /* DMA ready. */
-               atari_enable_irq(IRQ_TT_MFP_SCSI);
-               return IRQ_HANDLED;
-       }
-
-       /*
-        * Install new bus error routine.
-        */
-
-       __asm__ __volatile__ ("movec.l  %%vbr,%%a0\n\t"
-                             "move.l   8(%%a0),%0\n\t"
-                             "move.l   %1,8(%%a0)\n\t"
-                             : "=&r" (save_buserr)
-                             : "r" (&&scsi_bus_error)
-                             : "a0" );
-
-       hades_dma_ctrl &= 0xfc;         /* Bus error and EOP off. */
-
-       /*
-        * Save the stack pointer.
-        */
-
-       __asm__ __volatile__ ("move.l   %%sp,%0\n\t"
-                             : "=&r" (save_sp) );
-
-       tries = 100;                    /* Maximum number of bus errors. */
-       start_addr = phys_to_virt(dma_base);
-       end_addr = start_addr + dma_cnt;
-
-scsi_loop:
-       dma_cnt--;
-       rem = dma_cnt & (TRANSFER_SIZE - 1);
-       dma_cnt &= ~(TRANSFER_SIZE - 1);
-       psdm_reg = &hades_psdm_reg;
-
-       if (tt_scsi_dma.dma_ctrl & 1)   /* Read or write? */
-       {
-               /*
-                * SCSI write. Abort when count is zero.
-                */
-
-               switch (rem)
-               {
-               case 0:
-                       while (dma_cnt > 0)
-                       {
-                               dma_cnt -= TRANSFER_SIZE;
-
-                               *psdm_reg = *start_addr++;
-               case 15:
-                               *psdm_reg = *start_addr++;
-               case 14:
-                               *psdm_reg = *start_addr++;
-               case 13:
-                               *psdm_reg = *start_addr++;
-               case 12:
-                               *psdm_reg = *start_addr++;
-               case 11:
-                               *psdm_reg = *start_addr++;
-               case 10:
-                               *psdm_reg = *start_addr++;
-               case 9:
-                               *psdm_reg = *start_addr++;
-               case 8:
-                               *psdm_reg = *start_addr++;
-               case 7:
-                               *psdm_reg = *start_addr++;
-               case 6:
-                               *psdm_reg = *start_addr++;
-               case 5:
-                               *psdm_reg = *start_addr++;
-               case 4:
-                               *psdm_reg = *start_addr++;
-               case 3:
-                               *psdm_reg = *start_addr++;
-               case 2:
-                               *psdm_reg = *start_addr++;
-               case 1:
-                               *psdm_reg = *start_addr++;
-                       }
-               }
-
-               hades_dma_ctrl |= 1;    /* Set EOP. */
-               udelay(10);
-               *psdm_reg = *start_addr++;      /* Dummy byte. */
-               tt_scsi_dma.dma_ctrl &= 0xfd;   /* DMA ready. */
-       }
-       else
-       {
-               /*
-                * SCSI read. Abort when count is zero.
-                */
-
-               switch (rem)
-               {
-               case 0:
-                       while (dma_cnt > 0)
-                       {
-                               dma_cnt -= TRANSFER_SIZE;
-
-                               *start_addr++ = *psdm_reg;
-               case 15:
-                               *start_addr++ = *psdm_reg;
-               case 14:
-                               *start_addr++ = *psdm_reg;
-               case 13:
-                               *start_addr++ = *psdm_reg;
-               case 12:
-                               *start_addr++ = *psdm_reg;
-               case 11:
-                               *start_addr++ = *psdm_reg;
-               case 10:
-                               *start_addr++ = *psdm_reg;
-               case 9:
-                               *start_addr++ = *psdm_reg;
-               case 8:
-                               *start_addr++ = *psdm_reg;
-               case 7:
-                               *start_addr++ = *psdm_reg;
-               case 6:
-                               *start_addr++ = *psdm_reg;
-               case 5:
-                               *start_addr++ = *psdm_reg;
-               case 4:
-                               *start_addr++ = *psdm_reg;
-               case 3:
-                               *start_addr++ = *psdm_reg;
-               case 2:
-                               *start_addr++ = *psdm_reg;
-               case 1:
-                               *start_addr++ = *psdm_reg;
-                       }
-               }
-
-               hades_dma_ctrl |= 1;    /* Set EOP. */
-               udelay(10);
-               *start_addr++ = *psdm_reg;
-               tt_scsi_dma.dma_ctrl &= 0xfd;   /* DMA ready. */
-
-               set_restdata_reg(start_addr);
-       }
-
-       if (start_addr != end_addr)
-               printk(KERN_CRIT "DMA emulation: FATAL: Count is not zero at end of transfer.\n");
-
-       dma_cnt = end_addr - start_addr;
-
-scsi_end:
-       dma_base = (dma_cnt == 0) ? virt_to_phys(start_addr - 1) + 1 :  
-                                   virt_to_phys(start_addr);
-
-       SCSI_DMA_WRITE_P(dma_addr, dma_base);
-       SCSI_DMA_WRITE_P(dma_cnt, dma_cnt);
-
-       /*
-        * Restore old bus error routine.
-        */
-
-       __asm__ __volatile__ ("movec.l  %%vbr,%%a0\n\t"
-                             "move.l   %0,8(%%a0)\n\t"
-                             :
-                             : "r" (save_buserr)
-                             : "a0" );
-
-       atari_enable_irq(IRQ_TT_MFP_SCSI);
-
-       return IRQ_HANDLED;
-
-scsi_bus_error:
-       /*
-        * First check if the bus error is caused by our code.
-        * If not, call the original handler.
-        */
-
-       __asm__ __volatile__ ("cmp.l    %0,2(%%sp)\n\t"
-                             "bcs.s    .old_vector\n\t"
-                             "cmp.l    %1,2(%%sp)\n\t"
-                             "bls.s    .scsi_buserr\n"
-                       ".old_vector:\n\t"
-                             "move.l   %2,-(%%sp)\n\t"
-                             "rts\n"
-                       ".scsi_buserr:\n\t"
-                             :
-                             : "i" (&&scsi_loop), "i" (&&scsi_end),
-                               "m" (save_buserr) );
-
-       if (CPU_IS_060)
-       {
-               /*
-                * Get effective address and restore the stack.
-                */
-
-               __asm__ __volatile__ ("move.l   8(%%sp),%0\n\t"
-                                     "move.l   %1,%%sp\n\t"
-                                     : "=a&" (eff_addr)
-                                     : "r" (save_sp) );
-       }
-       else
-       {
-               register struct m68040_frame *frame;
-
-               __asm__ __volatile__ ("lea      8(%%sp),%0\n\t"
-                                     : "=a&" (frame) );
-
-               if (tt_scsi_dma.dma_ctrl & 1)
-               {
-                       /*
-                        * Bus error while writing.
-                        */
-
-                       if (frame->wb3s & WBV_040)
-                       {
-                               if (frame->wb3a == (long) &hades_psdm_reg)
-                                       start_addr--;
-                               else
-                                       writeback(frame->wb3s, frame->wb3a,
-                                                 frame->wb3d, &&scsi_bus_error);
-                       }
-
-                       if (frame->wb2s & WBV_040)
-                       {
-                               if (frame->wb2a == (long) &hades_psdm_reg)
-                                       start_addr--;
-                               else
-                                       writeback(frame->wb2s, frame->wb2a,
-                                                 frame->wb2d, &&scsi_bus_error);
-                       }
-
-                       if (frame->wb1s & WBV_040)
-                       {
-                               if (frame->wb1a == (long) &hades_psdm_reg)
-                                       start_addr--;
-                       }
-               }
-               else
-               {
-                       /*
-                        * Bus error while reading.
-                        */
-
-                       if (frame->wb3s & WBV_040)
-                               writeback(frame->wb3s, frame->wb3a,
-                                         frame->wb3d, &&scsi_bus_error);
-               }
-
-               eff_addr = (unsigned char *) frame->faddr;
-
-               __asm__ __volatile__ ("move.l   %0,%%sp\n\t"
-                                     :
-                                     : "r" (save_sp) );
-       }
-
-       dma_cnt = end_addr - start_addr;
-
-       if (eff_addr == &hades_psdm_reg)
-       {
-               /*
-                * Bus error occurred while reading the pseudo
-                * DMA register. Time out.
-                */
-
-               tries--;
-
-               if (tries <= 0)
-               {
-                       if ((tt_scsi_dma.dma_ctrl & 1) == 0)    /* Read or write? */
-                               set_restdata_reg(start_addr);
-
-                       if (dma_cnt <= 1)
-                               printk(KERN_CRIT "DMA emulation: Fatal "
-                                      "error while %s the last byte.\n",
-                                      (tt_scsi_dma.dma_ctrl & 1)
-                                      ? "writing" : "reading");
-
-                       goto scsi_end;
-               }
-               else
-                       goto scsi_loop;
-       }
-       else
-       {
-               /*
-                * Bus error during pseudo DMA transfer.
-                * Terminate the DMA transfer.
-                */
-
-               hades_dma_ctrl |= 3;    /* Set EOP and bus error. */
-               if ((tt_scsi_dma.dma_ctrl & 1) == 0)    /* Read or write? */
-                       set_restdata_reg(start_addr);
-               goto scsi_end;
-       }
-}
index f5732d8f67fec793badb30948d369710e9260a55..21fe07f9df87be3ebd5222d8ce15eaea2e04d5d4 100644 (file)
@@ -249,10 +249,6 @@ static int setup_hostid = -1;
 module_param(setup_hostid, int, 0);
 
 
-#if defined(CONFIG_TT_DMA_EMUL)
-#include "atari_dma_emul.c"
-#endif
-
 #if defined(REAL_DMA)
 
 static int scsi_dma_is_ignored_buserr(unsigned char dma_stat)
@@ -695,21 +691,8 @@ int atari_scsi_detect(struct scsi_host_template *host)
 #ifdef REAL_DMA
                tt_scsi_dma.dma_ctrl = 0;
                atari_dma_residual = 0;
-#ifdef CONFIG_TT_DMA_EMUL
-               if (MACH_IS_HADES) {
-                       if (request_irq(IRQ_AUTO_2, hades_dma_emulator,
-                                        IRQ_TYPE_PRIO, "Hades DMA emulator",
-                                        hades_dma_emulator)) {
-                               printk(KERN_ERR "atari_scsi_detect: cannot allocate irq %d, aborting (MACH_IS_HADES)",IRQ_AUTO_2);
-                               free_irq(IRQ_TT_MFP_SCSI, instance);
-                               scsi_unregister(atari_scsi_host);
-                               atari_stram_free(atari_dma_buffer);
-                               atari_dma_buffer = 0;
-                               return 0;
-                       }
-               }
-#endif
-               if (MACH_IS_MEDUSA || MACH_IS_HADES) {
+
+               if (MACH_IS_MEDUSA) {
                        /* While the read overruns (described by Drew Eckhardt in
                         * NCR5380.c) never happened on TTs, they do in fact on the Medusa
                         * (This was the cause why SCSI didn't work right for so long
@@ -1007,11 +990,7 @@ static unsigned long atari_dma_xfer_len(unsigned long wanted_len,
                                        Scsi_Cmnd *cmd, int write_flag)
 {
        unsigned long   possible_len, limit;
-#ifndef CONFIG_TT_DMA_EMUL
-       if (MACH_IS_HADES)
-               /* Hades has no SCSI DMA at all :-( Always force use of PIO */
-               return 0;
-#endif
+
        if (IS_A_TT())
                /* TT SCSI DMA can transfer arbitrary #bytes */
                return wanted_len;
index d4104a3bbe87cd7400a68f083369c41da3396d5b..d3ca7d32abe0ef654f46f9bfd8b6cd00ad977557 100644 (file)
@@ -2969,6 +2969,9 @@ static int __init serial8250_init(void)
                "%d ports, IRQ sharing %sabled\n", nr_uarts,
                share_irqs ? "en" : "dis");
 
+       for (i = 0; i < NR_IRQS; i++)
+               spin_lock_init(&irq_lists[i].lock);
+
 #ifdef CONFIG_SPARC
        ret = sunserial_register_minors(&serial8250_reg, UART_NR);
 #else
index 9cbff84b787d9e4cf17d8ebd7d2dc1822d628f5f..da91bb16da8a1c530b7d4e7edccaef928876fee6 100644 (file)
@@ -1855,8 +1855,6 @@ static int fbcon_scroll(struct vc_data *vc, int t, int b, int dir,
        struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]];
        struct display *p = &fb_display[vc->vc_num];
        int scroll_partial = info->flags & FBINFO_PARTIAL_PAN_OK;
-       unsigned short saved_ec;
-       int ret;
 
        if (fbcon_is_inactive(vc, info))
                return -EINVAL;
@@ -1869,11 +1867,6 @@ static int fbcon_scroll(struct vc_data *vc, int t, int b, int dir,
         *           whole screen (prevents flicker).
         */
 
-       saved_ec = vc->vc_video_erase_char;
-       vc->vc_video_erase_char = vc->vc_scrl_erase_char;
-
-       ret = 0;
-
        switch (dir) {
        case SM_UP:
                if (count > vc->vc_rows)        /* Maximum realistic size */
@@ -1890,9 +1883,9 @@ static int fbcon_scroll(struct vc_data *vc, int t, int b, int dir,
                        scr_memsetw((unsigned short *) (vc->vc_origin +
                                                        vc->vc_size_row *
                                                        (b - count)),
-                                   vc->vc_scrl_erase_char,
+                                   vc->vc_video_erase_char,
                                    vc->vc_size_row * count);
-                       ret = 1;
+                       return 1;
                        break;
 
                case SCROLL_WRAP_MOVE:
@@ -1962,10 +1955,9 @@ static int fbcon_scroll(struct vc_data *vc, int t, int b, int dir,
                        scr_memsetw((unsigned short *) (vc->vc_origin +
                                                        vc->vc_size_row *
                                                        (b - count)),
-                                   vc->vc_scrl_erase_char,
+                                   vc->vc_video_erase_char,
                                    vc->vc_size_row * count);
-                       ret = 1;
-                       break;
+                       return 1;
                }
                break;
 
@@ -1982,9 +1974,9 @@ static int fbcon_scroll(struct vc_data *vc, int t, int b, int dir,
                        scr_memsetw((unsigned short *) (vc->vc_origin +
                                                        vc->vc_size_row *
                                                        t),
-                                   vc->vc_scrl_erase_char,
+                                   vc->vc_video_erase_char,
                                    vc->vc_size_row * count);
-                       ret = 1;
+                       return 1;
                        break;
 
                case SCROLL_WRAP_MOVE:
@@ -2052,15 +2044,12 @@ static int fbcon_scroll(struct vc_data *vc, int t, int b, int dir,
                        scr_memsetw((unsigned short *) (vc->vc_origin +
                                                        vc->vc_size_row *
                                                        t),
-                                   vc->vc_scrl_erase_char,
+                                   vc->vc_video_erase_char,
                                    vc->vc_size_row * count);
-                       ret = 1;
-                       break;
+                       return 1;
                }
-               break;
        }
-       vc->vc_video_erase_char = saved_ec;
-       return ret;
+       return 0;
 }
 
 
@@ -2522,9 +2511,6 @@ static int fbcon_do_set_font(struct vc_data *vc, int w, int h,
                        c = vc->vc_video_erase_char;
                        vc->vc_video_erase_char =
                            ((c & 0xfe00) >> 1) | (c & 0xff);
-                       c = vc->vc_scrl_erase_char;
-                       vc->vc_scrl_erase_char =
-                           ((c & 0xFE00) >> 1) | (c & 0xFF);
                        vc->vc_attr >>= 1;
                }
        } else if (!vc->vc_hi_font_mask && cnt == 512) {
@@ -2555,14 +2541,9 @@ static int fbcon_do_set_font(struct vc_data *vc, int w, int h,
                        if (vc->vc_can_do_color) {
                                vc->vc_video_erase_char =
                                    ((c & 0xff00) << 1) | (c & 0xff);
-                               c = vc->vc_scrl_erase_char;
-                               vc->vc_scrl_erase_char =
-                                   ((c & 0xFF00) << 1) | (c & 0xFF);
                                vc->vc_attr <<= 1;
-                       } else {
+                       } else
                                vc->vc_video_erase_char = c & ~0x100;
-                               vc->vc_scrl_erase_char = c & ~0x100;
-                       }
                }
 
        }
index 9901064199bd165599a163deacf45737f7d210c2..dd3eaaad44417781a6cef9a1537ca055812d3d65 100644 (file)
@@ -533,7 +533,7 @@ static void mdacon_cursor(struct vc_data *c, int mode)
 
 static int mdacon_scroll(struct vc_data *c, int t, int b, int dir, int lines)
 {
-       u16 eattr = mda_convert_attr(c->vc_scrl_erase_char);
+       u16 eattr = mda_convert_attr(c->vc_video_erase_char);
 
        if (!lines)
                return 0;
index 4055dbdd1b428e0dd978704204e0b182f364301d..491c1c1baf4ccd652eadf85079544686064e3228 100644 (file)
@@ -170,12 +170,12 @@ static int sticon_scroll(struct vc_data *conp, int t, int b, int dir, int count)
     switch (dir) {
     case SM_UP:
        sti_bmove(sti, t + count, 0, t, 0, b - t - count, conp->vc_cols);
-       sti_clear(sti, b - count, 0, count, conp->vc_cols, conp->vc_scrl_erase_char);
+       sti_clear(sti, b - count, 0, count, conp->vc_cols, conp->vc_video_erase_char);
        break;
 
     case SM_DOWN:
        sti_bmove(sti, t, 0, t + count, 0, b - t - count, conp->vc_cols);
-       sti_clear(sti, t, 0, count, conp->vc_cols, conp->vc_scrl_erase_char);
+       sti_clear(sti, t, 0, count, conp->vc_cols, conp->vc_video_erase_char);
        break;
     }
 
index bd1f57b259d9ce50d470b192e021ad95461b8e72..6df29a62d7202552d63ced7bfc588ef8854d6c49 100644 (file)
@@ -1350,7 +1350,7 @@ static int vgacon_scroll(struct vc_data *c, int t, int b, int dir,
                } else
                        c->vc_origin += delta;
                scr_memsetw((u16 *) (c->vc_origin + c->vc_screenbuf_size -
-                                    delta), c->vc_scrl_erase_char,
+                                    delta), c->vc_video_erase_char,
                            delta);
        } else {
                if (oldo - delta < vga_vram_base) {
@@ -1363,7 +1363,7 @@ static int vgacon_scroll(struct vc_data *c, int t, int b, int dir,
                } else
                        c->vc_origin -= delta;
                c->vc_scr_end = c->vc_origin + c->vc_screenbuf_size;
-               scr_memsetw((u16 *) (c->vc_origin), c->vc_scrl_erase_char,
+               scr_memsetw((u16 *) (c->vc_origin), c->vc_video_erase_char,
                            delta);
        }
        c->vc_scr_end = c->vc_origin + c->vc_screenbuf_size;
index f54a157a029689fae2556c2f3989f6475e123ec7..9e9d70c02a07c9a8876f71c3445ac5092c563810 100644 (file)
@@ -220,17 +220,16 @@ config JBD
        tristate
        help
          This is a generic journalling layer for block devices.  It is
-         currently used by the ext3 and OCFS2 file systems, but it could
-         also be used to add journal support to other file systems or block
+         currently used by the ext3 file system, but it could also be
+         used to add journal support to other file systems or block
          devices such as RAID or LVM.
 
-         If you are using the ext3 or OCFS2 file systems, you need to
-         say Y here. If you are not using ext3 OCFS2 then you will probably
-         want to say N.
+         If you are using the ext3 file system, you need to say Y here.
+         If you are not using ext3 then you will probably want to say N.
 
          To compile this device as a module, choose M here: the module will be
-         called jbd.  If you are compiling ext3 or OCFS2 into the kernel,
-         you cannot compile this code as a module.
+         called jbd.  If you are compiling ext3 into the kernel, you
+         cannot compile this code as a module.
 
 config JBD_DEBUG
        bool "JBD (ext3) debugging support"
@@ -254,15 +253,16 @@ config JBD2
        help
          This is a generic journaling layer for block devices that support
          both 32-bit and 64-bit block numbers.  It is currently used by
-         the ext4 filesystem, but it could also be used to add
+         the ext4 and OCFS2 filesystems, but it could also be used to add
          journal support to other file systems or block devices such
          as RAID or LVM.
 
-         If you are using ext4, you need to say Y here. If you are not
-         using ext4 then you will probably want to say N.
+         If you are using ext4 or OCFS2, you need to say Y here.
+         If you are not using ext4 or OCFS2 then you will
+         probably want to say N.
 
          To compile this device as a module, choose M here. The module will be
-         called jbd2.  If you are compiling ext4 into the kernel,
+         called jbd2.  If you are compiling ext4 or OCFS2 into the kernel,
          you cannot compile this code as a module.
 
 config JBD2_DEBUG
@@ -433,6 +433,14 @@ config FS_POSIX_ACL
        bool
        default n
 
+config FILE_LOCKING
+       bool "Enable POSIX file locking API" if EMBEDDED
+       default y
+       help
+         This option enables standard file locking support, required
+          for filesystems like NFS and for the flock() system
+          call. Disabling this option saves about 11k.
+
 source "fs/xfs/Kconfig"
 source "fs/gfs2/Kconfig"
 
@@ -440,7 +448,7 @@ config OCFS2_FS
        tristate "OCFS2 file system support"
        depends on NET && SYSFS
        select CONFIGFS_FS
-       select JBD
+       select JBD2
        select CRC32
        help
          OCFS2 is a general purpose extent based shared disk cluster file
@@ -511,6 +519,16 @@ config OCFS2_DEBUG_FS
          this option for debugging only as it is likely to decrease
          performance of the filesystem.
 
+config OCFS2_COMPAT_JBD
+       bool "Use JBD for compatibility"
+       depends on OCFS2_FS
+       default n
+       select JBD
+       help
+         The ocfs2 filesystem now uses JBD2 for its journalling.  JBD2
+         is backwards compatible with JBD.  It is safe to say N here.
+         However, if you really want to use the original JBD, say Y here.
+
 endif # BLOCK
 
 config DNOTIFY
@@ -1779,6 +1797,28 @@ config SUNRPC_XPRT_RDMA
 
          If unsure, say N.
 
+config SUNRPC_REGISTER_V4
+       bool "Register local RPC services via rpcbind v4 (EXPERIMENTAL)"
+       depends on SUNRPC && EXPERIMENTAL
+       default n
+       help
+         Sun added support for registering RPC services at an IPv6
+         address by creating two new versions of the rpcbind protocol
+         (RFC 1833).
+
+         This option enables support in the kernel RPC server for
+         registering kernel RPC services via version 4 of the rpcbind
+         protocol.  If you enable this option, you must run a portmapper
+         daemon that supports rpcbind protocol version 4.
+
+         Serving NFS over IPv6 from knfsd (the kernel's NFS server)
+         requires that you enable this option and use a portmapper that
+         supports rpcbind version 4.
+
+         If unsure, say N to get traditional behavior (register kernel
+         RPC services using only rpcbind version 2).  Distributions
+         using the legacy Linux portmapper daemon must say N here.
+
 config RPCSEC_GSS_KRB5
        tristate "Secure RPC: Kerberos V mechanism (EXPERIMENTAL)"
        depends on SUNRPC && EXPERIMENTAL
index de404b00eb0cad255e28667472d7d2d75c43635a..b6f27dc26b7254a25c4dbf5223a2d24ce67f46d4 100644 (file)
@@ -7,7 +7,7 @@
 
 obj-y :=       open.o read_write.o file_table.o super.o \
                char_dev.o stat.o exec.o pipe.o namei.o fcntl.o \
-               ioctl.o readdir.o select.o fifo.o locks.o dcache.o inode.o \
+               ioctl.o readdir.o select.o fifo.o dcache.o inode.o \
                attr.o bad_inode.o file.o filesystems.o namespace.o aio.o \
                seq_file.o xattr.o libfs.o fs-writeback.o \
                pnode.o drop_caches.o splice.o sync.o utimes.o \
@@ -27,6 +27,7 @@ obj-$(CONFIG_ANON_INODES)     += anon_inodes.o
 obj-$(CONFIG_SIGNALFD)         += signalfd.o
 obj-$(CONFIG_TIMERFD)          += timerfd.o
 obj-$(CONFIG_EVENTFD)          += eventfd.o
+obj-$(CONFIG_FILE_LOCKING)      += locks.o
 obj-$(CONFIG_COMPAT)           += compat.o compat_ioctl.o
 
 nfsd-$(CONFIG_NFSD)            := nfsctl.o
index 7725a0a9a5553bf2db33b0fc81b1f92dbe7b48fc..97f6073ab339b327d4a5448224d18886eb3f42f7 100644 (file)
@@ -5,6 +5,6 @@
 obj-$(CONFIG_LOCKD) += lockd.o
 
 lockd-objs-y := clntlock.o clntproc.o host.o svc.o svclock.o svcshare.o \
-               svcproc.o svcsubs.o mon.o xdr.o
+               svcproc.o svcsubs.o mon.o xdr.o grace.o
 lockd-objs-$(CONFIG_LOCKD_V4) += xdr4.o svc4proc.o
 lockd-objs                   := $(lockd-objs-y)
index 0b45fd3a4bfd6dfdb9e6e8d426a46489954822bb..8307dd64bf46ca746d9c973f209626de49627d17 100644 (file)
@@ -54,14 +54,13 @@ struct nlm_host *nlmclnt_init(const struct nlmclnt_initdata *nlm_init)
        u32 nlm_version = (nlm_init->nfs_version == 2) ? 1 : 4;
        int status;
 
-       status = lockd_up(nlm_init->protocol);
+       status = lockd_up();
        if (status < 0)
                return ERR_PTR(status);
 
-       host = nlmclnt_lookup_host((struct sockaddr_in *)nlm_init->address,
+       host = nlmclnt_lookup_host(nlm_init->address, nlm_init->addrlen,
                                   nlm_init->protocol, nlm_version,
-                                  nlm_init->hostname,
-                                  strlen(nlm_init->hostname));
+                                  nlm_init->hostname);
        if (host == NULL) {
                lockd_down();
                return ERR_PTR(-ENOLCK);
@@ -142,7 +141,7 @@ int nlmclnt_block(struct nlm_wait *block, struct nlm_rqst *req, long timeout)
 /*
  * The server lockd has called us back to tell us the lock was granted
  */
-__be32 nlmclnt_grant(const struct sockaddr_in *addr, const struct nlm_lock *lock)
+__be32 nlmclnt_grant(const struct sockaddr *addr, const struct nlm_lock *lock)
 {
        const struct file_lock *fl = &lock->fl;
        const struct nfs_fh *fh = &lock->fh;
@@ -166,7 +165,7 @@ __be32 nlmclnt_grant(const struct sockaddr_in *addr, const struct nlm_lock *lock
                 */
                if (fl_blocked->fl_u.nfs_fl.owner->pid != lock->svid)
                        continue;
-               if (!nlm_cmp_addr(&block->b_host->h_addr, addr))
+               if (!nlm_cmp_addr(nlm_addr(block->b_host), addr))
                        continue;
                if (nfs_compare_fh(NFS_FH(fl_blocked->fl_file->f_path.dentry->d_inode) ,fh) != 0)
                        continue;
@@ -216,7 +215,7 @@ reclaimer(void *ptr)
        /* This one ensures that our parent doesn't terminate while the
         * reclaim is in progress */
        lock_kernel();
-       lockd_up(0); /* note: this cannot fail as lockd is already running */
+       lockd_up();     /* note: this cannot fail as lockd is already running */
 
        dprintk("lockd: reclaiming locks for host %s\n", host->h_name);
 
diff --git a/fs/lockd/grace.c b/fs/lockd/grace.c
new file mode 100644 (file)
index 0000000..183cc1f
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * Common code for control of lockd and nfsv4 grace periods.
+ */
+
+#include <linux/module.h>
+#include <linux/lockd/bind.h>
+
+static LIST_HEAD(grace_list);
+static DEFINE_SPINLOCK(grace_lock);
+
+/**
+ * locks_start_grace
+ * @lm: who this grace period is for
+ *
+ * A grace period is a period during which locks should not be given
+ * out.  Currently grace periods are only enforced by the two lock
+ * managers (lockd and nfsd), using the locks_in_grace() function to
+ * check when they are in a grace period.
+ *
+ * This function is called to start a grace period.
+ */
+void locks_start_grace(struct lock_manager *lm)
+{
+       spin_lock(&grace_lock);
+       list_add(&lm->list, &grace_list);
+       spin_unlock(&grace_lock);
+}
+EXPORT_SYMBOL_GPL(locks_start_grace);
+
+/**
+ * locks_end_grace
+ * @lm: who this grace period is for
+ *
+ * Call this function to state that the given lock manager is ready to
+ * resume regular locking.  The grace period will not end until all lock
+ * managers that called locks_start_grace() also call locks_end_grace().
+ * Note that callers count on it being safe to call this more than once,
+ * and the second call should be a no-op.
+ */
+void locks_end_grace(struct lock_manager *lm)
+{
+       spin_lock(&grace_lock);
+       list_del_init(&lm->list);
+       spin_unlock(&grace_lock);
+}
+EXPORT_SYMBOL_GPL(locks_end_grace);
+
+/**
+ * locks_in_grace
+ *
+ * Lock managers call this function to determine when it is OK for them
+ * to answer ordinary lock requests, and when they should accept only
+ * lock reclaims.
+ */
+int locks_in_grace(void)
+{
+       return !list_empty(&grace_list);
+}
+EXPORT_SYMBOL_GPL(locks_in_grace);
index a17664c7eacc4542ee8a27a6543c4b548f6efc62..9fd8889097b728b735150ff82b1ff123a503dac7 100644 (file)
 #include <linux/types.h>
 #include <linux/slab.h>
 #include <linux/in.h>
+#include <linux/in6.h>
 #include <linux/sunrpc/clnt.h>
 #include <linux/sunrpc/svc.h>
 #include <linux/lockd/lockd.h>
 #include <linux/lockd/sm_inter.h>
 #include <linux/mutex.h>
 
+#include <net/ipv6.h>
 
 #define NLMDBG_FACILITY                NLMDBG_HOSTCACHE
 #define NLM_HOST_NRHASH                32
-#define NLM_ADDRHASH(addr)     (ntohl(addr) & (NLM_HOST_NRHASH-1))
 #define NLM_HOST_REBIND                (60 * HZ)
 #define NLM_HOST_EXPIRE                (300 * HZ)
 #define NLM_HOST_COLLECT       (120 * HZ)
@@ -30,42 +31,115 @@ static unsigned long               next_gc;
 static int                     nrhosts;
 static DEFINE_MUTEX(nlm_host_mutex);
 
-
 static void                    nlm_gc_hosts(void);
-static struct nsm_handle *     __nsm_find(const struct sockaddr_in *,
-                                       const char *, unsigned int, int);
-static struct nsm_handle *     nsm_find(const struct sockaddr_in *sin,
-                                        const char *hostname,
-                                        unsigned int hostname_len);
+static struct nsm_handle       *nsm_find(const struct sockaddr *sap,
+                                               const size_t salen,
+                                               const char *hostname,
+                                               const size_t hostname_len,
+                                               const int create);
+
+struct nlm_lookup_host_info {
+       const int               server;         /* search for server|client */
+       const struct sockaddr   *sap;           /* address to search for */
+       const size_t            salen;          /* it's length */
+       const unsigned short    protocol;       /* transport to search for*/
+       const u32               version;        /* NLM version to search for */
+       const char              *hostname;      /* remote's hostname */
+       const size_t            hostname_len;   /* it's length */
+       const struct sockaddr   *src_sap;       /* our address (optional) */
+       const size_t            src_len;        /* it's length */
+};
+
+/*
+ * Hash function must work well on big- and little-endian platforms
+ */
+static unsigned int __nlm_hash32(const __be32 n)
+{
+       unsigned int hash = (__force u32)n ^ ((__force u32)n >> 16);
+       return hash ^ (hash >> 8);
+}
+
+static unsigned int __nlm_hash_addr4(const struct sockaddr *sap)
+{
+       const struct sockaddr_in *sin = (struct sockaddr_in *)sap;
+       return __nlm_hash32(sin->sin_addr.s_addr);
+}
+
+static unsigned int __nlm_hash_addr6(const struct sockaddr *sap)
+{
+       const struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sap;
+       const struct in6_addr addr = sin6->sin6_addr;
+       return __nlm_hash32(addr.s6_addr32[0]) ^
+              __nlm_hash32(addr.s6_addr32[1]) ^
+              __nlm_hash32(addr.s6_addr32[2]) ^
+              __nlm_hash32(addr.s6_addr32[3]);
+}
+
+static unsigned int nlm_hash_address(const struct sockaddr *sap)
+{
+       unsigned int hash;
+
+       switch (sap->sa_family) {
+       case AF_INET:
+               hash = __nlm_hash_addr4(sap);
+               break;
+       case AF_INET6:
+               hash = __nlm_hash_addr6(sap);
+               break;
+       default:
+               hash = 0;
+       }
+       return hash & (NLM_HOST_NRHASH - 1);
+}
+
+static void nlm_clear_port(struct sockaddr *sap)
+{
+       switch (sap->sa_family) {
+       case AF_INET:
+               ((struct sockaddr_in *)sap)->sin_port = 0;
+               break;
+       case AF_INET6:
+               ((struct sockaddr_in6 *)sap)->sin6_port = 0;
+               break;
+       }
+}
+
+static void nlm_display_address(const struct sockaddr *sap,
+                               char *buf, const size_t len)
+{
+       const struct sockaddr_in *sin = (struct sockaddr_in *)sap;
+       const struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sap;
+
+       switch (sap->sa_family) {
+       case AF_UNSPEC:
+               snprintf(buf, len, "unspecified");
+               break;
+       case AF_INET:
+               snprintf(buf, len, NIPQUAD_FMT, NIPQUAD(sin->sin_addr.s_addr));
+               break;
+       case AF_INET6:
+               if (ipv6_addr_v4mapped(&sin6->sin6_addr))
+                       snprintf(buf, len, NIPQUAD_FMT,
+                                NIPQUAD(sin6->sin6_addr.s6_addr32[3]));
+               else
+                       snprintf(buf, len, NIP6_FMT, NIP6(sin6->sin6_addr));
+               break;
+       default:
+               snprintf(buf, len, "unsupported address family");
+               break;
+       }
+}
 
 /*
  * Common host lookup routine for server & client
  */
-static struct nlm_host *nlm_lookup_host(int server,
-                                       const struct sockaddr_in *sin,
-                                       int proto, u32 version,
-                                       const char *hostname,
-                                       unsigned int hostname_len,
-                                       const struct sockaddr_in *ssin)
+static struct nlm_host *nlm_lookup_host(struct nlm_lookup_host_info *ni)
 {
        struct hlist_head *chain;
        struct hlist_node *pos;
        struct nlm_host *host;
        struct nsm_handle *nsm = NULL;
-       int             hash;
-
-       dprintk("lockd: nlm_lookup_host("NIPQUAD_FMT"->"NIPQUAD_FMT
-                       ", p=%d, v=%u, my role=%s, name=%.*s)\n",
-                       NIPQUAD(ssin->sin_addr.s_addr),
-                       NIPQUAD(sin->sin_addr.s_addr), proto, version,
-                       server? "server" : "client",
-                       hostname_len,
-                       hostname? hostname : "<none>");
 
-
-       hash = NLM_ADDRHASH(sin->sin_addr.s_addr);
-
-       /* Lock hash table */
        mutex_lock(&nlm_host_mutex);
 
        if (time_after_eq(jiffies, next_gc))
@@ -78,22 +152,22 @@ static struct nlm_host *nlm_lookup_host(int server,
         * different NLM rpc_clients into one single nlm_host object.
         * This would allow us to have one nlm_host per address.
         */
-       chain = &nlm_hosts[hash];
+       chain = &nlm_hosts[nlm_hash_address(ni->sap)];
        hlist_for_each_entry(host, pos, chain, h_hash) {
-               if (!nlm_cmp_addr(&host->h_addr, sin))
+               if (!nlm_cmp_addr(nlm_addr(host), ni->sap))
                        continue;
 
                /* See if we have an NSM handle for this client */
                if (!nsm)
                        nsm = host->h_nsmhandle;
 
-               if (host->h_proto != proto)
+               if (host->h_proto != ni->protocol)
                        continue;
-               if (host->h_version != version)
+               if (host->h_version != ni->version)
                        continue;
-               if (host->h_server != server)
+               if (host->h_server != ni->server)
                        continue;
-               if (!nlm_cmp_addr(&host->h_saddr, ssin))
+               if (!nlm_cmp_addr(nlm_srcaddr(host), ni->src_sap))
                        continue;
 
                /* Move to head of hash chain. */
@@ -101,30 +175,41 @@ static struct nlm_host *nlm_lookup_host(int server,
                hlist_add_head(&host->h_hash, chain);
 
                nlm_get_host(host);
+               dprintk("lockd: nlm_lookup_host found host %s (%s)\n",
+                               host->h_name, host->h_addrbuf);
                goto out;
        }
-       if (nsm)
-               atomic_inc(&nsm->sm_count);
-
-       host = NULL;
 
-       /* Sadly, the host isn't in our hash table yet. See if
-        * we have an NSM handle for it. If not, create one.
+       /*
+        * The host wasn't in our hash table.  If we don't
+        * have an NSM handle for it yet, create one.
         */
-       if (!nsm && !(nsm = nsm_find(sin, hostname, hostname_len)))
-               goto out;
+       if (nsm)
+               atomic_inc(&nsm->sm_count);
+       else {
+               host = NULL;
+               nsm = nsm_find(ni->sap, ni->salen,
+                               ni->hostname, ni->hostname_len, 1);
+               if (!nsm) {
+                       dprintk("lockd: nlm_lookup_host failed; "
+                               "no nsm handle\n");
+                       goto out;
+               }
+       }
 
        host = kzalloc(sizeof(*host), GFP_KERNEL);
        if (!host) {
                nsm_release(nsm);
+               dprintk("lockd: nlm_lookup_host failed; no memory\n");
                goto out;
        }
        host->h_name       = nsm->sm_name;
-       host->h_addr       = *sin;
-       host->h_addr.sin_port = 0;      /* ouch! */
-       host->h_saddr      = *ssin;
-       host->h_version    = version;
-       host->h_proto      = proto;
+       memcpy(nlm_addr(host), ni->sap, ni->salen);
+       host->h_addrlen = ni->salen;
+       nlm_clear_port(nlm_addr(host));
+       memcpy(nlm_srcaddr(host), ni->src_sap, ni->src_len);
+       host->h_version    = ni->version;
+       host->h_proto      = ni->protocol;
        host->h_rpcclnt    = NULL;
        mutex_init(&host->h_mutex);
        host->h_nextrebind = jiffies + NLM_HOST_REBIND;
@@ -135,7 +220,7 @@ static struct nlm_host *nlm_lookup_host(int server,
        host->h_state      = 0;                 /* pseudo NSM state */
        host->h_nsmstate   = 0;                 /* real NSM state */
        host->h_nsmhandle  = nsm;
-       host->h_server     = server;
+       host->h_server     = ni->server;
        hlist_add_head(&host->h_hash, chain);
        INIT_LIST_HEAD(&host->h_lockowners);
        spin_lock_init(&host->h_lock);
@@ -143,6 +228,15 @@ static struct nlm_host *nlm_lookup_host(int server,
        INIT_LIST_HEAD(&host->h_reclaim);
 
        nrhosts++;
+
+       nlm_display_address((struct sockaddr *)&host->h_addr,
+                               host->h_addrbuf, sizeof(host->h_addrbuf));
+       nlm_display_address((struct sockaddr *)&host->h_srcaddr,
+                               host->h_srcaddrbuf, sizeof(host->h_srcaddrbuf));
+
+       dprintk("lockd: nlm_lookup_host created host %s\n",
+                       host->h_name);
+
 out:
        mutex_unlock(&nlm_host_mutex);
        return host;
@@ -170,33 +264,103 @@ nlm_destroy_host(struct nlm_host *host)
        kfree(host);
 }
 
-/*
- * Find an NLM server handle in the cache. If there is none, create it.
+/**
+ * nlmclnt_lookup_host - Find an NLM host handle matching a remote server
+ * @sap: network address of server
+ * @salen: length of server address
+ * @protocol: transport protocol to use
+ * @version: NLM protocol version
+ * @hostname: '\0'-terminated hostname of server
+ *
+ * Returns an nlm_host structure that matches the passed-in
+ * [server address, transport protocol, NLM version, server hostname].
+ * If one doesn't already exist in the host cache, a new handle is
+ * created and returned.
  */
-struct nlm_host *nlmclnt_lookup_host(const struct sockaddr_in *sin,
-                                    int proto, u32 version,
-                                    const char *hostname,
-                                    unsigned int hostname_len)
+struct nlm_host *nlmclnt_lookup_host(const struct sockaddr *sap,
+                                    const size_t salen,
+                                    const unsigned short protocol,
+                                    const u32 version, const char *hostname)
 {
-       struct sockaddr_in ssin = {0};
-
-       return nlm_lookup_host(0, sin, proto, version,
-                              hostname, hostname_len, &ssin);
+       const struct sockaddr source = {
+               .sa_family      = AF_UNSPEC,
+       };
+       struct nlm_lookup_host_info ni = {
+               .server         = 0,
+               .sap            = sap,
+               .salen          = salen,
+               .protocol       = protocol,
+               .version        = version,
+               .hostname       = hostname,
+               .hostname_len   = strlen(hostname),
+               .src_sap        = &source,
+               .src_len        = sizeof(source),
+       };
+
+       dprintk("lockd: %s(host='%s', vers=%u, proto=%s)\n", __func__,
+                       (hostname ? hostname : "<none>"), version,
+                       (protocol == IPPROTO_UDP ? "udp" : "tcp"));
+
+       return nlm_lookup_host(&ni);
 }
 
-/*
- * Find an NLM client handle in the cache. If there is none, create it.
+/**
+ * nlmsvc_lookup_host - Find an NLM host handle matching a remote client
+ * @rqstp: incoming NLM request
+ * @hostname: name of client host
+ * @hostname_len: length of client hostname
+ *
+ * Returns an nlm_host structure that matches the [client address,
+ * transport protocol, NLM version, client hostname] of the passed-in
+ * NLM request.  If one doesn't already exist in the host cache, a
+ * new handle is created and returned.
+ *
+ * Before possibly creating a new nlm_host, construct a sockaddr
+ * for a specific source address in case the local system has
+ * multiple network addresses.  The family of the address in
+ * rq_daddr is guaranteed to be the same as the family of the
+ * address in rq_addr, so it's safe to use the same family for
+ * the source address.
  */
-struct nlm_host *
-nlmsvc_lookup_host(struct svc_rqst *rqstp,
-                       const char *hostname, unsigned int hostname_len)
+struct nlm_host *nlmsvc_lookup_host(const struct svc_rqst *rqstp,
+                                   const char *hostname,
+                                   const size_t hostname_len)
 {
-       struct sockaddr_in ssin = {0};
+       struct sockaddr_in sin = {
+               .sin_family     = AF_INET,
+       };
+       struct sockaddr_in6 sin6 = {
+               .sin6_family    = AF_INET6,
+       };
+       struct nlm_lookup_host_info ni = {
+               .server         = 1,
+               .sap            = svc_addr(rqstp),
+               .salen          = rqstp->rq_addrlen,
+               .protocol       = rqstp->rq_prot,
+               .version        = rqstp->rq_vers,
+               .hostname       = hostname,
+               .hostname_len   = hostname_len,
+               .src_len        = rqstp->rq_addrlen,
+       };
+
+       dprintk("lockd: %s(host='%*s', vers=%u, proto=%s)\n", __func__,
+                       (int)hostname_len, hostname, rqstp->rq_vers,
+                       (rqstp->rq_prot == IPPROTO_UDP ? "udp" : "tcp"));
+
+       switch (ni.sap->sa_family) {
+       case AF_INET:
+               sin.sin_addr.s_addr = rqstp->rq_daddr.addr.s_addr;
+               ni.src_sap = (struct sockaddr *)&sin;
+               break;
+       case AF_INET6:
+               ipv6_addr_copy(&sin6.sin6_addr, &rqstp->rq_daddr.addr6);
+               ni.src_sap = (struct sockaddr *)&sin6;
+               break;
+       default:
+               return NULL;
+       }
 
-       ssin.sin_addr = rqstp->rq_daddr.addr;
-       return nlm_lookup_host(1, svc_addr_in(rqstp),
-                              rqstp->rq_prot, rqstp->rq_vers,
-                              hostname, hostname_len, &ssin);
+       return nlm_lookup_host(&ni);
 }
 
 /*
@@ -207,9 +371,8 @@ nlm_bind_host(struct nlm_host *host)
 {
        struct rpc_clnt *clnt;
 
-       dprintk("lockd: nlm_bind_host("NIPQUAD_FMT"->"NIPQUAD_FMT")\n",
-                       NIPQUAD(host->h_saddr.sin_addr),
-                       NIPQUAD(host->h_addr.sin_addr));
+       dprintk("lockd: nlm_bind_host %s (%s), my addr=%s\n",
+                       host->h_name, host->h_addrbuf, host->h_srcaddrbuf);
 
        /* Lock host handle */
        mutex_lock(&host->h_mutex);
@@ -221,7 +384,7 @@ nlm_bind_host(struct nlm_host *host)
                if (time_after_eq(jiffies, host->h_nextrebind)) {
                        rpc_force_rebind(clnt);
                        host->h_nextrebind = jiffies + NLM_HOST_REBIND;
-                       dprintk("lockd: next rebind in %ld jiffies\n",
+                       dprintk("lockd: next rebind in %lu jiffies\n",
                                        host->h_nextrebind - jiffies);
                }
        } else {
@@ -234,9 +397,9 @@ nlm_bind_host(struct nlm_host *host)
                };
                struct rpc_create_args args = {
                        .protocol       = host->h_proto,
-                       .address        = (struct sockaddr *)&host->h_addr,
-                       .addrsize       = sizeof(host->h_addr),
-                       .saddress       = (struct sockaddr *)&host->h_saddr,
+                       .address        = nlm_addr(host),
+                       .addrsize       = host->h_addrlen,
+                       .saddress       = nlm_srcaddr(host),
                        .timeout        = &timeparms,
                        .servername     = host->h_name,
                        .program        = &nlm_program,
@@ -324,12 +487,16 @@ void nlm_host_rebooted(const struct sockaddr_in *sin,
        struct nsm_handle *nsm;
        struct nlm_host *host;
 
-       dprintk("lockd: nlm_host_rebooted(%s, %u.%u.%u.%u)\n",
-                       hostname, NIPQUAD(sin->sin_addr));
-
-       /* Find the NSM handle for this peer */
-       if (!(nsm = __nsm_find(sin, hostname, hostname_len, 0)))
+       nsm = nsm_find((struct sockaddr *)sin, sizeof(*sin),
+                       hostname, hostname_len, 0);
+       if (nsm == NULL) {
+               dprintk("lockd: never saw rebooted peer '%.*s' before\n",
+                               hostname_len, hostname);
                return;
+       }
+
+       dprintk("lockd: nlm_host_rebooted(%.*s, %s)\n",
+                       hostname_len, hostname, nsm->sm_addrbuf);
 
        /* When reclaiming locks on this peer, make sure that
         * we set up a new notification */
@@ -461,22 +628,23 @@ nlm_gc_hosts(void)
 static LIST_HEAD(nsm_handles);
 static DEFINE_SPINLOCK(nsm_lock);
 
-static struct nsm_handle *
-__nsm_find(const struct sockaddr_in *sin,
-               const char *hostname, unsigned int hostname_len,
-               int create)
+static struct nsm_handle *nsm_find(const struct sockaddr *sap,
+                                  const size_t salen,
+                                  const char *hostname,
+                                  const size_t hostname_len,
+                                  const int create)
 {
        struct nsm_handle *nsm = NULL;
        struct nsm_handle *pos;
 
-       if (!sin)
+       if (!sap)
                return NULL;
 
        if (hostname && memchr(hostname, '/', hostname_len) != NULL) {
                if (printk_ratelimit()) {
                        printk(KERN_WARNING "Invalid hostname \"%.*s\" "
                                            "in NFS lock request\n",
-                               hostname_len, hostname);
+                               (int)hostname_len, hostname);
                }
                return NULL;
        }
@@ -489,7 +657,7 @@ retry:
                        if (strlen(pos->sm_name) != hostname_len
                         || memcmp(pos->sm_name, hostname, hostname_len))
                                continue;
-               } else if (!nlm_cmp_addr(&pos->sm_addr, sin))
+               } else if (!nlm_cmp_addr(nsm_addr(pos), sap))
                        continue;
                atomic_inc(&pos->sm_count);
                kfree(nsm);
@@ -509,10 +677,13 @@ retry:
        if (nsm == NULL)
                return NULL;
 
-       nsm->sm_addr = *sin;
+       memcpy(nsm_addr(nsm), sap, salen);
+       nsm->sm_addrlen = salen;
        nsm->sm_name = (char *) (nsm + 1);
        memcpy(nsm->sm_name, hostname, hostname_len);
        nsm->sm_name[hostname_len] = '\0';
+       nlm_display_address((struct sockaddr *)&nsm->sm_addr,
+                               nsm->sm_addrbuf, sizeof(nsm->sm_addrbuf));
        atomic_set(&nsm->sm_count, 1);
        goto retry;
 
@@ -521,13 +692,6 @@ found:
        return nsm;
 }
 
-static struct nsm_handle *
-nsm_find(const struct sockaddr_in *sin, const char *hostname,
-        unsigned int hostname_len)
-{
-       return __nsm_find(sin, hostname, hostname_len, 1);
-}
-
 /*
  * Release an NSM handle
  */
index e4d563543b11066a1acd17a5beb7d0a8916aa520..4e7e958e8f6753236832ed9ebd82f6bae0746899 100644 (file)
@@ -51,7 +51,7 @@ nsm_mon_unmon(struct nsm_handle *nsm, u32 proc, struct nsm_res *res)
 
        memset(&args, 0, sizeof(args));
        args.mon_name = nsm->sm_name;
-       args.addr = nsm->sm_addr.sin_addr.s_addr;
+       args.addr = nsm_addr_in(nsm)->sin_addr.s_addr;
        args.prog = NLM_PROGRAM;
        args.vers = 3;
        args.proc = NLMPROC_NSM_NOTIFY;
index 5bd9bf0fa9df09121f8c190d7e47ec8ef7871ede..c631a83931cec9cd56128daf5fc494b8fac9703a 100644 (file)
@@ -51,7 +51,6 @@ static DEFINE_MUTEX(nlmsvc_mutex);
 static unsigned int            nlmsvc_users;
 static struct task_struct      *nlmsvc_task;
 static struct svc_rqst         *nlmsvc_rqst;
-int                            nlmsvc_grace_period;
 unsigned long                  nlmsvc_timeout;
 
 /*
@@ -85,27 +84,23 @@ static unsigned long get_lockd_grace_period(void)
                return nlm_timeout * 5 * HZ;
 }
 
-unsigned long get_nfs_grace_period(void)
-{
-       unsigned long lockdgrace = get_lockd_grace_period();
-       unsigned long nfsdgrace = 0;
-
-       if (nlmsvc_ops)
-               nfsdgrace = nlmsvc_ops->get_grace_period();
-
-       return max(lockdgrace, nfsdgrace);
-}
-EXPORT_SYMBOL(get_nfs_grace_period);
+static struct lock_manager lockd_manager = {
+};
 
-static unsigned long set_grace_period(void)
+static void grace_ender(struct work_struct *not_used)
 {
-       nlmsvc_grace_period = 1;
-       return get_nfs_grace_period() + jiffies;
+       locks_end_grace(&lockd_manager);
 }
 
-static inline void clear_grace_period(void)
+static DECLARE_DELAYED_WORK(grace_period_end, grace_ender);
+
+static void set_grace_period(void)
 {
-       nlmsvc_grace_period = 0;
+       unsigned long grace_period = get_lockd_grace_period();
+
+       locks_start_grace(&lockd_manager);
+       cancel_delayed_work_sync(&grace_period_end);
+       schedule_delayed_work(&grace_period_end, grace_period);
 }
 
 /*
@@ -116,7 +111,6 @@ lockd(void *vrqstp)
 {
        int             err = 0, preverr = 0;
        struct svc_rqst *rqstp = vrqstp;
-       unsigned long grace_period_expire;
 
        /* try_to_freeze() is called from svc_recv() */
        set_freezable();
@@ -139,7 +133,7 @@ lockd(void *vrqstp)
                nlm_timeout = LOCKD_DFLT_TIMEO;
        nlmsvc_timeout = nlm_timeout * HZ;
 
-       grace_period_expire = set_grace_period();
+       set_grace_period();
 
        /*
         * The main request loop. We don't terminate until the last
@@ -153,21 +147,12 @@ lockd(void *vrqstp)
                        flush_signals(current);
                        if (nlmsvc_ops) {
                                nlmsvc_invalidate_all();
-                               grace_period_expire = set_grace_period();
+                               set_grace_period();
                        }
                        continue;
                }
 
-               /*
-                * Retry any blocked locks that have been notified by
-                * the VFS. Don't do this during grace period.
-                * (Theoretically, there shouldn't even be blocked locks
-                * during grace period).
-                */
-               if (!nlmsvc_grace_period) {
-                       timeout = nlmsvc_retry_blocked();
-               } else if (time_before(grace_period_expire, jiffies))
-                       clear_grace_period();
+               timeout = nlmsvc_retry_blocked();
 
                /*
                 * Find a socket with data available and call its
@@ -195,6 +180,7 @@ lockd(void *vrqstp)
                svc_process(rqstp);
        }
        flush_signals(current);
+       cancel_delayed_work_sync(&grace_period_end);
        if (nlmsvc_ops)
                nlmsvc_invalidate_all();
        nlm_shutdown_hosts();
@@ -203,25 +189,28 @@ lockd(void *vrqstp)
 }
 
 /*
- * Make any sockets that are needed but not present.
- * If nlm_udpport or nlm_tcpport were set as module
- * options, make those sockets unconditionally
+ * Ensure there are active UDP and TCP listeners for lockd.
+ *
+ * Even if we have only TCP NFS mounts and/or TCP NFSDs, some
+ * local services (such as rpc.statd) still require UDP, and
+ * some NFS servers do not yet support NLM over TCP.
+ *
+ * Returns zero if all listeners are available; otherwise a
+ * negative errno value is returned.
  */
-static int make_socks(struct svc_serv *serv, int proto)
+static int make_socks(struct svc_serv *serv)
 {
        static int warned;
        struct svc_xprt *xprt;
        int err = 0;
 
-       if (proto == IPPROTO_UDP || nlm_udpport) {
-               xprt = svc_find_xprt(serv, "udp", 0, 0);
-               if (!xprt)
-                       err = svc_create_xprt(serv, "udp", nlm_udpport,
-                                             SVC_SOCK_DEFAULTS);
-               else
-                       svc_xprt_put(xprt);
-       }
-       if (err >= 0 && (proto == IPPROTO_TCP || nlm_tcpport)) {
+       xprt = svc_find_xprt(serv, "udp", 0, 0);
+       if (!xprt)
+               err = svc_create_xprt(serv, "udp", nlm_udpport,
+                                     SVC_SOCK_DEFAULTS);
+       else
+               svc_xprt_put(xprt);
+       if (err >= 0) {
                xprt = svc_find_xprt(serv, "tcp", 0, 0);
                if (!xprt)
                        err = svc_create_xprt(serv, "tcp", nlm_tcpport,
@@ -241,8 +230,7 @@ static int make_socks(struct svc_serv *serv, int proto)
 /*
  * Bring up the lockd process if it's not already up.
  */
-int
-lockd_up(int proto) /* Maybe add a 'family' option when IPv6 is supported ?? */
+int lockd_up(void)
 {
        struct svc_serv *serv;
        int             error = 0;
@@ -251,11 +239,8 @@ lockd_up(int proto) /* Maybe add a 'family' option when IPv6 is supported ?? */
        /*
         * Check whether we're already up and running.
         */
-       if (nlmsvc_rqst) {
-               if (proto)
-                       error = make_socks(nlmsvc_rqst->rq_server, proto);
+       if (nlmsvc_rqst)
                goto out;
-       }
 
        /*
         * Sanity check: if there's no pid,
@@ -266,13 +251,14 @@ lockd_up(int proto) /* Maybe add a 'family' option when IPv6 is supported ?? */
                        "lockd_up: no pid, %d users??\n", nlmsvc_users);
 
        error = -ENOMEM;
-       serv = svc_create(&nlmsvc_program, LOCKD_BUFSIZE, NULL);
+       serv = svc_create(&nlmsvc_program, LOCKD_BUFSIZE, AF_INET, NULL);
        if (!serv) {
                printk(KERN_WARNING "lockd_up: create service failed\n");
                goto out;
        }
 
-       if ((error = make_socks(serv, proto)) < 0)
+       error = make_socks(serv);
+       if (error < 0)
                goto destroy_and_out;
 
        /*
index 4a714f64515b06557704b38cf02f8ba7f36f7ea0..014f6ce48172fa73cc5d534bda12096776b39e20 100644 (file)
@@ -88,12 +88,6 @@ nlm4svc_proc_test(struct svc_rqst *rqstp, struct nlm_args *argp,
        dprintk("lockd: TEST4        called\n");
        resp->cookie = argp->cookie;
 
-       /* Don't accept test requests during grace period */
-       if (nlmsvc_grace_period) {
-               resp->status = nlm_lck_denied_grace_period;
-               return rc;
-       }
-
        /* Obtain client and file */
        if ((resp->status = nlm4svc_retrieve_args(rqstp, argp, &host, &file)))
                return resp->status == nlm_drop_reply ? rpc_drop_reply :rpc_success;
@@ -122,12 +116,6 @@ nlm4svc_proc_lock(struct svc_rqst *rqstp, struct nlm_args *argp,
 
        resp->cookie = argp->cookie;
 
-       /* Don't accept new lock requests during grace period */
-       if (nlmsvc_grace_period && !argp->reclaim) {
-               resp->status = nlm_lck_denied_grace_period;
-               return rc;
-       }
-
        /* Obtain client and file */
        if ((resp->status = nlm4svc_retrieve_args(rqstp, argp, &host, &file)))
                return resp->status == nlm_drop_reply ? rpc_drop_reply :rpc_success;
@@ -146,7 +134,8 @@ nlm4svc_proc_lock(struct svc_rqst *rqstp, struct nlm_args *argp,
 
        /* Now try to lock the file */
        resp->status = nlmsvc_lock(rqstp, file, host, &argp->lock,
-                                       argp->block, &argp->cookie);
+                                       argp->block, &argp->cookie,
+                                       argp->reclaim);
        if (resp->status == nlm_drop_reply)
                rc = rpc_drop_reply;
        else
@@ -169,7 +158,7 @@ nlm4svc_proc_cancel(struct svc_rqst *rqstp, struct nlm_args *argp,
        resp->cookie = argp->cookie;
 
        /* Don't accept requests during grace period */
-       if (nlmsvc_grace_period) {
+       if (locks_in_grace()) {
                resp->status = nlm_lck_denied_grace_period;
                return rpc_success;
        }
@@ -202,7 +191,7 @@ nlm4svc_proc_unlock(struct svc_rqst *rqstp, struct nlm_args *argp,
        resp->cookie = argp->cookie;
 
        /* Don't accept new lock requests during grace period */
-       if (nlmsvc_grace_period) {
+       if (locks_in_grace()) {
                resp->status = nlm_lck_denied_grace_period;
                return rpc_success;
        }
@@ -231,7 +220,7 @@ nlm4svc_proc_granted(struct svc_rqst *rqstp, struct nlm_args *argp,
        resp->cookie = argp->cookie;
 
        dprintk("lockd: GRANTED       called\n");
-       resp->status = nlmclnt_grant(svc_addr_in(rqstp), &argp->lock);
+       resp->status = nlmclnt_grant(svc_addr(rqstp), &argp->lock);
        dprintk("lockd: GRANTED       status %d\n", ntohl(resp->status));
        return rpc_success;
 }
@@ -341,7 +330,7 @@ nlm4svc_proc_share(struct svc_rqst *rqstp, struct nlm_args *argp,
        resp->cookie = argp->cookie;
 
        /* Don't accept new lock requests during grace period */
-       if (nlmsvc_grace_period && !argp->reclaim) {
+       if (locks_in_grace() && !argp->reclaim) {
                resp->status = nlm_lck_denied_grace_period;
                return rpc_success;
        }
@@ -374,7 +363,7 @@ nlm4svc_proc_unshare(struct svc_rqst *rqstp, struct nlm_args *argp,
        resp->cookie = argp->cookie;
 
        /* Don't accept requests during grace period */
-       if (nlmsvc_grace_period) {
+       if (locks_in_grace()) {
                resp->status = nlm_lck_denied_grace_period;
                return rpc_success;
        }
@@ -432,11 +421,9 @@ nlm4svc_proc_sm_notify(struct svc_rqst *rqstp, struct nlm_reboot *argp,
 {
        struct sockaddr_in      saddr;
 
-       memcpy(&saddr, svc_addr_in(rqstp), sizeof(saddr));
-
        dprintk("lockd: SM_NOTIFY     called\n");
-       if (saddr.sin_addr.s_addr != htonl(INADDR_LOOPBACK)
-        || ntohs(saddr.sin_port) >= 1024) {
+
+       if (!nlm_privileged_requester(rqstp)) {
                char buf[RPC_MAX_ADDRBUFLEN];
                printk(KERN_WARNING "lockd: rejected NSM callback from %s\n",
                                svc_print_addr(rqstp, buf, sizeof(buf)));
index cf0d5c2c318d6002330f1bca7965b09e8a047e9d..6063a8e4b9f3223104843bce393882181b890539 100644 (file)
@@ -360,7 +360,7 @@ nlmsvc_defer_lock_rqst(struct svc_rqst *rqstp, struct nlm_block *block)
 __be32
 nlmsvc_lock(struct svc_rqst *rqstp, struct nlm_file *file,
            struct nlm_host *host, struct nlm_lock *lock, int wait,
-           struct nlm_cookie *cookie)
+           struct nlm_cookie *cookie, int reclaim)
 {
        struct nlm_block        *block = NULL;
        int                     error;
@@ -406,6 +406,15 @@ nlmsvc_lock(struct svc_rqst *rqstp, struct nlm_file *file,
                goto out;
        }
 
+       if (locks_in_grace() && !reclaim) {
+               ret = nlm_lck_denied_grace_period;
+               goto out;
+       }
+       if (reclaim && !locks_in_grace()) {
+               ret = nlm_lck_denied_grace_period;
+               goto out;
+       }
+
        if (!wait)
                lock->fl.fl_flags &= ~FL_SLEEP;
        error = vfs_lock_file(file->f_file, F_SETLK, &lock->fl, NULL);
@@ -502,6 +511,10 @@ nlmsvc_testlock(struct svc_rqst *rqstp, struct nlm_file *file,
                goto out;
        }
 
+       if (locks_in_grace()) {
+               ret = nlm_lck_denied_grace_period;
+               goto out;
+       }
        error = vfs_test_lock(file->f_file, &lock->fl);
        if (error == FILE_LOCK_DEFERRED) {
                ret = nlmsvc_defer_lock_rqst(rqstp, block);
@@ -582,6 +595,9 @@ nlmsvc_cancel_blocked(struct nlm_file *file, struct nlm_lock *lock)
                                (long long)lock->fl.fl_start,
                                (long long)lock->fl.fl_end);
 
+       if (locks_in_grace())
+               return nlm_lck_denied_grace_period;
+
        mutex_lock(&file->f_mutex);
        block = nlmsvc_lookup_block(file, lock);
        mutex_unlock(&file->f_mutex);
index 76262c1986f2d36d1f6765da9acf506736b3b30b..548b0bb2b84d1e7dccb6351bdff03980d276959e 100644 (file)
@@ -117,12 +117,6 @@ nlmsvc_proc_test(struct svc_rqst *rqstp, struct nlm_args *argp,
        dprintk("lockd: TEST          called\n");
        resp->cookie = argp->cookie;
 
-       /* Don't accept test requests during grace period */
-       if (nlmsvc_grace_period) {
-               resp->status = nlm_lck_denied_grace_period;
-               return rc;
-       }
-
        /* Obtain client and file */
        if ((resp->status = nlmsvc_retrieve_args(rqstp, argp, &host, &file)))
                return resp->status == nlm_drop_reply ? rpc_drop_reply :rpc_success;
@@ -152,12 +146,6 @@ nlmsvc_proc_lock(struct svc_rqst *rqstp, struct nlm_args *argp,
 
        resp->cookie = argp->cookie;
 
-       /* Don't accept new lock requests during grace period */
-       if (nlmsvc_grace_period && !argp->reclaim) {
-               resp->status = nlm_lck_denied_grace_period;
-               return rc;
-       }
-
        /* Obtain client and file */
        if ((resp->status = nlmsvc_retrieve_args(rqstp, argp, &host, &file)))
                return resp->status == nlm_drop_reply ? rpc_drop_reply :rpc_success;
@@ -176,7 +164,8 @@ nlmsvc_proc_lock(struct svc_rqst *rqstp, struct nlm_args *argp,
 
        /* Now try to lock the file */
        resp->status = cast_status(nlmsvc_lock(rqstp, file, host, &argp->lock,
-                                              argp->block, &argp->cookie));
+                                              argp->block, &argp->cookie,
+                                              argp->reclaim));
        if (resp->status == nlm_drop_reply)
                rc = rpc_drop_reply;
        else
@@ -199,7 +188,7 @@ nlmsvc_proc_cancel(struct svc_rqst *rqstp, struct nlm_args *argp,
        resp->cookie = argp->cookie;
 
        /* Don't accept requests during grace period */
-       if (nlmsvc_grace_period) {
+       if (locks_in_grace()) {
                resp->status = nlm_lck_denied_grace_period;
                return rpc_success;
        }
@@ -232,7 +221,7 @@ nlmsvc_proc_unlock(struct svc_rqst *rqstp, struct nlm_args *argp,
        resp->cookie = argp->cookie;
 
        /* Don't accept new lock requests during grace period */
-       if (nlmsvc_grace_period) {
+       if (locks_in_grace()) {
                resp->status = nlm_lck_denied_grace_period;
                return rpc_success;
        }
@@ -261,7 +250,7 @@ nlmsvc_proc_granted(struct svc_rqst *rqstp, struct nlm_args *argp,
        resp->cookie = argp->cookie;
 
        dprintk("lockd: GRANTED       called\n");
-       resp->status = nlmclnt_grant(svc_addr_in(rqstp), &argp->lock);
+       resp->status = nlmclnt_grant(svc_addr(rqstp), &argp->lock);
        dprintk("lockd: GRANTED       status %d\n", ntohl(resp->status));
        return rpc_success;
 }
@@ -373,7 +362,7 @@ nlmsvc_proc_share(struct svc_rqst *rqstp, struct nlm_args *argp,
        resp->cookie = argp->cookie;
 
        /* Don't accept new lock requests during grace period */
-       if (nlmsvc_grace_period && !argp->reclaim) {
+       if (locks_in_grace() && !argp->reclaim) {
                resp->status = nlm_lck_denied_grace_period;
                return rpc_success;
        }
@@ -406,7 +395,7 @@ nlmsvc_proc_unshare(struct svc_rqst *rqstp, struct nlm_args *argp,
        resp->cookie = argp->cookie;
 
        /* Don't accept requests during grace period */
-       if (nlmsvc_grace_period) {
+       if (locks_in_grace()) {
                resp->status = nlm_lck_denied_grace_period;
                return rpc_success;
        }
@@ -464,11 +453,9 @@ nlmsvc_proc_sm_notify(struct svc_rqst *rqstp, struct nlm_reboot *argp,
 {
        struct sockaddr_in      saddr;
 
-       memcpy(&saddr, svc_addr_in(rqstp), sizeof(saddr));
-
        dprintk("lockd: SM_NOTIFY     called\n");
-       if (saddr.sin_addr.s_addr != htonl(INADDR_LOOPBACK)
-        || ntohs(saddr.sin_port) >= 1024) {
+
+       if (!nlm_privileged_requester(rqstp)) {
                char buf[RPC_MAX_ADDRBUFLEN];
                printk(KERN_WARNING "lockd: rejected NSM callback from %s\n",
                                svc_print_addr(rqstp, buf, sizeof(buf)));
index 198b4e55b3732523fbe569cfd927948c3ee70cb0..34c2766e27c7273138af21bdc8b65e9447c71d72 100644 (file)
@@ -418,7 +418,7 @@ EXPORT_SYMBOL_GPL(nlmsvc_unlock_all_by_sb);
 static int
 nlmsvc_match_ip(void *datap, struct nlm_host *host)
 {
-       return nlm_cmp_addr(&host->h_saddr, datap);
+       return nlm_cmp_addr(nlm_srcaddr(host), datap);
 }
 
 /**
index 3e459e18cc31ba2efeaca67dc9b40ae506604247..1f226290c67cf88a2707f08447d2e92f9a98bfbc 100644 (file)
@@ -351,8 +351,6 @@ nlmsvc_decode_reboot(struct svc_rqst *rqstp, __be32 *p, struct nlm_reboot *argp)
        argp->state = ntohl(*p++);
        /* Preserve the address in network byte order */
        argp->addr = *p++;
-       argp->vers = *p++;
-       argp->proto = *p++;
        return xdr_argsize_check(rqstp, p);
 }
 
index 43ff9397e6c67fee3504dda5e5c3579c04212cf2..50c493a8ad8e5e1f4fcd10eaa7d2311a35c5285b 100644 (file)
@@ -358,8 +358,6 @@ nlm4svc_decode_reboot(struct svc_rqst *rqstp, __be32 *p, struct nlm_reboot *argp
        argp->state = ntohl(*p++);
        /* Preserve the address in network byte order */
        argp->addr  = *p++;
-       argp->vers  = *p++;
-       argp->proto = *p++;
        return xdr_argsize_check(rqstp, p);
 }
 
index f447f4b4476cc37b26144ffdfcef83cd80eab53e..6a09760c5960fd427935c2306f5080caa66bce3b 100644 (file)
@@ -105,7 +105,8 @@ int nfs_callback_up(void)
        mutex_lock(&nfs_callback_mutex);
        if (nfs_callback_info.users++ || nfs_callback_info.task != NULL)
                goto out;
-       serv = svc_create(&nfs4_callback_program, NFS4_CALLBACK_BUFSIZE, NULL);
+       serv = svc_create(&nfs4_callback_program, NFS4_CALLBACK_BUFSIZE,
+                               AF_INET, NULL);
        ret = -ENOMEM;
        if (!serv)
                goto out_err;
index 15c6faeec77c118a77b11c50287a6a43828bd9a2..b2786a5f9afefaaea3573303339cd7154ad54bd7 100644 (file)
@@ -70,7 +70,6 @@ nlm_fclose(struct file *filp)
 static struct nlmsvc_binding   nfsd_nlm_ops = {
        .fopen          = nlm_fopen,            /* open file for locking */
        .fclose         = nlm_fclose,           /* close file */
-       .get_grace_period = get_nfs4_grace_period,
 };
 
 void
index 4d617ea28cfc0b699868275db4cd95202e23f989..9dbd2eb91281b14a4f3472c3ac5dad046c4525c9 100644 (file)
@@ -63,7 +63,8 @@ nfsd3_proc_getattr(struct svc_rqst *rqstp, struct nfsd_fhandle  *argp,
                SVCFH_fmt(&argp->fh));
 
        fh_copy(&resp->fh, &argp->fh);
-       nfserr = fh_verify(rqstp, &resp->fh, 0, NFSD_MAY_NOP);
+       nfserr = fh_verify(rqstp, &resp->fh, 0,
+                       NFSD_MAY_NOP | NFSD_MAY_BYPASS_GSS_ON_ROOT);
        if (nfserr)
                RETURN_STATUS(nfserr);
 
@@ -530,7 +531,7 @@ nfsd3_proc_fsstat(struct svc_rqst * rqstp, struct nfsd_fhandle    *argp,
        dprintk("nfsd: FSSTAT(3)   %s\n",
                                SVCFH_fmt(&argp->fh));
 
-       nfserr = nfsd_statfs(rqstp, &argp->fh, &resp->stats);
+       nfserr = nfsd_statfs(rqstp, &argp->fh, &resp->stats, 0);
        fh_put(&argp->fh);
        RETURN_STATUS(nfserr);
 }
@@ -558,7 +559,8 @@ nfsd3_proc_fsinfo(struct svc_rqst * rqstp, struct nfsd_fhandle    *argp,
        resp->f_maxfilesize = ~(u32) 0;
        resp->f_properties = NFS3_FSF_DEFAULT;
 
-       nfserr = fh_verify(rqstp, &argp->fh, 0, NFSD_MAY_NOP);
+       nfserr = fh_verify(rqstp, &argp->fh, 0,
+                       NFSD_MAY_NOP | NFSD_MAY_BYPASS_GSS_ON_ROOT);
 
        /* Check special features of the file system. May request
         * different read/write sizes for file systems known to have
index 702fa577aa6e075a6fef3a9b31be6ffdb1d3a9c5..094747a1227c18b6ace1726d81ed97487c98169a 100644 (file)
@@ -225,7 +225,8 @@ encode_cb_recall(struct xdr_stream *xdr, struct nfs4_cb_recall *cb_rec)
 
        RESERVE_SPACE(12+sizeof(cb_rec->cbr_stateid) + len);
        WRITE32(OP_CB_RECALL);
-       WRITEMEM(&cb_rec->cbr_stateid, sizeof(stateid_t));
+       WRITE32(cb_rec->cbr_stateid.si_generation);
+       WRITEMEM(&cb_rec->cbr_stateid.si_opaque, sizeof(stateid_opaque_t));
        WRITE32(cb_rec->cbr_trunc);
        WRITE32(len);
        WRITEMEM(cb_rec->cbr_fhval, len);
@@ -379,6 +380,7 @@ static int do_probe_callback(void *data)
                .addrsize       = sizeof(addr),
                .timeout        = &timeparms,
                .program        = &cb_program,
+               .prognumber     = cb->cb_prog,
                .version        = nfs_cb_version[1]->number,
                .authflavor     = RPC_AUTH_UNIX, /* XXX: need AUTH_GSS... */
                .flags          = (RPC_CLNT_CREATE_NOPING | RPC_CLNT_CREATE_QUIET),
@@ -396,9 +398,6 @@ static int do_probe_callback(void *data)
        addr.sin_port = htons(cb->cb_port);
        addr.sin_addr.s_addr = htonl(cb->cb_addr);
 
-       /* Initialize rpc_stat */
-       memset(args.program->stats, 0, sizeof(struct rpc_stat));
-
        /* Create RPC client */
        client = rpc_create(&args);
        if (IS_ERR(client)) {
index e5b51ffafc6c2c2300fb61f3fb0c0036e0762115..669461e291aecbd7f15e2065c61e86f26cd9d2e7 100644 (file)
@@ -201,10 +201,10 @@ nfsd4_open(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
        /* Openowner is now set, so sequence id will get bumped.  Now we need
         * these checks before we do any creates: */
        status = nfserr_grace;
-       if (nfs4_in_grace() && open->op_claim_type != NFS4_OPEN_CLAIM_PREVIOUS)
+       if (locks_in_grace() && open->op_claim_type != NFS4_OPEN_CLAIM_PREVIOUS)
                goto out;
        status = nfserr_no_grace;
-       if (!nfs4_in_grace() && open->op_claim_type == NFS4_OPEN_CLAIM_PREVIOUS)
+       if (!locks_in_grace() && open->op_claim_type == NFS4_OPEN_CLAIM_PREVIOUS)
                goto out;
 
        switch (open->op_claim_type) {
@@ -575,7 +575,7 @@ nfsd4_remove(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
 {
        __be32 status;
 
-       if (nfs4_in_grace())
+       if (locks_in_grace())
                return nfserr_grace;
        status = nfsd_unlink(rqstp, &cstate->current_fh, 0,
                             remove->rm_name, remove->rm_namelen);
@@ -596,7 +596,7 @@ nfsd4_rename(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
 
        if (!cstate->save_fh.fh_dentry)
                return status;
-       if (nfs4_in_grace() && !(cstate->save_fh.fh_export->ex_flags
+       if (locks_in_grace() && !(cstate->save_fh.fh_export->ex_flags
                                        & NFSEXP_NOSUBTREECHECK))
                return nfserr_grace;
        status = nfsd_rename(rqstp, &cstate->save_fh, rename->rn_sname,
index 1578d7a2667ec397f3e0689d1445527cf9a38cf4..0cc7ff5d5ab53e4bf0c0c5e4646cac4012f18165 100644 (file)
@@ -61,7 +61,6 @@
 static time_t lease_time = 90;     /* default lease time */
 static time_t user_lease_time = 90;
 static time_t boot_time;
-static int in_grace = 1;
 static u32 current_ownerid = 1;
 static u32 current_fileid = 1;
 static u32 current_delegid = 1;
@@ -1640,7 +1639,7 @@ nfs4_open_delegation(struct svc_fh *fh, struct nfsd4_open *open, struct nfs4_sta
                case NFS4_OPEN_CLAIM_NULL:
                        /* Let's not give out any delegations till everyone's
                         * had the chance to reclaim theirs.... */
-                       if (nfs4_in_grace())
+                       if (locks_in_grace())
                                goto out;
                        if (!atomic_read(&cb->cb_set) || !sop->so_confirmed)
                                goto out;
@@ -1816,12 +1815,15 @@ out:
        return status;
 }
 
+struct lock_manager nfsd4_manager = {
+};
+
 static void
-end_grace(void)
+nfsd4_end_grace(void)
 {
        dprintk("NFSD: end of grace period\n");
        nfsd4_recdir_purge_old();
-       in_grace = 0;
+       locks_end_grace(&nfsd4_manager);
 }
 
 static time_t
@@ -1838,8 +1840,8 @@ nfs4_laundromat(void)
        nfs4_lock_state();
 
        dprintk("NFSD: laundromat service - starting\n");
-       if (in_grace)
-               end_grace();
+       if (locks_in_grace())
+               nfsd4_end_grace();
        list_for_each_safe(pos, next, &client_lru) {
                clp = list_entry(pos, struct nfs4_client, cl_lru);
                if (time_after((unsigned long)clp->cl_time, (unsigned long)cutoff)) {
@@ -1974,7 +1976,7 @@ check_special_stateids(svc_fh *current_fh, stateid_t *stateid, int flags)
                return nfserr_bad_stateid;
        else if (ONE_STATEID(stateid) && (flags & RD_STATE))
                return nfs_ok;
-       else if (nfs4_in_grace()) {
+       else if (locks_in_grace()) {
                /* Answer in remaining cases depends on existance of
                 * conflicting state; so we must wait out the grace period. */
                return nfserr_grace;
@@ -1993,7 +1995,7 @@ check_special_stateids(svc_fh *current_fh, stateid_t *stateid, int flags)
 static inline int
 io_during_grace_disallowed(struct inode *inode, int flags)
 {
-       return nfs4_in_grace() && (flags & (RD_STATE | WR_STATE))
+       return locks_in_grace() && (flags & (RD_STATE | WR_STATE))
                && mandatory_lock(inode);
 }
 
@@ -2693,10 +2695,10 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
        filp = lock_stp->st_vfs_file;
 
        status = nfserr_grace;
-       if (nfs4_in_grace() && !lock->lk_reclaim)
+       if (locks_in_grace() && !lock->lk_reclaim)
                goto out;
        status = nfserr_no_grace;
-       if (!nfs4_in_grace() && lock->lk_reclaim)
+       if (!locks_in_grace() && lock->lk_reclaim)
                goto out;
 
        locks_init_lock(&file_lock);
@@ -2779,7 +2781,7 @@ nfsd4_lockt(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
        int error;
        __be32 status;
 
-       if (nfs4_in_grace())
+       if (locks_in_grace())
                return nfserr_grace;
 
        if (check_lock_length(lockt->lt_offset, lockt->lt_length))
@@ -3192,9 +3194,9 @@ __nfs4_state_start(void)
        unsigned long grace_time;
 
        boot_time = get_seconds();
-       grace_time = get_nfs_grace_period();
+       grace_time = get_nfs4_grace_period();
        lease_time = user_lease_time;
-       in_grace = 1;
+       locks_start_grace(&nfsd4_manager);
        printk(KERN_INFO "NFSD: starting %ld-second grace period\n",
               grace_time/HZ);
        laundry_wq = create_singlethread_workqueue("nfsd4");
@@ -3213,12 +3215,6 @@ nfs4_state_start(void)
        return;
 }
 
-int
-nfs4_in_grace(void)
-{
-       return in_grace;
-}
-
 time_t
 nfs4_lease_time(void)
 {
index 14ba4d9b285913f8aea89eae504908eebc56f435..afcdf4b76843a7219e50c66d3d3dba4dde5ae514 100644 (file)
@@ -412,6 +412,18 @@ out_nfserr:
        goto out;
 }
 
+static __be32
+nfsd4_decode_stateid(struct nfsd4_compoundargs *argp, stateid_t *sid)
+{
+       DECODE_HEAD;
+
+       READ_BUF(sizeof(stateid_t));
+       READ32(sid->si_generation);
+       COPYMEM(&sid->si_opaque, sizeof(stateid_opaque_t));
+
+       DECODE_TAIL;
+}
+
 static __be32
 nfsd4_decode_access(struct nfsd4_compoundargs *argp, struct nfsd4_access *access)
 {
@@ -429,10 +441,9 @@ nfsd4_decode_close(struct nfsd4_compoundargs *argp, struct nfsd4_close *close)
        DECODE_HEAD;
 
        close->cl_stateowner = NULL;
-       READ_BUF(4 + sizeof(stateid_t));
+       READ_BUF(4);
        READ32(close->cl_seqid);
-       READ32(close->cl_stateid.si_generation);
-       COPYMEM(&close->cl_stateid.si_opaque, sizeof(stateid_opaque_t));
+       return nfsd4_decode_stateid(argp, &close->cl_stateid);
 
        DECODE_TAIL;
 }
@@ -493,13 +504,7 @@ nfsd4_decode_create(struct nfsd4_compoundargs *argp, struct nfsd4_create *create
 static inline __be32
 nfsd4_decode_delegreturn(struct nfsd4_compoundargs *argp, struct nfsd4_delegreturn *dr)
 {
-       DECODE_HEAD;
-
-       READ_BUF(sizeof(stateid_t));
-       READ32(dr->dr_stateid.si_generation);
-       COPYMEM(&dr->dr_stateid.si_opaque, sizeof(stateid_opaque_t));
-
-       DECODE_TAIL;
+       return nfsd4_decode_stateid(argp, &dr->dr_stateid);
 }
 
 static inline __be32
@@ -542,20 +547,22 @@ nfsd4_decode_lock(struct nfsd4_compoundargs *argp, struct nfsd4_lock *lock)
        READ32(lock->lk_is_new);
 
        if (lock->lk_is_new) {
-               READ_BUF(36);
+               READ_BUF(4);
                READ32(lock->lk_new_open_seqid);
-               READ32(lock->lk_new_open_stateid.si_generation);
-
-               COPYMEM(&lock->lk_new_open_stateid.si_opaque, sizeof(stateid_opaque_t));
+               status = nfsd4_decode_stateid(argp, &lock->lk_new_open_stateid);
+               if (status)
+                       return status;
+               READ_BUF(8 + sizeof(clientid_t));
                READ32(lock->lk_new_lock_seqid);
                COPYMEM(&lock->lk_new_clientid, sizeof(clientid_t));
                READ32(lock->lk_new_owner.len);
                READ_BUF(lock->lk_new_owner.len);
                READMEM(lock->lk_new_owner.data, lock->lk_new_owner.len);
        } else {
-               READ_BUF(20);
-               READ32(lock->lk_old_lock_stateid.si_generation);
-               COPYMEM(&lock->lk_old_lock_stateid.si_opaque, sizeof(stateid_opaque_t));
+               status = nfsd4_decode_stateid(argp, &lock->lk_old_lock_stateid);
+               if (status)
+                       return status;
+               READ_BUF(4);
                READ32(lock->lk_old_lock_seqid);
        }
 
@@ -587,13 +594,15 @@ nfsd4_decode_locku(struct nfsd4_compoundargs *argp, struct nfsd4_locku *locku)
        DECODE_HEAD;
 
        locku->lu_stateowner = NULL;
-       READ_BUF(24 + sizeof(stateid_t));
+       READ_BUF(8);
        READ32(locku->lu_type);
        if ((locku->lu_type < NFS4_READ_LT) || (locku->lu_type > NFS4_WRITEW_LT))
                goto xdr_error;
        READ32(locku->lu_seqid);
-       READ32(locku->lu_stateid.si_generation);
-       COPYMEM(&locku->lu_stateid.si_opaque, sizeof(stateid_opaque_t));
+       status = nfsd4_decode_stateid(argp, &locku->lu_stateid);
+       if (status)
+               return status;
+       READ_BUF(16);
        READ64(locku->lu_offset);
        READ64(locku->lu_length);
 
@@ -678,8 +687,10 @@ nfsd4_decode_open(struct nfsd4_compoundargs *argp, struct nfsd4_open *open)
                READ32(open->op_delegate_type);
                break;
        case NFS4_OPEN_CLAIM_DELEGATE_CUR:
-               READ_BUF(sizeof(stateid_t) + 4);
-               COPYMEM(&open->op_delegate_stateid, sizeof(stateid_t));
+               status = nfsd4_decode_stateid(argp, &open->op_delegate_stateid);
+               if (status)
+                       return status;
+               READ_BUF(4);
                READ32(open->op_fname.len);
                READ_BUF(open->op_fname.len);
                SAVEMEM(open->op_fname.data, open->op_fname.len);
@@ -699,9 +710,10 @@ nfsd4_decode_open_confirm(struct nfsd4_compoundargs *argp, struct nfsd4_open_con
        DECODE_HEAD;
                    
        open_conf->oc_stateowner = NULL;
-       READ_BUF(4 + sizeof(stateid_t));
-       READ32(open_conf->oc_req_stateid.si_generation);
-       COPYMEM(&open_conf->oc_req_stateid.si_opaque, sizeof(stateid_opaque_t));
+       status = nfsd4_decode_stateid(argp, &open_conf->oc_req_stateid);
+       if (status)
+               return status;
+       READ_BUF(4);
        READ32(open_conf->oc_seqid);
                                                        
        DECODE_TAIL;
@@ -713,9 +725,10 @@ nfsd4_decode_open_downgrade(struct nfsd4_compoundargs *argp, struct nfsd4_open_d
        DECODE_HEAD;
                    
        open_down->od_stateowner = NULL;
-       READ_BUF(12 + sizeof(stateid_t));
-       READ32(open_down->od_stateid.si_generation);
-       COPYMEM(&open_down->od_stateid.si_opaque, sizeof(stateid_opaque_t));
+       status = nfsd4_decode_stateid(argp, &open_down->od_stateid);
+       if (status)
+               return status;
+       READ_BUF(12);
        READ32(open_down->od_seqid);
        READ32(open_down->od_share_access);
        READ32(open_down->od_share_deny);
@@ -743,9 +756,10 @@ nfsd4_decode_read(struct nfsd4_compoundargs *argp, struct nfsd4_read *read)
 {
        DECODE_HEAD;
 
-       READ_BUF(sizeof(stateid_t) + 12);
-       READ32(read->rd_stateid.si_generation);
-       COPYMEM(&read->rd_stateid.si_opaque, sizeof(stateid_opaque_t));
+       status = nfsd4_decode_stateid(argp, &read->rd_stateid);
+       if (status)
+               return status;
+       READ_BUF(12);
        READ64(read->rd_offset);
        READ32(read->rd_length);
 
@@ -834,15 +848,13 @@ nfsd4_decode_secinfo(struct nfsd4_compoundargs *argp,
 static __be32
 nfsd4_decode_setattr(struct nfsd4_compoundargs *argp, struct nfsd4_setattr *setattr)
 {
-       DECODE_HEAD;
-
-       READ_BUF(sizeof(stateid_t));
-       READ32(setattr->sa_stateid.si_generation);
-       COPYMEM(&setattr->sa_stateid.si_opaque, sizeof(stateid_opaque_t));
-       if ((status = nfsd4_decode_fattr(argp, setattr->sa_bmval, &setattr->sa_iattr, &setattr->sa_acl)))
-               goto out;
+       __be32 status;
 
-       DECODE_TAIL;
+       status = nfsd4_decode_stateid(argp, &setattr->sa_stateid);
+       if (status)
+               return status;
+       return nfsd4_decode_fattr(argp, setattr->sa_bmval,
+                                 &setattr->sa_iattr, &setattr->sa_acl);
 }
 
 static __be32
@@ -927,9 +939,10 @@ nfsd4_decode_write(struct nfsd4_compoundargs *argp, struct nfsd4_write *write)
        int len;
        DECODE_HEAD;
 
-       READ_BUF(sizeof(stateid_opaque_t) + 20);
-       READ32(write->wr_stateid.si_generation);
-       COPYMEM(&write->wr_stateid.si_opaque, sizeof(stateid_opaque_t));
+       status = nfsd4_decode_stateid(argp, &write->wr_stateid);
+       if (status)
+               return status;
+       READ_BUF(16);
        READ64(write->wr_offset);
        READ32(write->wr_stable_how);
        if (write->wr_stable_how > 2)
@@ -1183,7 +1196,6 @@ nfsd4_decode_compound(struct nfsd4_compoundargs *argp)
  * Header routine to setup seqid operation replay cache
  */
 #define ENCODE_SEQID_OP_HEAD                                   \
-       __be32 *p;                                              \
        __be32 *save;                                           \
                                                                \
        save = resp->p;
@@ -1950,6 +1962,17 @@ fail:
        return -EINVAL;
 }
 
+static void
+nfsd4_encode_stateid(struct nfsd4_compoundres *resp, stateid_t *sid)
+{
+       ENCODE_HEAD;
+
+       RESERVE_SPACE(sizeof(stateid_t));
+       WRITE32(sid->si_generation);
+       WRITEMEM(&sid->si_opaque, sizeof(stateid_opaque_t));
+       ADJUST_ARGS();
+}
+
 static __be32
 nfsd4_encode_access(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_access *access)
 {
@@ -1969,12 +1992,9 @@ nfsd4_encode_close(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_c
 {
        ENCODE_SEQID_OP_HEAD;
 
-       if (!nfserr) {
-               RESERVE_SPACE(sizeof(stateid_t));
-               WRITE32(close->cl_stateid.si_generation);
-               WRITEMEM(&close->cl_stateid.si_opaque, sizeof(stateid_opaque_t));
-               ADJUST_ARGS();
-       }
+       if (!nfserr)
+               nfsd4_encode_stateid(resp, &close->cl_stateid);
+
        ENCODE_SEQID_OP_TAIL(close->cl_stateowner);
        return nfserr;
 }
@@ -2074,12 +2094,9 @@ nfsd4_encode_lock(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_lo
 {
        ENCODE_SEQID_OP_HEAD;
 
-       if (!nfserr) {
-               RESERVE_SPACE(4 + sizeof(stateid_t));
-               WRITE32(lock->lk_resp_stateid.si_generation);
-               WRITEMEM(&lock->lk_resp_stateid.si_opaque, sizeof(stateid_opaque_t));
-               ADJUST_ARGS();
-       } else if (nfserr == nfserr_denied)
+       if (!nfserr)
+               nfsd4_encode_stateid(resp, &lock->lk_resp_stateid);
+       else if (nfserr == nfserr_denied)
                nfsd4_encode_lock_denied(resp, &lock->lk_denied);
 
        ENCODE_SEQID_OP_TAIL(lock->lk_replay_owner);
@@ -2099,13 +2116,9 @@ nfsd4_encode_locku(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_l
 {
        ENCODE_SEQID_OP_HEAD;
 
-       if (!nfserr) {
-               RESERVE_SPACE(sizeof(stateid_t));
-               WRITE32(locku->lu_stateid.si_generation);
-               WRITEMEM(&locku->lu_stateid.si_opaque, sizeof(stateid_opaque_t));
-               ADJUST_ARGS();
-       }
-                                       
+       if (!nfserr)
+               nfsd4_encode_stateid(resp, &locku->lu_stateid);
+
        ENCODE_SEQID_OP_TAIL(locku->lu_stateowner);
        return nfserr;
 }
@@ -2128,14 +2141,14 @@ nfsd4_encode_link(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_li
 static __be32
 nfsd4_encode_open(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_open *open)
 {
+       ENCODE_HEAD;
        ENCODE_SEQID_OP_HEAD;
 
        if (nfserr)
                goto out;
 
-       RESERVE_SPACE(36 + sizeof(stateid_t));
-       WRITE32(open->op_stateid.si_generation);
-       WRITEMEM(&open->op_stateid.si_opaque, sizeof(stateid_opaque_t));
+       nfsd4_encode_stateid(resp, &open->op_stateid);
+       RESERVE_SPACE(40);
        WRITECINFO(open->op_cinfo);
        WRITE32(open->op_rflags);
        WRITE32(2);
@@ -2148,8 +2161,8 @@ nfsd4_encode_open(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_op
        case NFS4_OPEN_DELEGATE_NONE:
                break;
        case NFS4_OPEN_DELEGATE_READ:
-               RESERVE_SPACE(20 + sizeof(stateid_t));
-               WRITEMEM(&open->op_delegate_stateid, sizeof(stateid_t));
+               nfsd4_encode_stateid(resp, &open->op_delegate_stateid);
+               RESERVE_SPACE(20);
                WRITE32(open->op_recall);
 
                /*
@@ -2162,8 +2175,8 @@ nfsd4_encode_open(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_op
                ADJUST_ARGS();
                break;
        case NFS4_OPEN_DELEGATE_WRITE:
-               RESERVE_SPACE(32 + sizeof(stateid_t));
-               WRITEMEM(&open->op_delegate_stateid, sizeof(stateid_t));
+               nfsd4_encode_stateid(resp, &open->op_delegate_stateid);
+               RESERVE_SPACE(32);
                WRITE32(0);
 
                /*
@@ -2195,13 +2208,9 @@ static __be32
 nfsd4_encode_open_confirm(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_open_confirm *oc)
 {
        ENCODE_SEQID_OP_HEAD;
-                                       
-       if (!nfserr) {
-               RESERVE_SPACE(sizeof(stateid_t));
-               WRITE32(oc->oc_resp_stateid.si_generation);
-               WRITEMEM(&oc->oc_resp_stateid.si_opaque, sizeof(stateid_opaque_t));
-               ADJUST_ARGS();
-       }
+
+       if (!nfserr)
+               nfsd4_encode_stateid(resp, &oc->oc_resp_stateid);
 
        ENCODE_SEQID_OP_TAIL(oc->oc_stateowner);
        return nfserr;
@@ -2211,13 +2220,9 @@ static __be32
 nfsd4_encode_open_downgrade(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_open_downgrade *od)
 {
        ENCODE_SEQID_OP_HEAD;
-                                       
-       if (!nfserr) {
-               RESERVE_SPACE(sizeof(stateid_t));
-               WRITE32(od->od_stateid.si_generation);
-               WRITEMEM(&od->od_stateid.si_opaque, sizeof(stateid_opaque_t));
-               ADJUST_ARGS();
-       }
+
+       if (!nfserr)
+               nfsd4_encode_stateid(resp, &od->od_stateid);
 
        ENCODE_SEQID_OP_TAIL(od->od_stateowner);
        return nfserr;
index c53e65f8f3a2b460d6c01424094b29c8082843a4..97543df58242f1bc54244aeac633a4fc2e139c8f 100644 (file)
@@ -614,10 +614,9 @@ static ssize_t __write_ports(struct file *file, char *buf, size_t size)
                        return -EINVAL;
                err = nfsd_create_serv();
                if (!err) {
-                       int proto = 0;
-                       err = svc_addsock(nfsd_serv, fd, buf, &proto);
+                       err = svc_addsock(nfsd_serv, fd, buf);
                        if (err >= 0) {
-                               err = lockd_up(proto);
+                               err = lockd_up();
                                if (err < 0)
                                        svc_sock_names(buf+strlen(buf)+1, nfsd_serv, buf);
                        }
index ea37c96f04454e7f1513ca1222aa4eba86f98fb0..cd25d91895a19fc0906b3e5b1891bed123cff15b 100644 (file)
@@ -302,17 +302,27 @@ fh_verify(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, int access)
        if (error)
                goto out;
 
-       if (!(access & NFSD_MAY_LOCK)) {
-               /*
-                * pseudoflavor restrictions are not enforced on NLM,
-                * which clients virtually always use auth_sys for,
-                * even while using RPCSEC_GSS for NFS.
-                */
-               error = check_nfsd_access(exp, rqstp);
-               if (error)
-                       goto out;
-       }
+       /*
+        * pseudoflavor restrictions are not enforced on NLM,
+        * which clients virtually always use auth_sys for,
+        * even while using RPCSEC_GSS for NFS.
+        */
+       if (access & NFSD_MAY_LOCK)
+               goto skip_pseudoflavor_check;
+       /*
+        * Clients may expect to be able to use auth_sys during mount,
+        * even if they use gss for everything else; see section 2.3.2
+        * of rfc 2623.
+        */
+       if (access & NFSD_MAY_BYPASS_GSS_ON_ROOT
+                       && exp->ex_path.dentry == dentry)
+               goto skip_pseudoflavor_check;
+
+       error = check_nfsd_access(exp, rqstp);
+       if (error)
+               goto out;
 
+skip_pseudoflavor_check:
        /* Finally, check access permissions. */
        error = nfsd_permission(rqstp, exp, dentry, access);
 
index 0766f95d236a23cf73f247bea43e4d0b28c61844..5cffeca7acef326d87a804eeb87e26bc6c11b67e 100644 (file)
@@ -65,7 +65,8 @@ nfsd_proc_getattr(struct svc_rqst *rqstp, struct nfsd_fhandle  *argp,
        dprintk("nfsd: GETATTR  %s\n", SVCFH_fmt(&argp->fh));
 
        fh_copy(&resp->fh, &argp->fh);
-       nfserr = fh_verify(rqstp, &resp->fh, 0, NFSD_MAY_NOP);
+       nfserr = fh_verify(rqstp, &resp->fh, 0,
+                       NFSD_MAY_NOP | NFSD_MAY_BYPASS_GSS_ON_ROOT);
        return nfsd_return_attrs(nfserr, resp);
 }
 
@@ -521,7 +522,8 @@ nfsd_proc_statfs(struct svc_rqst * rqstp, struct nfsd_fhandle   *argp,
 
        dprintk("nfsd: STATFS   %s\n", SVCFH_fmt(&argp->fh));
 
-       nfserr = nfsd_statfs(rqstp, &argp->fh, &resp->stats);
+       nfserr = nfsd_statfs(rqstp, &argp->fh, &resp->stats,
+                       NFSD_MAY_BYPASS_GSS_ON_ROOT);
        fh_put(&argp->fh);
        return nfserr;
 }
index 80292ff5e924a15ff0130866878cf7669cfd041f..59eeb46f82c5e842c528ff63a905347c2015e15e 100644 (file)
@@ -229,6 +229,7 @@ int nfsd_create_serv(void)
 
        atomic_set(&nfsd_busy, 0);
        nfsd_serv = svc_create_pooled(&nfsd_program, nfsd_max_blksize,
+                                     AF_INET,
                                      nfsd_last_thread, nfsd, THIS_MODULE);
        if (nfsd_serv == NULL)
                err = -ENOMEM;
@@ -243,25 +244,20 @@ static int nfsd_init_socks(int port)
        if (!list_empty(&nfsd_serv->sv_permsocks))
                return 0;
 
-       error = lockd_up(IPPROTO_UDP);
-       if (error >= 0) {
-               error = svc_create_xprt(nfsd_serv, "udp", port,
+       error = svc_create_xprt(nfsd_serv, "udp", port,
                                        SVC_SOCK_DEFAULTS);
-               if (error < 0)
-                       lockd_down();
-       }
        if (error < 0)
                return error;
 
-       error = lockd_up(IPPROTO_TCP);
-       if (error >= 0) {
-               error = svc_create_xprt(nfsd_serv, "tcp", port,
+       error = svc_create_xprt(nfsd_serv, "tcp", port,
                                        SVC_SOCK_DEFAULTS);
-               if (error < 0)
-                       lockd_down();
-       }
        if (error < 0)
                return error;
+
+       error = lockd_up();
+       if (error < 0)
+               return error;
+
        return 0;
 }
 
index 18060bed5267451c005345ef19f5a270ee5143aa..aa1d0d6489a119a3e8e00aa301cc9a9719006087 100644 (file)
@@ -83,7 +83,6 @@ struct raparm_hbucket {
        spinlock_t              pb_lock;
 } ____cacheline_aligned_in_smp;
 
-static struct raparms *                raparml;
 #define RAPARM_HASH_BITS       4
 #define RAPARM_HASH_SIZE       (1<<RAPARM_HASH_BITS)
 #define RAPARM_HASH_MASK       (RAPARM_HASH_SIZE-1)
@@ -1866,9 +1865,9 @@ out:
  * N.B. After this call fhp needs an fh_put
  */
 __be32
-nfsd_statfs(struct svc_rqst *rqstp, struct svc_fh *fhp, struct kstatfs *stat)
+nfsd_statfs(struct svc_rqst *rqstp, struct svc_fh *fhp, struct kstatfs *stat, int access)
 {
-       __be32 err = fh_verify(rqstp, fhp, 0, NFSD_MAY_NOP);
+       __be32 err = fh_verify(rqstp, fhp, 0, NFSD_MAY_NOP | access);
        if (!err && vfs_statfs(fhp->fh_dentry,stat))
                err = nfserr_io;
        return err;
@@ -1966,11 +1965,20 @@ nfsd_permission(struct svc_rqst *rqstp, struct svc_export *exp,
 void
 nfsd_racache_shutdown(void)
 {
-       if (!raparml)
-               return;
+       struct raparms *raparm, *last_raparm;
+       unsigned int i;
+
        dprintk("nfsd: freeing readahead buffers.\n");
-       kfree(raparml);
-       raparml = NULL;
+
+       for (i = 0; i < RAPARM_HASH_SIZE; i++) {
+               raparm = raparm_hash[i].pb_head;
+               while(raparm) {
+                       last_raparm = raparm;
+                       raparm = raparm->p_next;
+                       kfree(last_raparm);
+               }
+               raparm_hash[i].pb_head = NULL;
+       }
 }
 /*
  * Initialize readahead param cache
@@ -1981,35 +1989,38 @@ nfsd_racache_init(int cache_size)
        int     i;
        int     j = 0;
        int     nperbucket;
+       struct raparms **raparm = NULL;
 
 
-       if (raparml)
+       if (raparm_hash[0].pb_head)
                return 0;
-       if (cache_size < 2*RAPARM_HASH_SIZE)
-               cache_size = 2*RAPARM_HASH_SIZE;
-       raparml = kcalloc(cache_size, sizeof(struct raparms), GFP_KERNEL);
-
-       if (!raparml) {
-               printk(KERN_WARNING
-                       "nfsd: Could not allocate memory read-ahead cache.\n");
-               return -ENOMEM;
-       }
+       nperbucket = DIV_ROUND_UP(cache_size, RAPARM_HASH_SIZE);
+       if (nperbucket < 2)
+               nperbucket = 2;
+       cache_size = nperbucket * RAPARM_HASH_SIZE;
 
        dprintk("nfsd: allocating %d readahead buffers.\n", cache_size);
-       for (i = 0 ; i < RAPARM_HASH_SIZE ; i++) {
-               raparm_hash[i].pb_head = NULL;
+
+       for (i = 0; i < RAPARM_HASH_SIZE; i++) {
                spin_lock_init(&raparm_hash[i].pb_lock);
-       }
-       nperbucket = DIV_ROUND_UP(cache_size, RAPARM_HASH_SIZE);
-       for (i = 0; i < cache_size - 1; i++) {
-               if (i % nperbucket == 0)
-                       raparm_hash[j++].pb_head = raparml + i;
-               if (i % nperbucket < nperbucket-1)
-                       raparml[i].p_next = raparml + i + 1;
+
+               raparm = &raparm_hash[i].pb_head;
+               for (j = 0; j < nperbucket; j++) {
+                       *raparm = kzalloc(sizeof(struct raparms), GFP_KERNEL);
+                       if (!*raparm)
+                               goto out_nomem;
+                       raparm = &(*raparm)->p_next;
+               }
+               *raparm = NULL;
        }
 
        nfsdstats.ra_size = cache_size;
        return 0;
+
+out_nomem:
+       dprintk("nfsd: kmalloc failed, freeing readahead buffers\n");
+       nfsd_racache_shutdown();
+       return -ENOMEM;
 }
 
 #if defined(CONFIG_NFSD_V2_ACL) || defined(CONFIG_NFSD_V3_ACL)
index f6956de56fdb8e96b0e592f5ed4b95f98a7d3320..589dcdfdfe3cfebab34e7186f46a09407985e774 100644 (file)
@@ -34,7 +34,8 @@ ocfs2-objs := \
        symlink.o               \
        sysfile.o               \
        uptodate.o              \
-       ver.o
+       ver.o                   \
+       xattr.o
 
 ocfs2_stackglue-objs := stackglue.o
 ocfs2_stack_o2cb-objs := stack_o2cb.o
index 29ff57ec5d1f97047549e69f97a5d426b3e60af2..0cc2deb9394c02e1a961c0a1366ae5737b8f32bc 100644 (file)
 
 #include "buffer_head_io.h"
 
+
+/*
+ * Operations for a specific extent tree type.
+ *
+ * To implement an on-disk btree (extent tree) type in ocfs2, add
+ * an ocfs2_extent_tree_operations structure and the matching
+ * ocfs2_init_<thingy>_extent_tree() function.  That's pretty much it
+ * for the allocation portion of the extent tree.
+ */
+struct ocfs2_extent_tree_operations {
+       /*
+        * last_eb_blk is the block number of the right most leaf extent
+        * block.  Most on-disk structures containing an extent tree store
+        * this value for fast access.  The ->eo_set_last_eb_blk() and
+        * ->eo_get_last_eb_blk() operations access this value.  They are
+        *  both required.
+        */
+       void (*eo_set_last_eb_blk)(struct ocfs2_extent_tree *et,
+                                  u64 blkno);
+       u64 (*eo_get_last_eb_blk)(struct ocfs2_extent_tree *et);
+
+       /*
+        * The on-disk structure usually keeps track of how many total
+        * clusters are stored in this extent tree.  This function updates
+        * that value.  new_clusters is the delta, and must be
+        * added to the total.  Required.
+        */
+       void (*eo_update_clusters)(struct inode *inode,
+                                  struct ocfs2_extent_tree *et,
+                                  u32 new_clusters);
+
+       /*
+        * If ->eo_insert_check() exists, it is called before rec is
+        * inserted into the extent tree.  It is optional.
+        */
+       int (*eo_insert_check)(struct inode *inode,
+                              struct ocfs2_extent_tree *et,
+                              struct ocfs2_extent_rec *rec);
+       int (*eo_sanity_check)(struct inode *inode, struct ocfs2_extent_tree *et);
+
+       /*
+        * --------------------------------------------------------------
+        * The remaining are internal to ocfs2_extent_tree and don't have
+        * accessor functions
+        */
+
+       /*
+        * ->eo_fill_root_el() takes et->et_object and sets et->et_root_el.
+        * It is required.
+        */
+       void (*eo_fill_root_el)(struct ocfs2_extent_tree *et);
+
+       /*
+        * ->eo_fill_max_leaf_clusters sets et->et_max_leaf_clusters if
+        * it exists.  If it does not, et->et_max_leaf_clusters is set
+        * to 0 (unlimited).  Optional.
+        */
+       void (*eo_fill_max_leaf_clusters)(struct inode *inode,
+                                         struct ocfs2_extent_tree *et);
+};
+
+
+/*
+ * Pre-declare ocfs2_dinode_et_ops so we can use it as a sanity check
+ * in the methods.
+ */
+static u64 ocfs2_dinode_get_last_eb_blk(struct ocfs2_extent_tree *et);
+static void ocfs2_dinode_set_last_eb_blk(struct ocfs2_extent_tree *et,
+                                        u64 blkno);
+static void ocfs2_dinode_update_clusters(struct inode *inode,
+                                        struct ocfs2_extent_tree *et,
+                                        u32 clusters);
+static int ocfs2_dinode_insert_check(struct inode *inode,
+                                    struct ocfs2_extent_tree *et,
+                                    struct ocfs2_extent_rec *rec);
+static int ocfs2_dinode_sanity_check(struct inode *inode,
+                                    struct ocfs2_extent_tree *et);
+static void ocfs2_dinode_fill_root_el(struct ocfs2_extent_tree *et);
+static struct ocfs2_extent_tree_operations ocfs2_dinode_et_ops = {
+       .eo_set_last_eb_blk     = ocfs2_dinode_set_last_eb_blk,
+       .eo_get_last_eb_blk     = ocfs2_dinode_get_last_eb_blk,
+       .eo_update_clusters     = ocfs2_dinode_update_clusters,
+       .eo_insert_check        = ocfs2_dinode_insert_check,
+       .eo_sanity_check        = ocfs2_dinode_sanity_check,
+       .eo_fill_root_el        = ocfs2_dinode_fill_root_el,
+};
+
+static void ocfs2_dinode_set_last_eb_blk(struct ocfs2_extent_tree *et,
+                                        u64 blkno)
+{
+       struct ocfs2_dinode *di = et->et_object;
+
+       BUG_ON(et->et_ops != &ocfs2_dinode_et_ops);
+       di->i_last_eb_blk = cpu_to_le64(blkno);
+}
+
+static u64 ocfs2_dinode_get_last_eb_blk(struct ocfs2_extent_tree *et)
+{
+       struct ocfs2_dinode *di = et->et_object;
+
+       BUG_ON(et->et_ops != &ocfs2_dinode_et_ops);
+       return le64_to_cpu(di->i_last_eb_blk);
+}
+
+static void ocfs2_dinode_update_clusters(struct inode *inode,
+                                        struct ocfs2_extent_tree *et,
+                                        u32 clusters)
+{
+       struct ocfs2_dinode *di = et->et_object;
+
+       le32_add_cpu(&di->i_clusters, clusters);
+       spin_lock(&OCFS2_I(inode)->ip_lock);
+       OCFS2_I(inode)->ip_clusters = le32_to_cpu(di->i_clusters);
+       spin_unlock(&OCFS2_I(inode)->ip_lock);
+}
+
+static int ocfs2_dinode_insert_check(struct inode *inode,
+                                    struct ocfs2_extent_tree *et,
+                                    struct ocfs2_extent_rec *rec)
+{
+       struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
+
+       BUG_ON(OCFS2_I(inode)->ip_dyn_features & OCFS2_INLINE_DATA_FL);
+       mlog_bug_on_msg(!ocfs2_sparse_alloc(osb) &&
+                       (OCFS2_I(inode)->ip_clusters != rec->e_cpos),
+                       "Device %s, asking for sparse allocation: inode %llu, "
+                       "cpos %u, clusters %u\n",
+                       osb->dev_str,
+                       (unsigned long long)OCFS2_I(inode)->ip_blkno,
+                       rec->e_cpos,
+                       OCFS2_I(inode)->ip_clusters);
+
+       return 0;
+}
+
+static int ocfs2_dinode_sanity_check(struct inode *inode,
+                                    struct ocfs2_extent_tree *et)
+{
+       int ret = 0;
+       struct ocfs2_dinode *di;
+
+       BUG_ON(et->et_ops != &ocfs2_dinode_et_ops);
+
+       di = et->et_object;
+       if (!OCFS2_IS_VALID_DINODE(di)) {
+               ret = -EIO;
+               ocfs2_error(inode->i_sb,
+                       "Inode %llu has invalid path root",
+                       (unsigned long long)OCFS2_I(inode)->ip_blkno);
+       }
+
+       return ret;
+}
+
+static void ocfs2_dinode_fill_root_el(struct ocfs2_extent_tree *et)
+{
+       struct ocfs2_dinode *di = et->et_object;
+
+       et->et_root_el = &di->id2.i_list;
+}
+
+
+static void ocfs2_xattr_value_fill_root_el(struct ocfs2_extent_tree *et)
+{
+       struct ocfs2_xattr_value_root *xv = et->et_object;
+
+       et->et_root_el = &xv->xr_list;
+}
+
+static void ocfs2_xattr_value_set_last_eb_blk(struct ocfs2_extent_tree *et,
+                                             u64 blkno)
+{
+       struct ocfs2_xattr_value_root *xv =
+               (struct ocfs2_xattr_value_root *)et->et_object;
+
+       xv->xr_last_eb_blk = cpu_to_le64(blkno);
+}
+
+static u64 ocfs2_xattr_value_get_last_eb_blk(struct ocfs2_extent_tree *et)
+{
+       struct ocfs2_xattr_value_root *xv =
+               (struct ocfs2_xattr_value_root *) et->et_object;
+
+       return le64_to_cpu(xv->xr_last_eb_blk);
+}
+
+static void ocfs2_xattr_value_update_clusters(struct inode *inode,
+                                             struct ocfs2_extent_tree *et,
+                                             u32 clusters)
+{
+       struct ocfs2_xattr_value_root *xv =
+               (struct ocfs2_xattr_value_root *)et->et_object;
+
+       le32_add_cpu(&xv->xr_clusters, clusters);
+}
+
+static struct ocfs2_extent_tree_operations ocfs2_xattr_value_et_ops = {
+       .eo_set_last_eb_blk     = ocfs2_xattr_value_set_last_eb_blk,
+       .eo_get_last_eb_blk     = ocfs2_xattr_value_get_last_eb_blk,
+       .eo_update_clusters     = ocfs2_xattr_value_update_clusters,
+       .eo_fill_root_el        = ocfs2_xattr_value_fill_root_el,
+};
+
+static void ocfs2_xattr_tree_fill_root_el(struct ocfs2_extent_tree *et)
+{
+       struct ocfs2_xattr_block *xb = et->et_object;
+
+       et->et_root_el = &xb->xb_attrs.xb_root.xt_list;
+}
+
+static void ocfs2_xattr_tree_fill_max_leaf_clusters(struct inode *inode,
+                                                   struct ocfs2_extent_tree *et)
+{
+       et->et_max_leaf_clusters =
+               ocfs2_clusters_for_bytes(inode->i_sb,
+                                        OCFS2_MAX_XATTR_TREE_LEAF_SIZE);
+}
+
+static void ocfs2_xattr_tree_set_last_eb_blk(struct ocfs2_extent_tree *et,
+                                            u64 blkno)
+{
+       struct ocfs2_xattr_block *xb = et->et_object;
+       struct ocfs2_xattr_tree_root *xt = &xb->xb_attrs.xb_root;
+
+       xt->xt_last_eb_blk = cpu_to_le64(blkno);
+}
+
+static u64 ocfs2_xattr_tree_get_last_eb_blk(struct ocfs2_extent_tree *et)
+{
+       struct ocfs2_xattr_block *xb = et->et_object;
+       struct ocfs2_xattr_tree_root *xt = &xb->xb_attrs.xb_root;
+
+       return le64_to_cpu(xt->xt_last_eb_blk);
+}
+
+static void ocfs2_xattr_tree_update_clusters(struct inode *inode,
+                                            struct ocfs2_extent_tree *et,
+                                            u32 clusters)
+{
+       struct ocfs2_xattr_block *xb = et->et_object;
+
+       le32_add_cpu(&xb->xb_attrs.xb_root.xt_clusters, clusters);
+}
+
+static struct ocfs2_extent_tree_operations ocfs2_xattr_tree_et_ops = {
+       .eo_set_last_eb_blk     = ocfs2_xattr_tree_set_last_eb_blk,
+       .eo_get_last_eb_blk     = ocfs2_xattr_tree_get_last_eb_blk,
+       .eo_update_clusters     = ocfs2_xattr_tree_update_clusters,
+       .eo_fill_root_el        = ocfs2_xattr_tree_fill_root_el,
+       .eo_fill_max_leaf_clusters = ocfs2_xattr_tree_fill_max_leaf_clusters,
+};
+
+static void __ocfs2_init_extent_tree(struct ocfs2_extent_tree *et,
+                                    struct inode *inode,
+                                    struct buffer_head *bh,
+                                    void *obj,
+                                    struct ocfs2_extent_tree_operations *ops)
+{
+       et->et_ops = ops;
+       et->et_root_bh = bh;
+       if (!obj)
+               obj = (void *)bh->b_data;
+       et->et_object = obj;
+
+       et->et_ops->eo_fill_root_el(et);
+       if (!et->et_ops->eo_fill_max_leaf_clusters)
+               et->et_max_leaf_clusters = 0;
+       else
+               et->et_ops->eo_fill_max_leaf_clusters(inode, et);
+}
+
+void ocfs2_init_dinode_extent_tree(struct ocfs2_extent_tree *et,
+                                  struct inode *inode,
+                                  struct buffer_head *bh)
+{
+       __ocfs2_init_extent_tree(et, inode, bh, NULL, &ocfs2_dinode_et_ops);
+}
+
+void ocfs2_init_xattr_tree_extent_tree(struct ocfs2_extent_tree *et,
+                                      struct inode *inode,
+                                      struct buffer_head *bh)
+{
+       __ocfs2_init_extent_tree(et, inode, bh, NULL,
+                                &ocfs2_xattr_tree_et_ops);
+}
+
+void ocfs2_init_xattr_value_extent_tree(struct ocfs2_extent_tree *et,
+                                       struct inode *inode,
+                                       struct buffer_head *bh,
+                                       struct ocfs2_xattr_value_root *xv)
+{
+       __ocfs2_init_extent_tree(et, inode, bh, xv,
+                                &ocfs2_xattr_value_et_ops);
+}
+
+static inline void ocfs2_et_set_last_eb_blk(struct ocfs2_extent_tree *et,
+                                           u64 new_last_eb_blk)
+{
+       et->et_ops->eo_set_last_eb_blk(et, new_last_eb_blk);
+}
+
+static inline u64 ocfs2_et_get_last_eb_blk(struct ocfs2_extent_tree *et)
+{
+       return et->et_ops->eo_get_last_eb_blk(et);
+}
+
+static inline void ocfs2_et_update_clusters(struct inode *inode,
+                                           struct ocfs2_extent_tree *et,
+                                           u32 clusters)
+{
+       et->et_ops->eo_update_clusters(inode, et, clusters);
+}
+
+static inline int ocfs2_et_insert_check(struct inode *inode,
+                                       struct ocfs2_extent_tree *et,
+                                       struct ocfs2_extent_rec *rec)
+{
+       int ret = 0;
+
+       if (et->et_ops->eo_insert_check)
+               ret = et->et_ops->eo_insert_check(inode, et, rec);
+       return ret;
+}
+
+static inline int ocfs2_et_sanity_check(struct inode *inode,
+                                       struct ocfs2_extent_tree *et)
+{
+       int ret = 0;
+
+       if (et->et_ops->eo_sanity_check)
+               ret = et->et_ops->eo_sanity_check(inode, et);
+       return ret;
+}
+
 static void ocfs2_free_truncate_context(struct ocfs2_truncate_context *tc);
 static int ocfs2_cache_extent_block_free(struct ocfs2_cached_dealloc_ctxt *ctxt,
                                         struct ocfs2_extent_block *eb);
@@ -204,17 +538,6 @@ static struct ocfs2_path *ocfs2_new_path(struct buffer_head *root_bh,
        return path;
 }
 
-/*
- * Allocate and initialize a new path based on a disk inode tree.
- */
-static struct ocfs2_path *ocfs2_new_inode_path(struct buffer_head *di_bh)
-{
-       struct ocfs2_dinode *di = (struct ocfs2_dinode *)di_bh->b_data;
-       struct ocfs2_extent_list *el = &di->id2.i_list;
-
-       return ocfs2_new_path(di_bh, el);
-}
-
 /*
  * Convenience function to journal all components in a path.
  */
@@ -368,39 +691,35 @@ struct ocfs2_merge_ctxt {
  */
 int ocfs2_num_free_extents(struct ocfs2_super *osb,
                           struct inode *inode,
-                          struct ocfs2_dinode *fe)
+                          struct ocfs2_extent_tree *et)
 {
        int retval;
-       struct ocfs2_extent_list *el;
+       struct ocfs2_extent_list *el = NULL;
        struct ocfs2_extent_block *eb;
        struct buffer_head *eb_bh = NULL;
+       u64 last_eb_blk = 0;
 
        mlog_entry_void();
 
-       if (!OCFS2_IS_VALID_DINODE(fe)) {
-               OCFS2_RO_ON_INVALID_DINODE(inode->i_sb, fe);
-               retval = -EIO;
-               goto bail;
-       }
+       el = et->et_root_el;
+       last_eb_blk = ocfs2_et_get_last_eb_blk(et);
 
-       if (fe->i_last_eb_blk) {
-               retval = ocfs2_read_block(osb, le64_to_cpu(fe->i_last_eb_blk),
-                                         &eb_bh, OCFS2_BH_CACHED, inode);
+       if (last_eb_blk) {
+               retval = ocfs2_read_block(inode, last_eb_blk,
+                                         &eb_bh);
                if (retval < 0) {
                        mlog_errno(retval);
                        goto bail;
                }
                eb = (struct ocfs2_extent_block *) eb_bh->b_data;
                el = &eb->h_list;
-       } else
-               el = &fe->id2.i_list;
+       }
 
        BUG_ON(el->l_tree_depth != 0);
 
        retval = le16_to_cpu(el->l_count) - le16_to_cpu(el->l_next_free_rec);
 bail:
-       if (eb_bh)
-               brelse(eb_bh);
+       brelse(eb_bh);
 
        mlog_exit(retval);
        return retval;
@@ -486,8 +805,7 @@ static int ocfs2_create_new_meta_bhs(struct ocfs2_super *osb,
 bail:
        if (status < 0) {
                for(i = 0; i < wanted; i++) {
-                       if (bhs[i])
-                               brelse(bhs[i]);
+                       brelse(bhs[i]);
                        bhs[i] = NULL;
                }
        }
@@ -531,7 +849,7 @@ static inline u32 ocfs2_sum_rightmost_rec(struct ocfs2_extent_list  *el)
 static int ocfs2_add_branch(struct ocfs2_super *osb,
                            handle_t *handle,
                            struct inode *inode,
-                           struct buffer_head *fe_bh,
+                           struct ocfs2_extent_tree *et,
                            struct buffer_head *eb_bh,
                            struct buffer_head **last_eb_bh,
                            struct ocfs2_alloc_context *meta_ac)
@@ -540,7 +858,6 @@ static int ocfs2_add_branch(struct ocfs2_super *osb,
        u64 next_blkno, new_last_eb_blk;
        struct buffer_head *bh;
        struct buffer_head **new_eb_bhs = NULL;
-       struct ocfs2_dinode *fe;
        struct ocfs2_extent_block *eb;
        struct ocfs2_extent_list  *eb_el;
        struct ocfs2_extent_list  *el;
@@ -550,13 +867,11 @@ static int ocfs2_add_branch(struct ocfs2_super *osb,
 
        BUG_ON(!last_eb_bh || !*last_eb_bh);
 
-       fe = (struct ocfs2_dinode *) fe_bh->b_data;
-
        if (eb_bh) {
                eb = (struct ocfs2_extent_block *) eb_bh->b_data;
                el = &eb->h_list;
        } else
-               el = &fe->id2.i_list;
+               el = et->et_root_el;
 
        /* we never add a branch to a leaf. */
        BUG_ON(!el->l_tree_depth);
@@ -646,7 +961,7 @@ static int ocfs2_add_branch(struct ocfs2_super *osb,
                mlog_errno(status);
                goto bail;
        }
-       status = ocfs2_journal_access(handle, inode, fe_bh,
+       status = ocfs2_journal_access(handle, inode, et->et_root_bh,
                                      OCFS2_JOURNAL_ACCESS_WRITE);
        if (status < 0) {
                mlog_errno(status);
@@ -662,7 +977,7 @@ static int ocfs2_add_branch(struct ocfs2_super *osb,
        }
 
        /* Link the new branch into the rest of the tree (el will
-        * either be on the fe, or the extent block passed in. */
+        * either be on the root_bh, or the extent block passed in. */
        i = le16_to_cpu(el->l_next_free_rec);
        el->l_recs[i].e_blkno = cpu_to_le64(next_blkno);
        el->l_recs[i].e_cpos = cpu_to_le32(new_cpos);
@@ -671,7 +986,7 @@ static int ocfs2_add_branch(struct ocfs2_super *osb,
 
        /* fe needs a new last extent block pointer, as does the
         * next_leaf on the previously last-extent-block. */
-       fe->i_last_eb_blk = cpu_to_le64(new_last_eb_blk);
+       ocfs2_et_set_last_eb_blk(et, new_last_eb_blk);
 
        eb = (struct ocfs2_extent_block *) (*last_eb_bh)->b_data;
        eb->h_next_leaf_blk = cpu_to_le64(new_last_eb_blk);
@@ -679,7 +994,7 @@ static int ocfs2_add_branch(struct ocfs2_super *osb,
        status = ocfs2_journal_dirty(handle, *last_eb_bh);
        if (status < 0)
                mlog_errno(status);
-       status = ocfs2_journal_dirty(handle, fe_bh);
+       status = ocfs2_journal_dirty(handle, et->et_root_bh);
        if (status < 0)
                mlog_errno(status);
        if (eb_bh) {
@@ -700,8 +1015,7 @@ static int ocfs2_add_branch(struct ocfs2_super *osb,
 bail:
        if (new_eb_bhs) {
                for (i = 0; i < new_blocks; i++)
-                       if (new_eb_bhs[i])
-                               brelse(new_eb_bhs[i]);
+                       brelse(new_eb_bhs[i]);
                kfree(new_eb_bhs);
        }
 
@@ -717,16 +1031,15 @@ bail:
 static int ocfs2_shift_tree_depth(struct ocfs2_super *osb,
                                  handle_t *handle,
                                  struct inode *inode,
-                                 struct buffer_head *fe_bh,
+                                 struct ocfs2_extent_tree *et,
                                  struct ocfs2_alloc_context *meta_ac,
                                  struct buffer_head **ret_new_eb_bh)
 {
        int status, i;
        u32 new_clusters;
        struct buffer_head *new_eb_bh = NULL;
-       struct ocfs2_dinode *fe;
        struct ocfs2_extent_block *eb;
-       struct ocfs2_extent_list  *fe_el;
+       struct ocfs2_extent_list  *root_el;
        struct ocfs2_extent_list  *eb_el;
 
        mlog_entry_void();
@@ -746,8 +1059,7 @@ static int ocfs2_shift_tree_depth(struct ocfs2_super *osb,
        }
 
        eb_el = &eb->h_list;
-       fe = (struct ocfs2_dinode *) fe_bh->b_data;
-       fe_el = &fe->id2.i_list;
+       root_el = et->et_root_el;
 
        status = ocfs2_journal_access(handle, inode, new_eb_bh,
                                      OCFS2_JOURNAL_ACCESS_CREATE);
@@ -756,11 +1068,11 @@ static int ocfs2_shift_tree_depth(struct ocfs2_super *osb,
                goto bail;
        }
 
-       /* copy the fe data into the new extent block */
-       eb_el->l_tree_depth = fe_el->l_tree_depth;
-       eb_el->l_next_free_rec = fe_el->l_next_free_rec;
-       for(i = 0; i < le16_to_cpu(fe_el->l_next_free_rec); i++)
-               eb_el->l_recs[i] = fe_el->l_recs[i];
+       /* copy the root extent list data into the new extent block */
+       eb_el->l_tree_depth = root_el->l_tree_depth;
+       eb_el->l_next_free_rec = root_el->l_next_free_rec;
+       for (i = 0; i < le16_to_cpu(root_el->l_next_free_rec); i++)
+               eb_el->l_recs[i] = root_el->l_recs[i];
 
        status = ocfs2_journal_dirty(handle, new_eb_bh);
        if (status < 0) {
@@ -768,7 +1080,7 @@ static int ocfs2_shift_tree_depth(struct ocfs2_super *osb,
                goto bail;
        }
 
-       status = ocfs2_journal_access(handle, inode, fe_bh,
+       status = ocfs2_journal_access(handle, inode, et->et_root_bh,
                                      OCFS2_JOURNAL_ACCESS_WRITE);
        if (status < 0) {
                mlog_errno(status);
@@ -777,21 +1089,21 @@ static int ocfs2_shift_tree_depth(struct ocfs2_super *osb,
 
        new_clusters = ocfs2_sum_rightmost_rec(eb_el);
 
-       /* update fe now */
-       le16_add_cpu(&fe_el->l_tree_depth, 1);
-       fe_el->l_recs[0].e_cpos = 0;
-       fe_el->l_recs[0].e_blkno = eb->h_blkno;
-       fe_el->l_recs[0].e_int_clusters = cpu_to_le32(new_clusters);
-       for(i = 1; i < le16_to_cpu(fe_el->l_next_free_rec); i++)
-               memset(&fe_el->l_recs[i], 0, sizeof(struct ocfs2_extent_rec));
-       fe_el->l_next_free_rec = cpu_to_le16(1);
+       /* update root_bh now */
+       le16_add_cpu(&root_el->l_tree_depth, 1);
+       root_el->l_recs[0].e_cpos = 0;
+       root_el->l_recs[0].e_blkno = eb->h_blkno;
+       root_el->l_recs[0].e_int_clusters = cpu_to_le32(new_clusters);
+       for (i = 1; i < le16_to_cpu(root_el->l_next_free_rec); i++)
+               memset(&root_el->l_recs[i], 0, sizeof(struct ocfs2_extent_rec));
+       root_el->l_next_free_rec = cpu_to_le16(1);
 
        /* If this is our 1st tree depth shift, then last_eb_blk
         * becomes the allocated extent block */
-       if (fe_el->l_tree_depth == cpu_to_le16(1))
-               fe->i_last_eb_blk = eb->h_blkno;
+       if (root_el->l_tree_depth == cpu_to_le16(1))
+               ocfs2_et_set_last_eb_blk(et, le64_to_cpu(eb->h_blkno));
 
-       status = ocfs2_journal_dirty(handle, fe_bh);
+       status = ocfs2_journal_dirty(handle, et->et_root_bh);
        if (status < 0) {
                mlog_errno(status);
                goto bail;
@@ -801,8 +1113,7 @@ static int ocfs2_shift_tree_depth(struct ocfs2_super *osb,
        new_eb_bh = NULL;
        status = 0;
 bail:
-       if (new_eb_bh)
-               brelse(new_eb_bh);
+       brelse(new_eb_bh);
 
        mlog_exit(status);
        return status;
@@ -817,22 +1128,21 @@ bail:
  * 1) a lowest extent block is found, then we pass it back in
  *    *lowest_eb_bh and return '0'
  *
- * 2) the search fails to find anything, but the dinode has room. We
+ * 2) the search fails to find anything, but the root_el has room. We
  *    pass NULL back in *lowest_eb_bh, but still return '0'
  *
- * 3) the search fails to find anything AND the dinode is full, in
+ * 3) the search fails to find anything AND the root_el is full, in
  *    which case we return > 0
  *
  * return status < 0 indicates an error.
  */
 static int ocfs2_find_branch_target(struct ocfs2_super *osb,
                                    struct inode *inode,
-                                   struct buffer_head *fe_bh,
+                                   struct ocfs2_extent_tree *et,
                                    struct buffer_head **target_bh)
 {
        int status = 0, i;
        u64 blkno;
-       struct ocfs2_dinode *fe;
        struct ocfs2_extent_block *eb;
        struct ocfs2_extent_list  *el;
        struct buffer_head *bh = NULL;
@@ -842,8 +1152,7 @@ static int ocfs2_find_branch_target(struct ocfs2_super *osb,
 
        *target_bh = NULL;
 
-       fe = (struct ocfs2_dinode *) fe_bh->b_data;
-       el = &fe->id2.i_list;
+       el = et->et_root_el;
 
        while(le16_to_cpu(el->l_tree_depth) > 1) {
                if (le16_to_cpu(el->l_next_free_rec) == 0) {
@@ -864,13 +1173,10 @@ static int ocfs2_find_branch_target(struct ocfs2_super *osb,
                        goto bail;
                }
 
-               if (bh) {
-                       brelse(bh);
-                       bh = NULL;
-               }
+               brelse(bh);
+               bh = NULL;
 
-               status = ocfs2_read_block(osb, blkno, &bh, OCFS2_BH_CACHED,
-                                         inode);
+               status = ocfs2_read_block(inode, blkno, &bh);
                if (status < 0) {
                        mlog_errno(status);
                        goto bail;
@@ -886,8 +1192,7 @@ static int ocfs2_find_branch_target(struct ocfs2_super *osb,
 
                if (le16_to_cpu(el->l_next_free_rec) <
                    le16_to_cpu(el->l_count)) {
-                       if (lowest_bh)
-                               brelse(lowest_bh);
+                       brelse(lowest_bh);
                        lowest_bh = bh;
                        get_bh(lowest_bh);
                }
@@ -895,14 +1200,13 @@ static int ocfs2_find_branch_target(struct ocfs2_super *osb,
 
        /* If we didn't find one and the fe doesn't have any room,
         * then return '1' */
-       if (!lowest_bh
-           && (fe->id2.i_list.l_next_free_rec == fe->id2.i_list.l_count))
+       el = et->et_root_el;
+       if (!lowest_bh && (el->l_next_free_rec == el->l_count))
                status = 1;
 
        *target_bh = lowest_bh;
 bail:
-       if (bh)
-               brelse(bh);
+       brelse(bh);
 
        mlog_exit(status);
        return status;
@@ -919,19 +1223,19 @@ bail:
  * *last_eb_bh will be updated by ocfs2_add_branch().
  */
 static int ocfs2_grow_tree(struct inode *inode, handle_t *handle,
-                          struct buffer_head *di_bh, int *final_depth,
+                          struct ocfs2_extent_tree *et, int *final_depth,
                           struct buffer_head **last_eb_bh,
                           struct ocfs2_alloc_context *meta_ac)
 {
        int ret, shift;
-       struct ocfs2_dinode *di = (struct ocfs2_dinode *)di_bh->b_data;
-       int depth = le16_to_cpu(di->id2.i_list.l_tree_depth);
+       struct ocfs2_extent_list *el = et->et_root_el;
+       int depth = le16_to_cpu(el->l_tree_depth);
        struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
        struct buffer_head *bh = NULL;
 
        BUG_ON(meta_ac == NULL);
 
-       shift = ocfs2_find_branch_target(osb, inode, di_bh, &bh);
+       shift = ocfs2_find_branch_target(osb, inode, et, &bh);
        if (shift < 0) {
                ret = shift;
                mlog_errno(ret);
@@ -948,7 +1252,7 @@ static int ocfs2_grow_tree(struct inode *inode, handle_t *handle,
                /* ocfs2_shift_tree_depth will return us a buffer with
                 * the new extent block (so we can pass that to
                 * ocfs2_add_branch). */
-               ret = ocfs2_shift_tree_depth(osb, handle, inode, di_bh,
+               ret = ocfs2_shift_tree_depth(osb, handle, inode, et,
                                             meta_ac, &bh);
                if (ret < 0) {
                        mlog_errno(ret);
@@ -975,7 +1279,7 @@ static int ocfs2_grow_tree(struct inode *inode, handle_t *handle,
        /* call ocfs2_add_branch to add the final part of the tree with
         * the new data. */
        mlog(0, "add branch. bh = %p\n", bh);
-       ret = ocfs2_add_branch(osb, handle, inode, di_bh, bh, last_eb_bh,
+       ret = ocfs2_add_branch(osb, handle, inode, et, bh, last_eb_bh,
                               meta_ac);
        if (ret < 0) {
                mlog_errno(ret);
@@ -1236,8 +1540,7 @@ static int __ocfs2_find_path(struct inode *inode,
 
                brelse(bh);
                bh = NULL;
-               ret = ocfs2_read_block(OCFS2_SB(inode->i_sb), blkno,
-                                      &bh, OCFS2_BH_CACHED, inode);
+               ret = ocfs2_read_block(inode, blkno, &bh);
                if (ret) {
                        mlog_errno(ret);
                        goto out;
@@ -2058,11 +2361,11 @@ static int ocfs2_rotate_subtree_left(struct inode *inode, handle_t *handle,
                                     struct ocfs2_path *right_path,
                                     int subtree_index,
                                     struct ocfs2_cached_dealloc_ctxt *dealloc,
-                                    int *deleted)
+                                    int *deleted,
+                                    struct ocfs2_extent_tree *et)
 {
        int ret, i, del_right_subtree = 0, right_has_empty = 0;
-       struct buffer_head *root_bh, *di_bh = path_root_bh(right_path);
-       struct ocfs2_dinode *di = (struct ocfs2_dinode *)di_bh->b_data;
+       struct buffer_head *root_bh, *et_root_bh = path_root_bh(right_path);
        struct ocfs2_extent_list *right_leaf_el, *left_leaf_el;
        struct ocfs2_extent_block *eb;
 
@@ -2114,7 +2417,7 @@ static int ocfs2_rotate_subtree_left(struct inode *inode, handle_t *handle,
                 * We have to update i_last_eb_blk during the meta
                 * data delete.
                 */
-               ret = ocfs2_journal_access(handle, inode, di_bh,
+               ret = ocfs2_journal_access(handle, inode, et_root_bh,
                                           OCFS2_JOURNAL_ACCESS_WRITE);
                if (ret) {
                        mlog_errno(ret);
@@ -2189,7 +2492,7 @@ static int ocfs2_rotate_subtree_left(struct inode *inode, handle_t *handle,
                ocfs2_update_edge_lengths(inode, handle, left_path);
 
                eb = (struct ocfs2_extent_block *)path_leaf_bh(left_path)->b_data;
-               di->i_last_eb_blk = eb->h_blkno;
+               ocfs2_et_set_last_eb_blk(et, le64_to_cpu(eb->h_blkno));
 
                /*
                 * Removal of the extent in the left leaf was skipped
@@ -2199,7 +2502,7 @@ static int ocfs2_rotate_subtree_left(struct inode *inode, handle_t *handle,
                if (right_has_empty)
                        ocfs2_remove_empty_extent(left_leaf_el);
 
-               ret = ocfs2_journal_dirty(handle, di_bh);
+               ret = ocfs2_journal_dirty(handle, et_root_bh);
                if (ret)
                        mlog_errno(ret);
 
@@ -2322,7 +2625,8 @@ static int __ocfs2_rotate_tree_left(struct inode *inode,
                                    handle_t *handle, int orig_credits,
                                    struct ocfs2_path *path,
                                    struct ocfs2_cached_dealloc_ctxt *dealloc,
-                                   struct ocfs2_path **empty_extent_path)
+                                   struct ocfs2_path **empty_extent_path,
+                                   struct ocfs2_extent_tree *et)
 {
        int ret, subtree_root, deleted;
        u32 right_cpos;
@@ -2395,7 +2699,7 @@ static int __ocfs2_rotate_tree_left(struct inode *inode,
 
                ret = ocfs2_rotate_subtree_left(inode, handle, left_path,
                                                right_path, subtree_root,
-                                               dealloc, &deleted);
+                                               dealloc, &deleted, et);
                if (ret == -EAGAIN) {
                        /*
                         * The rotation has to temporarily stop due to
@@ -2438,29 +2742,20 @@ out:
 }
 
 static int ocfs2_remove_rightmost_path(struct inode *inode, handle_t *handle,
-                                      struct ocfs2_path *path,
-                                      struct ocfs2_cached_dealloc_ctxt *dealloc)
+                               struct ocfs2_path *path,
+                               struct ocfs2_cached_dealloc_ctxt *dealloc,
+                               struct ocfs2_extent_tree *et)
 {
        int ret, subtree_index;
        u32 cpos;
        struct ocfs2_path *left_path = NULL;
-       struct ocfs2_dinode *di;
        struct ocfs2_extent_block *eb;
        struct ocfs2_extent_list *el;
 
-       /*
-        * XXX: This code assumes that the root is an inode, which is
-        * true for now but may change as tree code gets generic.
-        */
-       di = (struct ocfs2_dinode *)path_root_bh(path)->b_data;
-       if (!OCFS2_IS_VALID_DINODE(di)) {
-               ret = -EIO;
-               ocfs2_error(inode->i_sb,
-                           "Inode %llu has invalid path root",
-                           (unsigned long long)OCFS2_I(inode)->ip_blkno);
-               goto out;
-       }
 
+       ret = ocfs2_et_sanity_check(inode, et);
+       if (ret)
+               goto out;
        /*
         * There's two ways we handle this depending on
         * whether path is the only existing one.
@@ -2517,7 +2812,7 @@ static int ocfs2_remove_rightmost_path(struct inode *inode, handle_t *handle,
                ocfs2_update_edge_lengths(inode, handle, left_path);
 
                eb = (struct ocfs2_extent_block *)path_leaf_bh(left_path)->b_data;
-               di->i_last_eb_blk = eb->h_blkno;
+               ocfs2_et_set_last_eb_blk(et, le64_to_cpu(eb->h_blkno));
        } else {
                /*
                 * 'path' is also the leftmost path which
@@ -2528,12 +2823,12 @@ static int ocfs2_remove_rightmost_path(struct inode *inode, handle_t *handle,
                 */
                ocfs2_unlink_path(inode, handle, dealloc, path, 1);
 
-               el = &di->id2.i_list;
+               el = et->et_root_el;
                el->l_tree_depth = 0;
                el->l_next_free_rec = 0;
                memset(&el->l_recs[0], 0, sizeof(struct ocfs2_extent_rec));
 
-               di->i_last_eb_blk = 0;
+               ocfs2_et_set_last_eb_blk(et, 0);
        }
 
        ocfs2_journal_dirty(handle, path_root_bh(path));
@@ -2561,7 +2856,8 @@ out:
  */
 static int ocfs2_rotate_tree_left(struct inode *inode, handle_t *handle,
                                  struct ocfs2_path *path,
-                                 struct ocfs2_cached_dealloc_ctxt *dealloc)
+                                 struct ocfs2_cached_dealloc_ctxt *dealloc,
+                                 struct ocfs2_extent_tree *et)
 {
        int ret, orig_credits = handle->h_buffer_credits;
        struct ocfs2_path *tmp_path = NULL, *restart_path = NULL;
@@ -2575,7 +2871,7 @@ static int ocfs2_rotate_tree_left(struct inode *inode, handle_t *handle,
        if (path->p_tree_depth == 0) {
 rightmost_no_delete:
                /*
-                * In-inode extents. This is trivially handled, so do
+                * Inline extents. This is trivially handled, so do
                 * it up front.
                 */
                ret = ocfs2_rotate_rightmost_leaf_left(inode, handle,
@@ -2629,7 +2925,7 @@ rightmost_no_delete:
                 */
 
                ret = ocfs2_remove_rightmost_path(inode, handle, path,
-                                                 dealloc);
+                                                 dealloc, et);
                if (ret)
                        mlog_errno(ret);
                goto out;
@@ -2641,7 +2937,7 @@ rightmost_no_delete:
         */
 try_rotate:
        ret = __ocfs2_rotate_tree_left(inode, handle, orig_credits, path,
-                                      dealloc, &restart_path);
+                                      dealloc, &restart_path, et);
        if (ret && ret != -EAGAIN) {
                mlog_errno(ret);
                goto out;
@@ -2653,7 +2949,7 @@ try_rotate:
 
                ret = __ocfs2_rotate_tree_left(inode, handle, orig_credits,
                                               tmp_path, dealloc,
-                                              &restart_path);
+                                              &restart_path, et);
                if (ret && ret != -EAGAIN) {
                        mlog_errno(ret);
                        goto out;
@@ -2939,6 +3235,7 @@ static int ocfs2_merge_rec_left(struct inode *inode,
                                handle_t *handle,
                                struct ocfs2_extent_rec *split_rec,
                                struct ocfs2_cached_dealloc_ctxt *dealloc,
+                               struct ocfs2_extent_tree *et,
                                int index)
 {
        int ret, i, subtree_index = 0, has_empty_extent = 0;
@@ -3059,7 +3356,8 @@ static int ocfs2_merge_rec_left(struct inode *inode,
                    le16_to_cpu(el->l_next_free_rec) == 1) {
 
                        ret = ocfs2_remove_rightmost_path(inode, handle,
-                                                         right_path, dealloc);
+                                                         right_path,
+                                                         dealloc, et);
                        if (ret) {
                                mlog_errno(ret);
                                goto out;
@@ -3086,7 +3384,8 @@ static int ocfs2_try_to_merge_extent(struct inode *inode,
                                     int split_index,
                                     struct ocfs2_extent_rec *split_rec,
                                     struct ocfs2_cached_dealloc_ctxt *dealloc,
-                                    struct ocfs2_merge_ctxt *ctxt)
+                                    struct ocfs2_merge_ctxt *ctxt,
+                                    struct ocfs2_extent_tree *et)
 
 {
        int ret = 0;
@@ -3104,7 +3403,7 @@ static int ocfs2_try_to_merge_extent(struct inode *inode,
                 * illegal.
                 */
                ret = ocfs2_rotate_tree_left(inode, handle, path,
-                                            dealloc);
+                                            dealloc, et);
                if (ret) {
                        mlog_errno(ret);
                        goto out;
@@ -3147,7 +3446,8 @@ static int ocfs2_try_to_merge_extent(struct inode *inode,
                BUG_ON(!ocfs2_is_empty_extent(&el->l_recs[0]));
 
                /* The merge left us with an empty extent, remove it. */
-               ret = ocfs2_rotate_tree_left(inode, handle, path, dealloc);
+               ret = ocfs2_rotate_tree_left(inode, handle, path,
+                                            dealloc, et);
                if (ret) {
                        mlog_errno(ret);
                        goto out;
@@ -3161,7 +3461,7 @@ static int ocfs2_try_to_merge_extent(struct inode *inode,
                 */
                ret = ocfs2_merge_rec_left(inode, path,
                                           handle, rec,
-                                          dealloc,
+                                          dealloc, et,
                                           split_index);
 
                if (ret) {
@@ -3170,7 +3470,7 @@ static int ocfs2_try_to_merge_extent(struct inode *inode,
                }
 
                ret = ocfs2_rotate_tree_left(inode, handle, path,
-                                            dealloc);
+                                            dealloc, et);
                /*
                 * Error from this last rotate is not critical, so
                 * print but don't bubble it up.
@@ -3190,7 +3490,7 @@ static int ocfs2_try_to_merge_extent(struct inode *inode,
                        ret = ocfs2_merge_rec_left(inode,
                                                   path,
                                                   handle, split_rec,
-                                                  dealloc,
+                                                  dealloc, et,
                                                   split_index);
                        if (ret) {
                                mlog_errno(ret);
@@ -3213,7 +3513,7 @@ static int ocfs2_try_to_merge_extent(struct inode *inode,
                         * our leaf. Try to rotate it away.
                         */
                        ret = ocfs2_rotate_tree_left(inode, handle, path,
-                                                    dealloc);
+                                                    dealloc, et);
                        if (ret)
                                mlog_errno(ret);
                        ret = 0;
@@ -3347,16 +3647,6 @@ rotate:
        ocfs2_rotate_leaf(el, insert_rec);
 }
 
-static inline void ocfs2_update_dinode_clusters(struct inode *inode,
-                                               struct ocfs2_dinode *di,
-                                               u32 clusters)
-{
-       le32_add_cpu(&di->i_clusters, clusters);
-       spin_lock(&OCFS2_I(inode)->ip_lock);
-       OCFS2_I(inode)->ip_clusters = le32_to_cpu(di->i_clusters);
-       spin_unlock(&OCFS2_I(inode)->ip_lock);
-}
-
 static void ocfs2_adjust_rightmost_records(struct inode *inode,
                                           handle_t *handle,
                                           struct ocfs2_path *path,
@@ -3558,8 +3848,8 @@ static void ocfs2_split_record(struct inode *inode,
 }
 
 /*
- * This function only does inserts on an allocation b-tree. For dinode
- * lists, ocfs2_insert_at_leaf() is called directly.
+ * This function only does inserts on an allocation b-tree. For tree
+ * depth = 0, ocfs2_insert_at_leaf() is called directly.
  *
  * right_path is the path we want to do the actual insert
  * in. left_path should only be passed in if we need to update that
@@ -3656,7 +3946,7 @@ out:
 
 static int ocfs2_do_insert_extent(struct inode *inode,
                                  handle_t *handle,
-                                 struct buffer_head *di_bh,
+                                 struct ocfs2_extent_tree *et,
                                  struct ocfs2_extent_rec *insert_rec,
                                  struct ocfs2_insert_type *type)
 {
@@ -3664,13 +3954,11 @@ static int ocfs2_do_insert_extent(struct inode *inode,
        u32 cpos;
        struct ocfs2_path *right_path = NULL;
        struct ocfs2_path *left_path = NULL;
-       struct ocfs2_dinode *di;
        struct ocfs2_extent_list *el;
 
-       di = (struct ocfs2_dinode *) di_bh->b_data;
-       el = &di->id2.i_list;
+       el = et->et_root_el;
 
-       ret = ocfs2_journal_access(handle, inode, di_bh,
+       ret = ocfs2_journal_access(handle, inode, et->et_root_bh,
                                   OCFS2_JOURNAL_ACCESS_WRITE);
        if (ret) {
                mlog_errno(ret);
@@ -3682,7 +3970,7 @@ static int ocfs2_do_insert_extent(struct inode *inode,
                goto out_update_clusters;
        }
 
-       right_path = ocfs2_new_inode_path(di_bh);
+       right_path = ocfs2_new_path(et->et_root_bh, et->et_root_el);
        if (!right_path) {
                ret = -ENOMEM;
                mlog_errno(ret);
@@ -3732,7 +4020,7 @@ static int ocfs2_do_insert_extent(struct inode *inode,
                 * ocfs2_rotate_tree_right() might have extended the
                 * transaction without re-journaling our tree root.
                 */
-               ret = ocfs2_journal_access(handle, inode, di_bh,
+               ret = ocfs2_journal_access(handle, inode, et->et_root_bh,
                                           OCFS2_JOURNAL_ACCESS_WRITE);
                if (ret) {
                        mlog_errno(ret);
@@ -3757,10 +4045,10 @@ static int ocfs2_do_insert_extent(struct inode *inode,
 
 out_update_clusters:
        if (type->ins_split == SPLIT_NONE)
-               ocfs2_update_dinode_clusters(inode, di,
-                                            le16_to_cpu(insert_rec->e_leaf_clusters));
+               ocfs2_et_update_clusters(inode, et,
+                                        le16_to_cpu(insert_rec->e_leaf_clusters));
 
-       ret = ocfs2_journal_dirty(handle, di_bh);
+       ret = ocfs2_journal_dirty(handle, et->et_root_bh);
        if (ret)
                mlog_errno(ret);
 
@@ -3890,7 +4178,8 @@ out:
 static void ocfs2_figure_contig_type(struct inode *inode,
                                     struct ocfs2_insert_type *insert,
                                     struct ocfs2_extent_list *el,
-                                    struct ocfs2_extent_rec *insert_rec)
+                                    struct ocfs2_extent_rec *insert_rec,
+                                    struct ocfs2_extent_tree *et)
 {
        int i;
        enum ocfs2_contig_type contig_type = CONTIG_NONE;
@@ -3906,6 +4195,21 @@ static void ocfs2_figure_contig_type(struct inode *inode,
                }
        }
        insert->ins_contig = contig_type;
+
+       if (insert->ins_contig != CONTIG_NONE) {
+               struct ocfs2_extent_rec *rec =
+                               &el->l_recs[insert->ins_contig_index];
+               unsigned int len = le16_to_cpu(rec->e_leaf_clusters) +
+                                  le16_to_cpu(insert_rec->e_leaf_clusters);
+
+               /*
+                * Caller might want us to limit the size of extents, don't
+                * calculate contiguousness if we might exceed that limit.
+                */
+               if (et->et_max_leaf_clusters &&
+                   (len > et->et_max_leaf_clusters))
+                       insert->ins_contig = CONTIG_NONE;
+       }
 }
 
 /*
@@ -3914,8 +4218,8 @@ static void ocfs2_figure_contig_type(struct inode *inode,
  * ocfs2_figure_appending_type() will figure out whether we'll have to
  * insert at the tail of the rightmost leaf.
  *
- * This should also work against the dinode list for tree's with 0
- * depth. If we consider the dinode list to be the rightmost leaf node
+ * This should also work against the root extent list for tree's with 0
+ * depth. If we consider the root extent list to be the rightmost leaf node
  * then the logic here makes sense.
  */
 static void ocfs2_figure_appending_type(struct ocfs2_insert_type *insert,
@@ -3966,14 +4270,13 @@ set_tail_append:
  * structure.
  */
 static int ocfs2_figure_insert_type(struct inode *inode,
-                                   struct buffer_head *di_bh,
+                                   struct ocfs2_extent_tree *et,
                                    struct buffer_head **last_eb_bh,
                                    struct ocfs2_extent_rec *insert_rec,
                                    int *free_records,
                                    struct ocfs2_insert_type *insert)
 {
        int ret;
-       struct ocfs2_dinode *di = (struct ocfs2_dinode *)di_bh->b_data;
        struct ocfs2_extent_block *eb;
        struct ocfs2_extent_list *el;
        struct ocfs2_path *path = NULL;
@@ -3981,7 +4284,7 @@ static int ocfs2_figure_insert_type(struct inode *inode,
 
        insert->ins_split = SPLIT_NONE;
 
-       el = &di->id2.i_list;
+       el = et->et_root_el;
        insert->ins_tree_depth = le16_to_cpu(el->l_tree_depth);
 
        if (el->l_tree_depth) {
@@ -3991,9 +4294,7 @@ static int ocfs2_figure_insert_type(struct inode *inode,
                 * ocfs2_figure_insert_type() and ocfs2_add_branch()
                 * may want it later.
                 */
-               ret = ocfs2_read_block(OCFS2_SB(inode->i_sb),
-                                      le64_to_cpu(di->i_last_eb_blk), &bh,
-                                      OCFS2_BH_CACHED, inode);
+               ret = ocfs2_read_block(inode, ocfs2_et_get_last_eb_blk(et), &bh);
                if (ret) {
                        mlog_exit(ret);
                        goto out;
@@ -4014,12 +4315,12 @@ static int ocfs2_figure_insert_type(struct inode *inode,
                le16_to_cpu(el->l_next_free_rec);
 
        if (!insert->ins_tree_depth) {
-               ocfs2_figure_contig_type(inode, insert, el, insert_rec);
+               ocfs2_figure_contig_type(inode, insert, el, insert_rec, et);
                ocfs2_figure_appending_type(insert, el, insert_rec);
                return 0;
        }
 
-       path = ocfs2_new_inode_path(di_bh);
+       path = ocfs2_new_path(et->et_root_bh, et->et_root_el);
        if (!path) {
                ret = -ENOMEM;
                mlog_errno(ret);
@@ -4048,7 +4349,7 @@ static int ocfs2_figure_insert_type(struct inode *inode,
          *     into two types of appends: simple record append, or a
          *     rotate inside the tail leaf.
         */
-       ocfs2_figure_contig_type(inode, insert, el, insert_rec);
+       ocfs2_figure_contig_type(inode, insert, el, insert_rec, et);
 
        /*
         * The insert code isn't quite ready to deal with all cases of
@@ -4069,7 +4370,8 @@ static int ocfs2_figure_insert_type(struct inode *inode,
         * the case that we're doing a tail append, so maybe we can
         * take advantage of that information somehow.
         */
-       if (le64_to_cpu(di->i_last_eb_blk) == path_leaf_bh(path)->b_blocknr) {
+       if (ocfs2_et_get_last_eb_blk(et) ==
+           path_leaf_bh(path)->b_blocknr) {
                /*
                 * Ok, ocfs2_find_path() returned us the rightmost
                 * tree path. This might be an appending insert. There are
@@ -4099,7 +4401,7 @@ out:
 int ocfs2_insert_extent(struct ocfs2_super *osb,
                        handle_t *handle,
                        struct inode *inode,
-                       struct buffer_head *fe_bh,
+                       struct ocfs2_extent_tree *et,
                        u32 cpos,
                        u64 start_blk,
                        u32 new_clusters,
@@ -4112,26 +4414,21 @@ int ocfs2_insert_extent(struct ocfs2_super *osb,
        struct ocfs2_insert_type insert = {0, };
        struct ocfs2_extent_rec rec;
 
-       BUG_ON(OCFS2_I(inode)->ip_dyn_features & OCFS2_INLINE_DATA_FL);
-
        mlog(0, "add %u clusters at position %u to inode %llu\n",
             new_clusters, cpos, (unsigned long long)OCFS2_I(inode)->ip_blkno);
 
-       mlog_bug_on_msg(!ocfs2_sparse_alloc(osb) &&
-                       (OCFS2_I(inode)->ip_clusters != cpos),
-                       "Device %s, asking for sparse allocation: inode %llu, "
-                       "cpos %u, clusters %u\n",
-                       osb->dev_str,
-                       (unsigned long long)OCFS2_I(inode)->ip_blkno, cpos,
-                       OCFS2_I(inode)->ip_clusters);
-
        memset(&rec, 0, sizeof(rec));
        rec.e_cpos = cpu_to_le32(cpos);
        rec.e_blkno = cpu_to_le64(start_blk);
        rec.e_leaf_clusters = cpu_to_le16(new_clusters);
        rec.e_flags = flags;
+       status = ocfs2_et_insert_check(inode, et, &rec);
+       if (status) {
+               mlog_errno(status);
+               goto bail;
+       }
 
-       status = ocfs2_figure_insert_type(inode, fe_bh, &last_eb_bh, &rec,
+       status = ocfs2_figure_insert_type(inode, et, &last_eb_bh, &rec,
                                          &free_records, &insert);
        if (status < 0) {
                mlog_errno(status);
@@ -4145,7 +4442,7 @@ int ocfs2_insert_extent(struct ocfs2_super *osb,
             free_records, insert.ins_tree_depth);
 
        if (insert.ins_contig == CONTIG_NONE && free_records == 0) {
-               status = ocfs2_grow_tree(inode, handle, fe_bh,
+               status = ocfs2_grow_tree(inode, handle, et,
                                         &insert.ins_tree_depth, &last_eb_bh,
                                         meta_ac);
                if (status) {
@@ -4155,17 +4452,124 @@ int ocfs2_insert_extent(struct ocfs2_super *osb,
        }
 
        /* Finally, we can add clusters. This might rotate the tree for us. */
-       status = ocfs2_do_insert_extent(inode, handle, fe_bh, &rec, &insert);
+       status = ocfs2_do_insert_extent(inode, handle, et, &rec, &insert);
        if (status < 0)
                mlog_errno(status);
-       else
+       else if (et->et_ops == &ocfs2_dinode_et_ops)
                ocfs2_extent_map_insert_rec(inode, &rec);
 
 bail:
-       if (last_eb_bh)
-               brelse(last_eb_bh);
+       brelse(last_eb_bh);
+
+       mlog_exit(status);
+       return status;
+}
+
+/*
+ * Allcate and add clusters into the extent b-tree.
+ * The new clusters(clusters_to_add) will be inserted at logical_offset.
+ * The extent b-tree's root is specified by et, and
+ * it is not limited to the file storage. Any extent tree can use this
+ * function if it implements the proper ocfs2_extent_tree.
+ */
+int ocfs2_add_clusters_in_btree(struct ocfs2_super *osb,
+                               struct inode *inode,
+                               u32 *logical_offset,
+                               u32 clusters_to_add,
+                               int mark_unwritten,
+                               struct ocfs2_extent_tree *et,
+                               handle_t *handle,
+                               struct ocfs2_alloc_context *data_ac,
+                               struct ocfs2_alloc_context *meta_ac,
+                               enum ocfs2_alloc_restarted *reason_ret)
+{
+       int status = 0;
+       int free_extents;
+       enum ocfs2_alloc_restarted reason = RESTART_NONE;
+       u32 bit_off, num_bits;
+       u64 block;
+       u8 flags = 0;
+
+       BUG_ON(!clusters_to_add);
+
+       if (mark_unwritten)
+               flags = OCFS2_EXT_UNWRITTEN;
+
+       free_extents = ocfs2_num_free_extents(osb, inode, et);
+       if (free_extents < 0) {
+               status = free_extents;
+               mlog_errno(status);
+               goto leave;
+       }
+
+       /* there are two cases which could cause us to EAGAIN in the
+        * we-need-more-metadata case:
+        * 1) we haven't reserved *any*
+        * 2) we are so fragmented, we've needed to add metadata too
+        *    many times. */
+       if (!free_extents && !meta_ac) {
+               mlog(0, "we haven't reserved any metadata!\n");
+               status = -EAGAIN;
+               reason = RESTART_META;
+               goto leave;
+       } else if ((!free_extents)
+                  && (ocfs2_alloc_context_bits_left(meta_ac)
+                      < ocfs2_extend_meta_needed(et->et_root_el))) {
+               mlog(0, "filesystem is really fragmented...\n");
+               status = -EAGAIN;
+               reason = RESTART_META;
+               goto leave;
+       }
+
+       status = __ocfs2_claim_clusters(osb, handle, data_ac, 1,
+                                       clusters_to_add, &bit_off, &num_bits);
+       if (status < 0) {
+               if (status != -ENOSPC)
+                       mlog_errno(status);
+               goto leave;
+       }
+
+       BUG_ON(num_bits > clusters_to_add);
 
+       /* reserve our write early -- insert_extent may update the inode */
+       status = ocfs2_journal_access(handle, inode, et->et_root_bh,
+                                     OCFS2_JOURNAL_ACCESS_WRITE);
+       if (status < 0) {
+               mlog_errno(status);
+               goto leave;
+       }
+
+       block = ocfs2_clusters_to_blocks(osb->sb, bit_off);
+       mlog(0, "Allocating %u clusters at block %u for inode %llu\n",
+            num_bits, bit_off, (unsigned long long)OCFS2_I(inode)->ip_blkno);
+       status = ocfs2_insert_extent(osb, handle, inode, et,
+                                    *logical_offset, block,
+                                    num_bits, flags, meta_ac);
+       if (status < 0) {
+               mlog_errno(status);
+               goto leave;
+       }
+
+       status = ocfs2_journal_dirty(handle, et->et_root_bh);
+       if (status < 0) {
+               mlog_errno(status);
+               goto leave;
+       }
+
+       clusters_to_add -= num_bits;
+       *logical_offset += num_bits;
+
+       if (clusters_to_add) {
+               mlog(0, "need to alloc once more, wanted = %u\n",
+                    clusters_to_add);
+               status = -EAGAIN;
+               reason = RESTART_TRANS;
+       }
+
+leave:
        mlog_exit(status);
+       if (reason_ret)
+               *reason_ret = reason;
        return status;
 }
 
@@ -4192,7 +4596,7 @@ static void ocfs2_make_right_split_rec(struct super_block *sb,
 static int ocfs2_split_and_insert(struct inode *inode,
                                  handle_t *handle,
                                  struct ocfs2_path *path,
-                                 struct buffer_head *di_bh,
+                                 struct ocfs2_extent_tree *et,
                                  struct buffer_head **last_eb_bh,
                                  int split_index,
                                  struct ocfs2_extent_rec *orig_split_rec,
@@ -4206,7 +4610,6 @@ static int ocfs2_split_and_insert(struct inode *inode,
        struct ocfs2_extent_rec split_rec = *orig_split_rec;
        struct ocfs2_insert_type insert;
        struct ocfs2_extent_block *eb;
-       struct ocfs2_dinode *di;
 
 leftright:
        /*
@@ -4215,8 +4618,7 @@ leftright:
         */
        rec = path_leaf_el(path)->l_recs[split_index];
 
-       di = (struct ocfs2_dinode *)di_bh->b_data;
-       rightmost_el = &di->id2.i_list;
+       rightmost_el = et->et_root_el;
 
        depth = le16_to_cpu(rightmost_el->l_tree_depth);
        if (depth) {
@@ -4227,8 +4629,8 @@ leftright:
 
        if (le16_to_cpu(rightmost_el->l_next_free_rec) ==
            le16_to_cpu(rightmost_el->l_count)) {
-               ret = ocfs2_grow_tree(inode, handle, di_bh, &depth, last_eb_bh,
-                                     meta_ac);
+               ret = ocfs2_grow_tree(inode, handle, et,
+                                     &depth, last_eb_bh, meta_ac);
                if (ret) {
                        mlog_errno(ret);
                        goto out;
@@ -4265,8 +4667,7 @@ leftright:
                do_leftright = 1;
        }
 
-       ret = ocfs2_do_insert_extent(inode, handle, di_bh, &split_rec,
-                                    &insert);
+       ret = ocfs2_do_insert_extent(inode, handle, et, &split_rec, &insert);
        if (ret) {
                mlog_errno(ret);
                goto out;
@@ -4308,8 +4709,9 @@ out:
  * of the tree is required. All other cases will degrade into a less
  * optimal tree layout.
  *
- * last_eb_bh should be the rightmost leaf block for any inode with a
- * btree. Since a split may grow the tree or a merge might shrink it, the caller cannot trust the contents of that buffer after this call.
+ * last_eb_bh should be the rightmost leaf block for any extent
+ * btree. Since a split may grow the tree or a merge might shrink it,
+ * the caller cannot trust the contents of that buffer after this call.
  *
  * This code is optimized for readability - several passes might be
  * made over certain portions of the tree. All of those blocks will
@@ -4317,7 +4719,7 @@ out:
  * extra overhead is not expressed in terms of disk reads.
  */
 static int __ocfs2_mark_extent_written(struct inode *inode,
-                                      struct buffer_head *di_bh,
+                                      struct ocfs2_extent_tree *et,
                                       handle_t *handle,
                                       struct ocfs2_path *path,
                                       int split_index,
@@ -4357,11 +4759,9 @@ static int __ocfs2_mark_extent_written(struct inode *inode,
         */
        if (path->p_tree_depth) {
                struct ocfs2_extent_block *eb;
-               struct ocfs2_dinode *di = (struct ocfs2_dinode *)di_bh->b_data;
 
-               ret = ocfs2_read_block(OCFS2_SB(inode->i_sb),
-                                      le64_to_cpu(di->i_last_eb_blk),
-                                      &last_eb_bh, OCFS2_BH_CACHED, inode);
+               ret = ocfs2_read_block(inode, ocfs2_et_get_last_eb_blk(et),
+                                      &last_eb_bh);
                if (ret) {
                        mlog_exit(ret);
                        goto out;
@@ -4394,7 +4794,7 @@ static int __ocfs2_mark_extent_written(struct inode *inode,
                if (ctxt.c_split_covers_rec)
                        el->l_recs[split_index] = *split_rec;
                else
-                       ret = ocfs2_split_and_insert(inode, handle, path, di_bh,
+                       ret = ocfs2_split_and_insert(inode, handle, path, et,
                                                     &last_eb_bh, split_index,
                                                     split_rec, meta_ac);
                if (ret)
@@ -4402,7 +4802,7 @@ static int __ocfs2_mark_extent_written(struct inode *inode,
        } else {
                ret = ocfs2_try_to_merge_extent(inode, handle, path,
                                                split_index, split_rec,
-                                               dealloc, &ctxt);
+                                               dealloc, &ctxt, et);
                if (ret)
                        mlog_errno(ret);
        }
@@ -4420,7 +4820,8 @@ out:
  *
  * The caller is responsible for passing down meta_ac if we'll need it.
  */
-int ocfs2_mark_extent_written(struct inode *inode, struct buffer_head *di_bh,
+int ocfs2_mark_extent_written(struct inode *inode,
+                             struct ocfs2_extent_tree *et,
                              handle_t *handle, u32 cpos, u32 len, u32 phys,
                              struct ocfs2_alloc_context *meta_ac,
                              struct ocfs2_cached_dealloc_ctxt *dealloc)
@@ -4446,10 +4847,14 @@ int ocfs2_mark_extent_written(struct inode *inode, struct buffer_head *di_bh,
        /*
         * XXX: This should be fixed up so that we just re-insert the
         * next extent records.
+        *
+        * XXX: This is a hack on the extent tree, maybe it should be
+        * an op?
         */
-       ocfs2_extent_map_trunc(inode, 0);
+       if (et->et_ops == &ocfs2_dinode_et_ops)
+               ocfs2_extent_map_trunc(inode, 0);
 
-       left_path = ocfs2_new_inode_path(di_bh);
+       left_path = ocfs2_new_path(et->et_root_bh, et->et_root_el);
        if (!left_path) {
                ret = -ENOMEM;
                mlog_errno(ret);
@@ -4480,8 +4885,9 @@ int ocfs2_mark_extent_written(struct inode *inode, struct buffer_head *di_bh,
        split_rec.e_flags = path_leaf_el(left_path)->l_recs[index].e_flags;
        split_rec.e_flags &= ~OCFS2_EXT_UNWRITTEN;
 
-       ret = __ocfs2_mark_extent_written(inode, di_bh, handle, left_path,
-                                         index, &split_rec, meta_ac, dealloc);
+       ret = __ocfs2_mark_extent_written(inode, et, handle, left_path,
+                                         index, &split_rec, meta_ac,
+                                         dealloc);
        if (ret)
                mlog_errno(ret);
 
@@ -4490,13 +4896,12 @@ out:
        return ret;
 }
 
-static int ocfs2_split_tree(struct inode *inode, struct buffer_head *di_bh,
+static int ocfs2_split_tree(struct inode *inode, struct ocfs2_extent_tree *et,
                            handle_t *handle, struct ocfs2_path *path,
                            int index, u32 new_range,
                            struct ocfs2_alloc_context *meta_ac)
 {
        int ret, depth, credits = handle->h_buffer_credits;
-       struct ocfs2_dinode *di = (struct ocfs2_dinode *)di_bh->b_data;
        struct buffer_head *last_eb_bh = NULL;
        struct ocfs2_extent_block *eb;
        struct ocfs2_extent_list *rightmost_el, *el;
@@ -4513,9 +4918,8 @@ static int ocfs2_split_tree(struct inode *inode, struct buffer_head *di_bh,
 
        depth = path->p_tree_depth;
        if (depth > 0) {
-               ret = ocfs2_read_block(OCFS2_SB(inode->i_sb),
-                                      le64_to_cpu(di->i_last_eb_blk),
-                                      &last_eb_bh, OCFS2_BH_CACHED, inode);
+               ret = ocfs2_read_block(inode, ocfs2_et_get_last_eb_blk(et),
+                                      &last_eb_bh);
                if (ret < 0) {
                        mlog_errno(ret);
                        goto out;
@@ -4526,7 +4930,8 @@ static int ocfs2_split_tree(struct inode *inode, struct buffer_head *di_bh,
        } else
                rightmost_el = path_leaf_el(path);
 
-       credits += path->p_tree_depth + ocfs2_extend_meta_needed(di);
+       credits += path->p_tree_depth +
+                  ocfs2_extend_meta_needed(et->et_root_el);
        ret = ocfs2_extend_trans(handle, credits);
        if (ret) {
                mlog_errno(ret);
@@ -4535,7 +4940,7 @@ static int ocfs2_split_tree(struct inode *inode, struct buffer_head *di_bh,
 
        if (le16_to_cpu(rightmost_el->l_next_free_rec) ==
            le16_to_cpu(rightmost_el->l_count)) {
-               ret = ocfs2_grow_tree(inode, handle, di_bh, &depth, &last_eb_bh,
+               ret = ocfs2_grow_tree(inode, handle, et, &depth, &last_eb_bh,
                                      meta_ac);
                if (ret) {
                        mlog_errno(ret);
@@ -4549,7 +4954,7 @@ static int ocfs2_split_tree(struct inode *inode, struct buffer_head *di_bh,
        insert.ins_split = SPLIT_RIGHT;
        insert.ins_tree_depth = depth;
 
-       ret = ocfs2_do_insert_extent(inode, handle, di_bh, &split_rec, &insert);
+       ret = ocfs2_do_insert_extent(inode, handle, et, &split_rec, &insert);
        if (ret)
                mlog_errno(ret);
 
@@ -4561,7 +4966,8 @@ out:
 static int ocfs2_truncate_rec(struct inode *inode, handle_t *handle,
                              struct ocfs2_path *path, int index,
                              struct ocfs2_cached_dealloc_ctxt *dealloc,
-                             u32 cpos, u32 len)
+                             u32 cpos, u32 len,
+                             struct ocfs2_extent_tree *et)
 {
        int ret;
        u32 left_cpos, rec_range, trunc_range;
@@ -4573,7 +4979,7 @@ static int ocfs2_truncate_rec(struct inode *inode, handle_t *handle,
        struct ocfs2_extent_block *eb;
 
        if (ocfs2_is_empty_extent(&el->l_recs[0]) && index > 0) {
-               ret = ocfs2_rotate_tree_left(inode, handle, path, dealloc);
+               ret = ocfs2_rotate_tree_left(inode, handle, path, dealloc, et);
                if (ret) {
                        mlog_errno(ret);
                        goto out;
@@ -4704,7 +5110,7 @@ static int ocfs2_truncate_rec(struct inode *inode, handle_t *handle,
 
        ocfs2_journal_dirty(handle, path_leaf_bh(path));
 
-       ret = ocfs2_rotate_tree_left(inode, handle, path, dealloc);
+       ret = ocfs2_rotate_tree_left(inode, handle, path, dealloc, et);
        if (ret) {
                mlog_errno(ret);
                goto out;
@@ -4715,7 +5121,8 @@ out:
        return ret;
 }
 
-int ocfs2_remove_extent(struct inode *inode, struct buffer_head *di_bh,
+int ocfs2_remove_extent(struct inode *inode,
+                       struct ocfs2_extent_tree *et,
                        u32 cpos, u32 len, handle_t *handle,
                        struct ocfs2_alloc_context *meta_ac,
                        struct ocfs2_cached_dealloc_ctxt *dealloc)
@@ -4724,11 +5131,11 @@ int ocfs2_remove_extent(struct inode *inode, struct buffer_head *di_bh,
        u32 rec_range, trunc_range;
        struct ocfs2_extent_rec *rec;
        struct ocfs2_extent_list *el;
-       struct ocfs2_path *path;
+       struct ocfs2_path *path = NULL;
 
        ocfs2_extent_map_trunc(inode, 0);
 
-       path = ocfs2_new_inode_path(di_bh);
+       path = ocfs2_new_path(et->et_root_bh, et->et_root_el);
        if (!path) {
                ret = -ENOMEM;
                mlog_errno(ret);
@@ -4781,13 +5188,13 @@ int ocfs2_remove_extent(struct inode *inode, struct buffer_head *di_bh,
 
        if (le32_to_cpu(rec->e_cpos) == cpos || rec_range == trunc_range) {
                ret = ocfs2_truncate_rec(inode, handle, path, index, dealloc,
-                                        cpos, len);
+                                        cpos, len, et);
                if (ret) {
                        mlog_errno(ret);
                        goto out;
                }
        } else {
-               ret = ocfs2_split_tree(inode, di_bh, handle, path, index,
+               ret = ocfs2_split_tree(inode, et, handle, path, index,
                                       trunc_range, meta_ac);
                if (ret) {
                        mlog_errno(ret);
@@ -4836,7 +5243,7 @@ int ocfs2_remove_extent(struct inode *inode, struct buffer_head *di_bh,
                }
 
                ret = ocfs2_truncate_rec(inode, handle, path, index, dealloc,
-                                        cpos, len);
+                                        cpos, len, et);
                if (ret) {
                        mlog_errno(ret);
                        goto out;
@@ -5179,8 +5586,7 @@ static int ocfs2_get_truncate_log_info(struct ocfs2_super *osb,
                goto bail;
        }
 
-       status = ocfs2_read_block(osb, OCFS2_I(inode)->ip_blkno, &bh,
-                                 OCFS2_BH_CACHED, inode);
+       status = ocfs2_read_block(inode, OCFS2_I(inode)->ip_blkno, &bh);
        if (status < 0) {
                iput(inode);
                mlog_errno(status);
@@ -5255,8 +5661,7 @@ int ocfs2_begin_truncate_log_recovery(struct ocfs2_super *osb,
 bail:
        if (tl_inode)
                iput(tl_inode);
-       if (tl_bh)
-               brelse(tl_bh);
+       brelse(tl_bh);
 
        if (status < 0 && (*tl_copy)) {
                kfree(*tl_copy);
@@ -5999,20 +6404,13 @@ bail:
        return status;
 }
 
-static int ocfs2_writeback_zero_func(handle_t *handle, struct buffer_head *bh)
+static int ocfs2_zero_func(handle_t *handle, struct buffer_head *bh)
 {
        set_buffer_uptodate(bh);
        mark_buffer_dirty(bh);
        return 0;
 }
 
-static int ocfs2_ordered_zero_func(handle_t *handle, struct buffer_head *bh)
-{
-       set_buffer_uptodate(bh);
-       mark_buffer_dirty(bh);
-       return ocfs2_journal_dirty_data(handle, bh);
-}
-
 static void ocfs2_map_and_dirty_page(struct inode *inode, handle_t *handle,
                                     unsigned int from, unsigned int to,
                                     struct page *page, int zero, u64 *phys)
@@ -6031,17 +6429,18 @@ static void ocfs2_map_and_dirty_page(struct inode *inode, handle_t *handle,
         * here if they aren't - ocfs2_map_page_blocks()
         * might've skipped some
         */
-       if (ocfs2_should_order_data(inode)) {
-               ret = walk_page_buffers(handle,
-                                       page_buffers(page),
-                                       from, to, &partial,
-                                       ocfs2_ordered_zero_func);
-               if (ret < 0)
-                       mlog_errno(ret);
-       } else {
+       ret = walk_page_buffers(handle, page_buffers(page),
+                               from, to, &partial,
+                               ocfs2_zero_func);
+       if (ret < 0)
+               mlog_errno(ret);
+       else if (ocfs2_should_order_data(inode)) {
+               ret = ocfs2_jbd2_file_inode(handle, inode);
+#ifdef CONFIG_OCFS2_COMPAT_JBD
                ret = walk_page_buffers(handle, page_buffers(page),
                                        from, to, &partial,
-                                       ocfs2_writeback_zero_func);
+                                       ocfs2_journal_dirty_data);
+#endif
                if (ret < 0)
                        mlog_errno(ret);
        }
@@ -6206,20 +6605,29 @@ out:
        return ret;
 }
 
-static void ocfs2_zero_dinode_id2(struct inode *inode, struct ocfs2_dinode *di)
+static void ocfs2_zero_dinode_id2_with_xattr(struct inode *inode,
+                                            struct ocfs2_dinode *di)
 {
        unsigned int blocksize = 1 << inode->i_sb->s_blocksize_bits;
+       unsigned int xattrsize = le16_to_cpu(di->i_xattr_inline_size);
 
-       memset(&di->id2, 0, blocksize - offsetof(struct ocfs2_dinode, id2));
+       if (le16_to_cpu(di->i_dyn_features) & OCFS2_INLINE_XATTR_FL)
+               memset(&di->id2, 0, blocksize -
+                                   offsetof(struct ocfs2_dinode, id2) -
+                                   xattrsize);
+       else
+               memset(&di->id2, 0, blocksize -
+                                   offsetof(struct ocfs2_dinode, id2));
 }
 
 void ocfs2_dinode_new_extent_list(struct inode *inode,
                                  struct ocfs2_dinode *di)
 {
-       ocfs2_zero_dinode_id2(inode, di);
+       ocfs2_zero_dinode_id2_with_xattr(inode, di);
        di->id2.i_list.l_tree_depth = 0;
        di->id2.i_list.l_next_free_rec = 0;
-       di->id2.i_list.l_count = cpu_to_le16(ocfs2_extent_recs_per_inode(inode->i_sb));
+       di->id2.i_list.l_count = cpu_to_le16(
+               ocfs2_extent_recs_per_inode_with_xattr(inode->i_sb, di));
 }
 
 void ocfs2_set_inode_data_inline(struct inode *inode, struct ocfs2_dinode *di)
@@ -6236,9 +6644,10 @@ void ocfs2_set_inode_data_inline(struct inode *inode, struct ocfs2_dinode *di)
         * We clear the entire i_data structure here so that all
         * fields can be properly initialized.
         */
-       ocfs2_zero_dinode_id2(inode, di);
+       ocfs2_zero_dinode_id2_with_xattr(inode, di);
 
-       idata->id_count = cpu_to_le16(ocfs2_max_inline_data(inode->i_sb));
+       idata->id_count = cpu_to_le16(
+                       ocfs2_max_inline_data_with_xattr(inode->i_sb, di));
 }
 
 int ocfs2_convert_inline_data_to_extents(struct inode *inode,
@@ -6253,6 +6662,7 @@ int ocfs2_convert_inline_data_to_extents(struct inode *inode,
        struct ocfs2_alloc_context *data_ac = NULL;
        struct page **pages = NULL;
        loff_t end = osb->s_clustersize;
+       struct ocfs2_extent_tree et;
 
        has_data = i_size_read(inode) ? 1 : 0;
 
@@ -6352,7 +6762,8 @@ int ocfs2_convert_inline_data_to_extents(struct inode *inode,
                 * this proves to be false, we could always re-build
                 * the in-inode data from our pages.
                 */
-               ret = ocfs2_insert_extent(osb, handle, inode, di_bh,
+               ocfs2_init_dinode_extent_tree(&et, inode, di_bh);
+               ret = ocfs2_insert_extent(osb, handle, inode, &et,
                                          0, block, 1, 0, NULL);
                if (ret) {
                        mlog_errno(ret);
@@ -6395,13 +6806,14 @@ int ocfs2_commit_truncate(struct ocfs2_super *osb,
        handle_t *handle = NULL;
        struct inode *tl_inode = osb->osb_tl_inode;
        struct ocfs2_path *path = NULL;
+       struct ocfs2_dinode *di = (struct ocfs2_dinode *)fe_bh->b_data;
 
        mlog_entry_void();
 
        new_highest_cpos = ocfs2_clusters_for_bytes(osb->sb,
                                                     i_size_read(inode));
 
-       path = ocfs2_new_inode_path(fe_bh);
+       path = ocfs2_new_path(fe_bh, &di->id2.i_list);
        if (!path) {
                status = -ENOMEM;
                mlog_errno(status);
@@ -6572,8 +6984,8 @@ int ocfs2_prepare_truncate(struct ocfs2_super *osb,
        ocfs2_init_dealloc_ctxt(&(*tc)->tc_dealloc);
 
        if (fe->id2.i_list.l_tree_depth) {
-               status = ocfs2_read_block(osb, le64_to_cpu(fe->i_last_eb_blk),
-                                         &last_eb_bh, OCFS2_BH_CACHED, inode);
+               status = ocfs2_read_block(inode, le64_to_cpu(fe->i_last_eb_blk),
+                                         &last_eb_bh);
                if (status < 0) {
                        mlog_errno(status);
                        goto bail;
@@ -6686,8 +7098,7 @@ static void ocfs2_free_truncate_context(struct ocfs2_truncate_context *tc)
                mlog(ML_NOTICE,
                     "Truncate completion has non-empty dealloc context\n");
 
-       if (tc->tc_last_eb_bh)
-               brelse(tc->tc_last_eb_bh);
+       brelse(tc->tc_last_eb_bh);
 
        kfree(tc);
 }
index 60cd3d59230c85398babfa7225fe087f2992a739..70257c84cfbe46c092bb78d6f6f2ae00b38505b3 100644 (file)
 #ifndef OCFS2_ALLOC_H
 #define OCFS2_ALLOC_H
 
+
+/*
+ * For xattr tree leaf, we limit the leaf byte size to be 64K.
+ */
+#define OCFS2_MAX_XATTR_TREE_LEAF_SIZE 65536
+
+/*
+ * ocfs2_extent_tree and ocfs2_extent_tree_operations are used to abstract
+ * the b-tree operations in ocfs2. Now all the b-tree operations are not
+ * limited to ocfs2_dinode only. Any data which need to allocate clusters
+ * to store can use b-tree. And it only needs to implement its ocfs2_extent_tree
+ * and operation.
+ *
+ * ocfs2_extent_tree becomes the first-class object for extent tree
+ * manipulation.  Callers of the alloc.c code need to fill it via one of
+ * the ocfs2_init_*_extent_tree() operations below.
+ *
+ * ocfs2_extent_tree contains info for the root of the b-tree, it must have a
+ * root ocfs2_extent_list and a root_bh so that they can be used in the b-tree
+ * functions.
+ * ocfs2_extent_tree_operations abstract the normal operations we do for
+ * the root of extent b-tree.
+ */
+struct ocfs2_extent_tree_operations;
+struct ocfs2_extent_tree {
+       struct ocfs2_extent_tree_operations     *et_ops;
+       struct buffer_head                      *et_root_bh;
+       struct ocfs2_extent_list                *et_root_el;
+       void                                    *et_object;
+       unsigned int                            et_max_leaf_clusters;
+};
+
+/*
+ * ocfs2_init_*_extent_tree() will fill an ocfs2_extent_tree from the
+ * specified object buffer.
+ */
+void ocfs2_init_dinode_extent_tree(struct ocfs2_extent_tree *et,
+                                  struct inode *inode,
+                                  struct buffer_head *bh);
+void ocfs2_init_xattr_tree_extent_tree(struct ocfs2_extent_tree *et,
+                                      struct inode *inode,
+                                      struct buffer_head *bh);
+void ocfs2_init_xattr_value_extent_tree(struct ocfs2_extent_tree *et,
+                                       struct inode *inode,
+                                       struct buffer_head *bh,
+                                       struct ocfs2_xattr_value_root *xv);
+
 struct ocfs2_alloc_context;
 int ocfs2_insert_extent(struct ocfs2_super *osb,
                        handle_t *handle,
                        struct inode *inode,
-                       struct buffer_head *fe_bh,
+                       struct ocfs2_extent_tree *et,
                        u32 cpos,
                        u64 start_blk,
                        u32 new_clusters,
                        u8 flags,
                        struct ocfs2_alloc_context *meta_ac);
+
+enum ocfs2_alloc_restarted {
+       RESTART_NONE = 0,
+       RESTART_TRANS,
+       RESTART_META
+};
+int ocfs2_add_clusters_in_btree(struct ocfs2_super *osb,
+                               struct inode *inode,
+                               u32 *logical_offset,
+                               u32 clusters_to_add,
+                               int mark_unwritten,
+                               struct ocfs2_extent_tree *et,
+                               handle_t *handle,
+                               struct ocfs2_alloc_context *data_ac,
+                               struct ocfs2_alloc_context *meta_ac,
+                               enum ocfs2_alloc_restarted *reason_ret);
 struct ocfs2_cached_dealloc_ctxt;
-int ocfs2_mark_extent_written(struct inode *inode, struct buffer_head *di_bh,
+int ocfs2_mark_extent_written(struct inode *inode,
+                             struct ocfs2_extent_tree *et,
                              handle_t *handle, u32 cpos, u32 len, u32 phys,
                              struct ocfs2_alloc_context *meta_ac,
                              struct ocfs2_cached_dealloc_ctxt *dealloc);
-int ocfs2_remove_extent(struct inode *inode, struct buffer_head *di_bh,
+int ocfs2_remove_extent(struct inode *inode,
+                       struct ocfs2_extent_tree *et,
                        u32 cpos, u32 len, handle_t *handle,
                        struct ocfs2_alloc_context *meta_ac,
                        struct ocfs2_cached_dealloc_ctxt *dealloc);
 int ocfs2_num_free_extents(struct ocfs2_super *osb,
                           struct inode *inode,
-                          struct ocfs2_dinode *fe);
-/* how many new metadata chunks would an allocation need at maximum? */
-static inline int ocfs2_extend_meta_needed(struct ocfs2_dinode *fe)
+                          struct ocfs2_extent_tree *et);
+
+/*
+ * how many new metadata chunks would an allocation need at maximum?
+ *
+ * Please note that the caller must make sure that root_el is the root
+ * of extent tree. So for an inode, it should be &fe->id2.i_list. Otherwise
+ * the result may be wrong.
+ */
+static inline int ocfs2_extend_meta_needed(struct ocfs2_extent_list *root_el)
 {
        /*
         * Rather than do all the work of determining how much we need
@@ -59,7 +131,7 @@ static inline int ocfs2_extend_meta_needed(struct ocfs2_dinode *fe)
         * new tree_depth==0 extent_block, and one block at the new
         * top-of-the tree.
         */
-       return le16_to_cpu(fe->id2.i_list.l_tree_depth) + 2;
+       return le16_to_cpu(root_el->l_tree_depth) + 2;
 }
 
 void ocfs2_dinode_new_extent_list(struct inode *inode, struct ocfs2_dinode *di);
index a53da1466277abd843b0117f89df9850423ba92b..c22543b3342062f0308ec06a6f74f2334291727f 100644 (file)
@@ -68,9 +68,7 @@ static int ocfs2_symlink_get_block(struct inode *inode, sector_t iblock,
                goto bail;
        }
 
-       status = ocfs2_read_block(OCFS2_SB(inode->i_sb),
-                                 OCFS2_I(inode)->ip_blkno,
-                                 &bh, OCFS2_BH_CACHED, inode);
+       status = ocfs2_read_block(inode, OCFS2_I(inode)->ip_blkno, &bh);
        if (status < 0) {
                mlog_errno(status);
                goto bail;
@@ -128,8 +126,7 @@ static int ocfs2_symlink_get_block(struct inode *inode, sector_t iblock,
        err = 0;
 
 bail:
-       if (bh)
-               brelse(bh);
+       brelse(bh);
 
        mlog_exit(err);
        return err;
@@ -261,13 +258,11 @@ static int ocfs2_readpage_inline(struct inode *inode, struct page *page)
 {
        int ret;
        struct buffer_head *di_bh = NULL;
-       struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
 
        BUG_ON(!PageLocked(page));
        BUG_ON(!(OCFS2_I(inode)->ip_dyn_features & OCFS2_INLINE_DATA_FL));
 
-       ret = ocfs2_read_block(osb, OCFS2_I(inode)->ip_blkno, &di_bh,
-                              OCFS2_BH_CACHED, inode);
+       ret = ocfs2_read_block(inode, OCFS2_I(inode)->ip_blkno, &di_bh);
        if (ret) {
                mlog_errno(ret);
                goto out;
@@ -485,11 +480,14 @@ handle_t *ocfs2_start_walk_page_trans(struct inode *inode,
        }
 
        if (ocfs2_should_order_data(inode)) {
+               ret = ocfs2_jbd2_file_inode(handle, inode);
+#ifdef CONFIG_OCFS2_COMPAT_JBD
                ret = walk_page_buffers(handle,
                                        page_buffers(page),
                                        from, to, NULL,
                                        ocfs2_journal_dirty_data);
-               if (ret < 0) 
+#endif
+               if (ret < 0)
                        mlog_errno(ret);
        }
 out:
@@ -669,7 +667,7 @@ static void ocfs2_invalidatepage(struct page *page, unsigned long offset)
 {
        journal_t *journal = OCFS2_SB(page->mapping->host->i_sb)->journal->j_journal;
 
-       journal_invalidatepage(journal, page, offset);
+       jbd2_journal_invalidatepage(journal, page, offset);
 }
 
 static int ocfs2_releasepage(struct page *page, gfp_t wait)
@@ -678,7 +676,7 @@ static int ocfs2_releasepage(struct page *page, gfp_t wait)
 
        if (!page_has_buffers(page))
                return 0;
-       return journal_try_to_free_buffers(journal, page, wait);
+       return jbd2_journal_try_to_free_buffers(journal, page, wait);
 }
 
 static ssize_t ocfs2_direct_IO(int rw,
@@ -1074,11 +1072,15 @@ static void ocfs2_write_failure(struct inode *inode,
                tmppage = wc->w_pages[i];
 
                if (page_has_buffers(tmppage)) {
-                       if (ocfs2_should_order_data(inode))
+                       if (ocfs2_should_order_data(inode)) {
+                               ocfs2_jbd2_file_inode(wc->w_handle, inode);
+#ifdef CONFIG_OCFS2_COMPAT_JBD
                                walk_page_buffers(wc->w_handle,
                                                  page_buffers(tmppage),
                                                  from, to, NULL,
                                                  ocfs2_journal_dirty_data);
+#endif
+                       }
 
                        block_commit_write(tmppage, from, to);
                }
@@ -1242,6 +1244,7 @@ static int ocfs2_write_cluster(struct address_space *mapping,
        int ret, i, new, should_zero = 0;
        u64 v_blkno, p_blkno;
        struct inode *inode = mapping->host;
+       struct ocfs2_extent_tree et;
 
        new = phys == 0 ? 1 : 0;
        if (new || unwritten)
@@ -1255,10 +1258,10 @@ static int ocfs2_write_cluster(struct address_space *mapping,
                 * any additional semaphores or cluster locks.
                 */
                tmp_pos = cpos;
-               ret = ocfs2_do_extend_allocation(OCFS2_SB(inode->i_sb), inode,
-                                                &tmp_pos, 1, 0, wc->w_di_bh,
-                                                wc->w_handle, data_ac,
-                                                meta_ac, NULL);
+               ret = ocfs2_add_inode_data(OCFS2_SB(inode->i_sb), inode,
+                                          &tmp_pos, 1, 0, wc->w_di_bh,
+                                          wc->w_handle, data_ac,
+                                          meta_ac, NULL);
                /*
                 * This shouldn't happen because we must have already
                 * calculated the correct meta data allocation required. The
@@ -1276,7 +1279,8 @@ static int ocfs2_write_cluster(struct address_space *mapping,
                        goto out;
                }
        } else if (unwritten) {
-               ret = ocfs2_mark_extent_written(inode, wc->w_di_bh,
+               ocfs2_init_dinode_extent_tree(&et, inode, wc->w_di_bh);
+               ret = ocfs2_mark_extent_written(inode, &et,
                                                wc->w_handle, cpos, 1, phys,
                                                meta_ac, &wc->w_dealloc);
                if (ret < 0) {
@@ -1665,6 +1669,7 @@ int ocfs2_write_begin_nolock(struct address_space *mapping,
        struct ocfs2_alloc_context *data_ac = NULL;
        struct ocfs2_alloc_context *meta_ac = NULL;
        handle_t *handle;
+       struct ocfs2_extent_tree et;
 
        ret = ocfs2_alloc_write_ctxt(&wc, osb, pos, len, di_bh);
        if (ret) {
@@ -1712,14 +1717,23 @@ int ocfs2_write_begin_nolock(struct address_space *mapping,
                 * ocfs2_lock_allocators(). It greatly over-estimates
                 * the work to be done.
                 */
-               ret = ocfs2_lock_allocators(inode, di, clusters_to_alloc,
-                                           extents_to_split, &data_ac, &meta_ac);
+               mlog(0, "extend inode %llu, i_size = %lld, di->i_clusters = %u,"
+                    " clusters_to_add = %u, extents_to_split = %u\n",
+                    (unsigned long long)OCFS2_I(inode)->ip_blkno,
+                    (long long)i_size_read(inode), le32_to_cpu(di->i_clusters),
+                    clusters_to_alloc, extents_to_split);
+
+               ocfs2_init_dinode_extent_tree(&et, inode, wc->w_di_bh);
+               ret = ocfs2_lock_allocators(inode, &et,
+                                           clusters_to_alloc, extents_to_split,
+                                           &data_ac, &meta_ac);
                if (ret) {
                        mlog_errno(ret);
                        goto out;
                }
 
-               credits = ocfs2_calc_extend_credits(inode->i_sb, di,
+               credits = ocfs2_calc_extend_credits(inode->i_sb,
+                                                   &di->id2.i_list,
                                                    clusters_to_alloc);
 
        }
@@ -1905,11 +1919,15 @@ int ocfs2_write_end_nolock(struct address_space *mapping,
                }
 
                if (page_has_buffers(tmppage)) {
-                       if (ocfs2_should_order_data(inode))
+                       if (ocfs2_should_order_data(inode)) {
+                               ocfs2_jbd2_file_inode(wc->w_handle, inode);
+#ifdef CONFIG_OCFS2_COMPAT_JBD
                                walk_page_buffers(wc->w_handle,
                                                  page_buffers(tmppage),
                                                  from, to, NULL,
                                                  ocfs2_journal_dirty_data);
+#endif
+                       }
                        block_commit_write(tmppage, from, to);
                }
        }
index f136639f5b41d023ea0f71d9c2bd1b7084c39322..7e947c6724694c74f7a1658392f83e15970fb7cd 100644 (file)
@@ -66,7 +66,7 @@ int ocfs2_write_block(struct ocfs2_super *osb, struct buffer_head *bh,
        /* remove from dirty list before I/O. */
        clear_buffer_dirty(bh);
 
-       get_bh(bh); /* for end_buffer_write_sync() */                   
+       get_bh(bh); /* for end_buffer_write_sync() */
        bh->b_end_io = end_buffer_write_sync;
        submit_bh(WRITE, bh);
 
@@ -88,22 +88,103 @@ out:
        return ret;
 }
 
-int ocfs2_read_blocks(struct ocfs2_super *osb, u64 block, int nr,
-                     struct buffer_head *bhs[], int flags,
-                     struct inode *inode)
+int ocfs2_read_blocks_sync(struct ocfs2_super *osb, u64 block,
+                          unsigned int nr, struct buffer_head *bhs[])
+{
+       int status = 0;
+       unsigned int i;
+       struct buffer_head *bh;
+
+       if (!nr) {
+               mlog(ML_BH_IO, "No buffers will be read!\n");
+               goto bail;
+       }
+
+       for (i = 0 ; i < nr ; i++) {
+               if (bhs[i] == NULL) {
+                       bhs[i] = sb_getblk(osb->sb, block++);
+                       if (bhs[i] == NULL) {
+                               status = -EIO;
+                               mlog_errno(status);
+                               goto bail;
+                       }
+               }
+               bh = bhs[i];
+
+               if (buffer_jbd(bh)) {
+                       mlog(ML_ERROR,
+                            "trying to sync read a jbd "
+                            "managed bh (blocknr = %llu), skipping\n",
+                            (unsigned long long)bh->b_blocknr);
+                       continue;
+               }
+
+               if (buffer_dirty(bh)) {
+                       /* This should probably be a BUG, or
+                        * at least return an error. */
+                       mlog(ML_ERROR,
+                            "trying to sync read a dirty "
+                            "buffer! (blocknr = %llu), skipping\n",
+                            (unsigned long long)bh->b_blocknr);
+                       continue;
+               }
+
+               lock_buffer(bh);
+               if (buffer_jbd(bh)) {
+                       mlog(ML_ERROR,
+                            "block %llu had the JBD bit set "
+                            "while I was in lock_buffer!",
+                            (unsigned long long)bh->b_blocknr);
+                       BUG();
+               }
+
+               clear_buffer_uptodate(bh);
+               get_bh(bh); /* for end_buffer_read_sync() */
+               bh->b_end_io = end_buffer_read_sync;
+               submit_bh(READ, bh);
+       }
+
+       for (i = nr; i > 0; i--) {
+               bh = bhs[i - 1];
+
+               if (buffer_jbd(bh)) {
+                       mlog(ML_ERROR,
+                            "the journal got the buffer while it was "
+                            "locked for io! (blocknr = %llu)\n",
+                            (unsigned long long)bh->b_blocknr);
+                       BUG();
+               }
+
+               wait_on_buffer(bh);
+               if (!buffer_uptodate(bh)) {
+                       /* Status won't be cleared from here on out,
+                        * so we can safely record this and loop back
+                        * to cleanup the other buffers. */
+                       status = -EIO;
+                       put_bh(bh);
+                       bhs[i - 1] = NULL;
+               }
+       }
+
+bail:
+       return status;
+}
+
+int ocfs2_read_blocks(struct inode *inode, u64 block, int nr,
+                     struct buffer_head *bhs[], int flags)
 {
        int status = 0;
-       struct super_block *sb;
        int i, ignore_cache = 0;
        struct buffer_head *bh;
 
-       mlog_entry("(block=(%llu), nr=(%d), flags=%d, inode=%p)\n",
-                  (unsigned long long)block, nr, flags, inode);
+       mlog_entry("(inode=%p, block=(%llu), nr=(%d), flags=%d)\n",
+                  inode, (unsigned long long)block, nr, flags);
 
+       BUG_ON(!inode);
        BUG_ON((flags & OCFS2_BH_READAHEAD) &&
-              (!inode || !(flags & OCFS2_BH_CACHED)));
+              (flags & OCFS2_BH_IGNORE_CACHE));
 
-       if (osb == NULL || osb->sb == NULL || bhs == NULL) {
+       if (bhs == NULL) {
                status = -EINVAL;
                mlog_errno(status);
                goto bail;
@@ -122,26 +203,19 @@ int ocfs2_read_blocks(struct ocfs2_super *osb, u64 block, int nr,
                goto bail;
        }
 
-       sb = osb->sb;
-
-       if (flags & OCFS2_BH_CACHED && !inode)
-               flags &= ~OCFS2_BH_CACHED;
-
-       if (inode)
-               mutex_lock(&OCFS2_I(inode)->ip_io_mutex);
+       mutex_lock(&OCFS2_I(inode)->ip_io_mutex);
        for (i = 0 ; i < nr ; i++) {
                if (bhs[i] == NULL) {
-                       bhs[i] = sb_getblk(sb, block++);
+                       bhs[i] = sb_getblk(inode->i_sb, block++);
                        if (bhs[i] == NULL) {
-                               if (inode)
-                                       mutex_unlock(&OCFS2_I(inode)->ip_io_mutex);
+                               mutex_unlock(&OCFS2_I(inode)->ip_io_mutex);
                                status = -EIO;
                                mlog_errno(status);
                                goto bail;
                        }
                }
                bh = bhs[i];
-               ignore_cache = 0;
+               ignore_cache = (flags & OCFS2_BH_IGNORE_CACHE);
 
                /* There are three read-ahead cases here which we need to
                 * be concerned with. All three assume a buffer has
@@ -167,26 +241,27 @@ int ocfs2_read_blocks(struct ocfs2_super *osb, u64 block, int nr,
                 *    before our is-it-in-flight check.
                 */
 
-               if (flags & OCFS2_BH_CACHED &&
-                   !ocfs2_buffer_uptodate(inode, bh)) {
+               if (!ignore_cache && !ocfs2_buffer_uptodate(inode, bh)) {
                        mlog(ML_UPTODATE,
                             "bh (%llu), inode %llu not uptodate\n",
                             (unsigned long long)bh->b_blocknr,
                             (unsigned long long)OCFS2_I(inode)->ip_blkno);
+                       /* We're using ignore_cache here to say
+                        * "go to disk" */
                        ignore_cache = 1;
                }
 
                /* XXX: Can we ever get this and *not* have the cached
                 * flag set? */
                if (buffer_jbd(bh)) {
-                       if (!(flags & OCFS2_BH_CACHED) || ignore_cache)
+                       if (ignore_cache)
                                mlog(ML_BH_IO, "trying to sync read a jbd "
                                               "managed bh (blocknr = %llu)\n",
                                     (unsigned long long)bh->b_blocknr);
                        continue;
                }
 
-               if (!(flags & OCFS2_BH_CACHED) || ignore_cache) {
+               if (ignore_cache) {
                        if (buffer_dirty(bh)) {
                                /* This should probably be a BUG, or
                                 * at least return an error. */
@@ -221,7 +296,7 @@ int ocfs2_read_blocks(struct ocfs2_super *osb, u64 block, int nr,
                         * previously read-ahead buffer may have
                         * completed I/O while we were waiting for the
                         * buffer lock. */
-                       if ((flags & OCFS2_BH_CACHED)
+                       if (!(flags & OCFS2_BH_IGNORE_CACHE)
                            && !(flags & OCFS2_BH_READAHEAD)
                            && ocfs2_buffer_uptodate(inode, bh)) {
                                unlock_buffer(bh);
@@ -265,15 +340,14 @@ int ocfs2_read_blocks(struct ocfs2_super *osb, u64 block, int nr,
                /* Always set the buffer in the cache, even if it was
                 * a forced read, or read-ahead which hasn't yet
                 * completed. */
-               if (inode)
-                       ocfs2_set_buffer_uptodate(inode, bh);
+               ocfs2_set_buffer_uptodate(inode, bh);
        }
-       if (inode)
-               mutex_unlock(&OCFS2_I(inode)->ip_io_mutex);
+       mutex_unlock(&OCFS2_I(inode)->ip_io_mutex);
 
        mlog(ML_BH_IO, "block=(%llu), nr=(%d), cached=%s, flags=0x%x\n", 
             (unsigned long long)block, nr,
-            (!(flags & OCFS2_BH_CACHED) || ignore_cache) ? "no" : "yes", flags);
+            ((flags & OCFS2_BH_IGNORE_CACHE) || ignore_cache) ? "no" : "yes",
+            flags);
 
 bail:
 
index c2e78614c3e50970ed01521d5e38e2371adeb388..75e1dcb1ade7d19d92a70684f1d3daaf851c67ed 100644 (file)
 void ocfs2_end_buffer_io_sync(struct buffer_head *bh,
                             int uptodate);
 
-static inline int ocfs2_read_block(struct ocfs2_super          *osb,
+static inline int ocfs2_read_block(struct inode               *inode,
                                   u64                  off,
-                                  struct buffer_head **bh,
-                                  int                  flags,
-                                  struct inode        *inode);
+                                  struct buffer_head **bh);
 
 int ocfs2_write_block(struct ocfs2_super          *osb,
                      struct buffer_head  *bh,
                      struct inode        *inode);
-int ocfs2_read_blocks(struct ocfs2_super          *osb,
+int ocfs2_read_blocks(struct inode       *inode,
                      u64                  block,
                      int                  nr,
                      struct buffer_head  *bhs[],
-                     int                  flags,
-                     struct inode        *inode);
+                     int                  flags);
+int ocfs2_read_blocks_sync(struct ocfs2_super *osb, u64 block,
+                          unsigned int nr, struct buffer_head *bhs[]);
 
 int ocfs2_write_super_or_backup(struct ocfs2_super *osb,
                                struct buffer_head *bh);
 
-#define OCFS2_BH_CACHED            1
+#define OCFS2_BH_IGNORE_CACHE      1
 #define OCFS2_BH_READAHEAD         8
 
-static inline int ocfs2_read_block(struct ocfs2_super * osb, u64 off,
-                                  struct buffer_head **bh, int flags,
-                                  struct inode *inode)
+static inline int ocfs2_read_block(struct inode *inode, u64 off,
+                                  struct buffer_head **bh)
 {
        int status = 0;
 
@@ -65,8 +63,7 @@ static inline int ocfs2_read_block(struct ocfs2_super * osb, u64 off,
                goto bail;
        }
 
-       status = ocfs2_read_blocks(osb, off, 1, bh,
-                                  flags, inode);
+       status = ocfs2_read_blocks(inode, off, 1, bh, 0);
 
 bail:
        return status;
index 23c732f275293bfdd24dad2d03ddbb07c7c4b8e8..d8a0cb92cef62a6b108696b0a9dae36dcfc9768a 100644 (file)
@@ -109,6 +109,7 @@ static struct mlog_attribute mlog_attrs[MLOG_MAX_BITS] = {
        define_mask(CONN),
        define_mask(QUORUM),
        define_mask(EXPORT),
+       define_mask(XATTR),
        define_mask(ERROR),
        define_mask(NOTICE),
        define_mask(KTHREAD),
index 597e064bb94fa9333ea40ad0387d7b34fa2005dc..57670c6804713454f02f2a6b18ed5ca41bbbfe6b 100644 (file)
 #define ML_CONN                0x0000000004000000ULL /* net connection management */
 #define ML_QUORUM      0x0000000008000000ULL /* net connection quorum */
 #define ML_EXPORT      0x0000000010000000ULL /* ocfs2 export operations */
+#define ML_XATTR       0x0000000020000000ULL /* ocfs2 extended attributes */
 /* bits that are infrequently given and frequently matched in the high word */
 #define ML_ERROR       0x0000000100000000ULL /* sent to KERN_ERR */
 #define ML_NOTICE      0x0000000200000000ULL /* setn to KERN_NOTICE */
index 9cce563fd6278dde9a4da3db12eabb04e328526e..026e6eb85187844b60ba55eb9893651090ac1c4f 100644 (file)
@@ -82,6 +82,49 @@ static int ocfs2_do_extend_dir(struct super_block *sb,
                               struct ocfs2_alloc_context *meta_ac,
                               struct buffer_head **new_bh);
 
+static struct buffer_head *ocfs2_bread(struct inode *inode,
+                                      int block, int *err, int reada)
+{
+       struct buffer_head *bh = NULL;
+       int tmperr;
+       u64 p_blkno;
+       int readflags = 0;
+
+       if (reada)
+               readflags |= OCFS2_BH_READAHEAD;
+
+       if (((u64)block << inode->i_sb->s_blocksize_bits) >=
+           i_size_read(inode)) {
+               BUG_ON(!reada);
+               return NULL;
+       }
+
+       down_read(&OCFS2_I(inode)->ip_alloc_sem);
+       tmperr = ocfs2_extent_map_get_blocks(inode, block, &p_blkno, NULL,
+                                            NULL);
+       up_read(&OCFS2_I(inode)->ip_alloc_sem);
+       if (tmperr < 0) {
+               mlog_errno(tmperr);
+               goto fail;
+       }
+
+       tmperr = ocfs2_read_blocks(inode, p_blkno, 1, &bh, readflags);
+       if (tmperr < 0)
+               goto fail;
+
+       tmperr = 0;
+
+       *err = 0;
+       return bh;
+
+fail:
+       brelse(bh);
+       bh = NULL;
+
+       *err = -EIO;
+       return NULL;
+}
+
 /*
  * bh passed here can be an inode block or a dir data block, depending
  * on the inode inline data flag.
@@ -188,8 +231,7 @@ static struct buffer_head *ocfs2_find_entry_id(const char *name,
        struct ocfs2_dinode *di;
        struct ocfs2_inline_data *data;
 
-       ret = ocfs2_read_block(OCFS2_SB(dir->i_sb), OCFS2_I(dir)->ip_blkno,
-                              &di_bh, OCFS2_BH_CACHED, dir);
+       ret = ocfs2_read_block(dir, OCFS2_I(dir)->ip_blkno, &di_bh);
        if (ret) {
                mlog_errno(ret);
                goto out;
@@ -260,14 +302,13 @@ restart:
                }
                if ((bh = bh_use[ra_ptr++]) == NULL)
                        goto next;
-               wait_on_buffer(bh);
-               if (!buffer_uptodate(bh)) {
-                       /* read error, skip block & hope for the best */
+               if (ocfs2_read_block(dir, block, &bh)) {
+                       /* read error, skip block & hope for the best.
+                        * ocfs2_read_block() has released the bh. */
                        ocfs2_error(dir->i_sb, "reading directory %llu, "
                                    "offset %lu\n",
                                    (unsigned long long)OCFS2_I(dir)->ip_blkno,
                                    block);
-                       brelse(bh);
                        goto next;
                }
                i = ocfs2_search_dirblock(bh, dir, name, namelen,
@@ -417,8 +458,7 @@ static inline int ocfs2_delete_entry_id(handle_t *handle,
        struct ocfs2_dinode *di;
        struct ocfs2_inline_data *data;
 
-       ret = ocfs2_read_block(OCFS2_SB(dir->i_sb), OCFS2_I(dir)->ip_blkno,
-                              &di_bh, OCFS2_BH_CACHED, dir);
+       ret = ocfs2_read_block(dir, OCFS2_I(dir)->ip_blkno, &di_bh);
        if (ret) {
                mlog_errno(ret);
                goto out;
@@ -596,8 +636,7 @@ static int ocfs2_dir_foreach_blk_id(struct inode *inode,
        struct ocfs2_inline_data *data;
        struct ocfs2_dir_entry *de;
 
-       ret = ocfs2_read_block(OCFS2_SB(inode->i_sb), OCFS2_I(inode)->ip_blkno,
-                              &di_bh, OCFS2_BH_CACHED, inode);
+       ret = ocfs2_read_block(inode, OCFS2_I(inode)->ip_blkno, &di_bh);
        if (ret) {
                mlog(ML_ERROR, "Unable to read inode block for dir %llu\n",
                     (unsigned long long)OCFS2_I(inode)->ip_blkno);
@@ -716,8 +755,7 @@ static int ocfs2_dir_foreach_blk_el(struct inode *inode,
                        for (i = ra_sectors >> (sb->s_blocksize_bits - 9);
                             i > 0; i--) {
                                tmp = ocfs2_bread(inode, ++blk, &err, 1);
-                               if (tmp)
-                                       brelse(tmp);
+                               brelse(tmp);
                        }
                        last_ra_blk = blk;
                        ra_sectors = 8;
@@ -899,10 +937,8 @@ int ocfs2_find_files_on_disk(const char *name,
 leave:
        if (status < 0) {
                *dirent = NULL;
-               if (*dirent_bh) {
-                       brelse(*dirent_bh);
-                       *dirent_bh = NULL;
-               }
+               brelse(*dirent_bh);
+               *dirent_bh = NULL;
        }
 
        mlog_exit(status);
@@ -951,8 +987,7 @@ int ocfs2_check_dir_for_entry(struct inode *dir,
 
        ret = 0;
 bail:
-       if (dirent_bh)
-               brelse(dirent_bh);
+       brelse(dirent_bh);
 
        mlog_exit(ret);
        return ret;
@@ -1127,8 +1162,7 @@ static int ocfs2_fill_new_dir_el(struct ocfs2_super *osb,
 
        status = 0;
 bail:
-       if (new_bh)
-               brelse(new_bh);
+       brelse(new_bh);
 
        mlog_exit(status);
        return status;
@@ -1192,6 +1226,9 @@ static int ocfs2_expand_inline_dir(struct inode *dir, struct buffer_head *di_bh,
        struct buffer_head *dirdata_bh = NULL;
        struct ocfs2_dinode *di = (struct ocfs2_dinode *)di_bh->b_data;
        handle_t *handle;
+       struct ocfs2_extent_tree et;
+
+       ocfs2_init_dinode_extent_tree(&et, dir, di_bh);
 
        alloc = ocfs2_clusters_for_bytes(sb, bytes);
 
@@ -1305,8 +1342,8 @@ static int ocfs2_expand_inline_dir(struct inode *dir, struct buffer_head *di_bh,
         * This should never fail as our extent list is empty and all
         * related blocks have been journaled already.
         */
-       ret = ocfs2_insert_extent(osb, handle, dir, di_bh, 0, blkno, len, 0,
-                                 NULL);
+       ret = ocfs2_insert_extent(osb, handle, dir, &et, 0, blkno, len,
+                                 0, NULL);
        if (ret) {
                mlog_errno(ret);
                goto out_commit;
@@ -1337,8 +1374,8 @@ static int ocfs2_expand_inline_dir(struct inode *dir, struct buffer_head *di_bh,
                }
                blkno = ocfs2_clusters_to_blocks(dir->i_sb, bit_off);
 
-               ret = ocfs2_insert_extent(osb, handle, dir, di_bh, 1, blkno,
-                                         len, 0, NULL);
+               ret = ocfs2_insert_extent(osb, handle, dir, &et, 1,
+                                         blkno, len, 0, NULL);
                if (ret) {
                        mlog_errno(ret);
                        goto out_commit;
@@ -1383,9 +1420,9 @@ static int ocfs2_do_extend_dir(struct super_block *sb,
        if (extend) {
                u32 offset = OCFS2_I(dir)->ip_clusters;
 
-               status = ocfs2_do_extend_allocation(OCFS2_SB(sb), dir, &offset,
-                                                   1, 0, parent_fe_bh, handle,
-                                                   data_ac, meta_ac, NULL);
+               status = ocfs2_add_inode_data(OCFS2_SB(sb), dir, &offset,
+                                             1, 0, parent_fe_bh, handle,
+                                             data_ac, meta_ac, NULL);
                BUG_ON(status == -EAGAIN);
                if (status < 0) {
                        mlog_errno(status);
@@ -1430,12 +1467,14 @@ static int ocfs2_extend_dir(struct ocfs2_super *osb,
        int credits, num_free_extents, drop_alloc_sem = 0;
        loff_t dir_i_size;
        struct ocfs2_dinode *fe = (struct ocfs2_dinode *) parent_fe_bh->b_data;
+       struct ocfs2_extent_list *el = &fe->id2.i_list;
        struct ocfs2_alloc_context *data_ac = NULL;
        struct ocfs2_alloc_context *meta_ac = NULL;
        handle_t *handle = NULL;
        struct buffer_head *new_bh = NULL;
        struct ocfs2_dir_entry * de;
        struct super_block *sb = osb->sb;
+       struct ocfs2_extent_tree et;
 
        mlog_entry_void();
 
@@ -1479,7 +1518,8 @@ static int ocfs2_extend_dir(struct ocfs2_super *osb,
        spin_lock(&OCFS2_I(dir)->ip_lock);
        if (dir_i_size == ocfs2_clusters_to_bytes(sb, OCFS2_I(dir)->ip_clusters)) {
                spin_unlock(&OCFS2_I(dir)->ip_lock);
-               num_free_extents = ocfs2_num_free_extents(osb, dir, fe);
+               ocfs2_init_dinode_extent_tree(&et, dir, parent_fe_bh);
+               num_free_extents = ocfs2_num_free_extents(osb, dir, &et);
                if (num_free_extents < 0) {
                        status = num_free_extents;
                        mlog_errno(status);
@@ -1487,7 +1527,7 @@ static int ocfs2_extend_dir(struct ocfs2_super *osb,
                }
 
                if (!num_free_extents) {
-                       status = ocfs2_reserve_new_metadata(osb, fe, &meta_ac);
+                       status = ocfs2_reserve_new_metadata(osb, el, &meta_ac);
                        if (status < 0) {
                                if (status != -ENOSPC)
                                        mlog_errno(status);
@@ -1502,7 +1542,7 @@ static int ocfs2_extend_dir(struct ocfs2_super *osb,
                        goto bail;
                }
 
-               credits = ocfs2_calc_extend_credits(sb, fe, 1);
+               credits = ocfs2_calc_extend_credits(sb, el, 1);
        } else {
                spin_unlock(&OCFS2_I(dir)->ip_lock);
                credits = OCFS2_SIMPLE_DIR_EXTEND_CREDITS;
@@ -1568,8 +1608,7 @@ bail:
        if (meta_ac)
                ocfs2_free_alloc_context(meta_ac);
 
-       if (new_bh)
-               brelse(new_bh);
+       brelse(new_bh);
 
        mlog_exit(status);
        return status;
@@ -1696,8 +1735,7 @@ static int ocfs2_find_dir_space_el(struct inode *dir, const char *name,
 
        status = 0;
 bail:
-       if (bh)
-               brelse(bh);
+       brelse(bh);
 
        mlog_exit(status);
        return status;
@@ -1756,7 +1794,6 @@ int ocfs2_prepare_dir_for_insert(struct ocfs2_super *osb,
        *ret_de_bh = bh;
        bh = NULL;
 out:
-       if (bh)
-               brelse(bh);
+       brelse(bh);
        return ret;
 }
index eae3d643a5e4316332b23cabb6f3d7edae4b7a5e..ec684426034b6dc39ab2b772acc918d07cce6ccd 100644 (file)
@@ -2024,8 +2024,7 @@ static int ocfs2_inode_lock_update(struct inode *inode,
        } else {
                /* Boo, we have to go to disk. */
                /* read bh, cast, ocfs2_refresh_inode */
-               status = ocfs2_read_block(OCFS2_SB(inode->i_sb), oi->ip_blkno,
-                                         bh, OCFS2_BH_CACHED, inode);
+               status = ocfs2_read_block(inode, oi->ip_blkno, bh);
                if (status < 0) {
                        mlog_errno(status);
                        goto bail_refresh;
@@ -2086,11 +2085,7 @@ static int ocfs2_assign_bh(struct inode *inode,
                return 0;
        }
 
-       status = ocfs2_read_block(OCFS2_SB(inode->i_sb),
-                                 OCFS2_I(inode)->ip_blkno,
-                                 ret_bh,
-                                 OCFS2_BH_CACHED,
-                                 inode);
+       status = ocfs2_read_block(inode, OCFS2_I(inode)->ip_blkno, ret_bh);
        if (status < 0)
                mlog_errno(status);
 
index aed268e80b49173a5a3af264bcd5ca5c7e55ca8a..2baedac582341dbb60986840b29daded17521842 100644 (file)
@@ -293,8 +293,7 @@ static int ocfs2_last_eb_is_empty(struct inode *inode,
        struct ocfs2_extent_block *eb;
        struct ocfs2_extent_list *el;
 
-       ret = ocfs2_read_block(OCFS2_SB(inode->i_sb), last_eb_blk,
-                              &eb_bh, OCFS2_BH_CACHED, inode);
+       ret = ocfs2_read_block(inode, last_eb_blk, &eb_bh);
        if (ret) {
                mlog_errno(ret);
                goto out;
@@ -382,9 +381,9 @@ static int ocfs2_figure_hole_clusters(struct inode *inode,
                if (le64_to_cpu(eb->h_next_leaf_blk) == 0ULL)
                        goto no_more_extents;
 
-               ret = ocfs2_read_block(OCFS2_SB(inode->i_sb),
+               ret = ocfs2_read_block(inode,
                                       le64_to_cpu(eb->h_next_leaf_blk),
-                                      &next_eb_bh, OCFS2_BH_CACHED, inode);
+                                      &next_eb_bh);
                if (ret) {
                        mlog_errno(ret);
                        goto out;
@@ -551,6 +550,66 @@ static void ocfs2_relative_extent_offsets(struct super_block *sb,
                *num_clusters = le16_to_cpu(rec->e_leaf_clusters) - coff;
 }
 
+int ocfs2_xattr_get_clusters(struct inode *inode, u32 v_cluster,
+                            u32 *p_cluster, u32 *num_clusters,
+                            struct ocfs2_extent_list *el)
+{
+       int ret = 0, i;
+       struct buffer_head *eb_bh = NULL;
+       struct ocfs2_extent_block *eb;
+       struct ocfs2_extent_rec *rec;
+       u32 coff;
+
+       if (el->l_tree_depth) {
+               ret = ocfs2_find_leaf(inode, el, v_cluster, &eb_bh);
+               if (ret) {
+                       mlog_errno(ret);
+                       goto out;
+               }
+
+               eb = (struct ocfs2_extent_block *) eb_bh->b_data;
+               el = &eb->h_list;
+
+               if (el->l_tree_depth) {
+                       ocfs2_error(inode->i_sb,
+                                   "Inode %lu has non zero tree depth in "
+                                   "xattr leaf block %llu\n", inode->i_ino,
+                                   (unsigned long long)eb_bh->b_blocknr);
+                       ret = -EROFS;
+                       goto out;
+               }
+       }
+
+       i = ocfs2_search_extent_list(el, v_cluster);
+       if (i == -1) {
+               ret = -EROFS;
+               mlog_errno(ret);
+               goto out;
+       } else {
+               rec = &el->l_recs[i];
+               BUG_ON(v_cluster < le32_to_cpu(rec->e_cpos));
+
+               if (!rec->e_blkno) {
+                       ocfs2_error(inode->i_sb, "Inode %lu has bad extent "
+                                   "record (%u, %u, 0) in xattr", inode->i_ino,
+                                   le32_to_cpu(rec->e_cpos),
+                                   ocfs2_rec_clusters(el, rec));
+                       ret = -EROFS;
+                       goto out;
+               }
+               coff = v_cluster - le32_to_cpu(rec->e_cpos);
+               *p_cluster = ocfs2_blocks_to_clusters(inode->i_sb,
+                                                   le64_to_cpu(rec->e_blkno));
+               *p_cluster = *p_cluster + coff;
+               if (num_clusters)
+                       *num_clusters = ocfs2_rec_clusters(el, rec) - coff;
+       }
+out:
+       if (eb_bh)
+               brelse(eb_bh);
+       return ret;
+}
+
 int ocfs2_get_clusters(struct inode *inode, u32 v_cluster,
                       u32 *p_cluster, u32 *num_clusters,
                       unsigned int *extent_flags)
@@ -571,8 +630,7 @@ int ocfs2_get_clusters(struct inode *inode, u32 v_cluster,
        if (ret == 0)
                goto out;
 
-       ret = ocfs2_read_block(OCFS2_SB(inode->i_sb), OCFS2_I(inode)->ip_blkno,
-                              &di_bh, OCFS2_BH_CACHED, inode);
+       ret = ocfs2_read_block(inode, OCFS2_I(inode)->ip_blkno, &di_bh);
        if (ret) {
                mlog_errno(ret);
                goto out;
index 1b97490e1ea8d7b431cf40c9bbcab801b3bb4305..1c4aa8b06f348c89b4560f733cdd0e1aefe9e852 100644 (file)
@@ -53,4 +53,8 @@ int ocfs2_extent_map_get_blocks(struct inode *inode, u64 v_blkno, u64 *p_blkno,
 int ocfs2_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,
                 u64 map_start, u64 map_len);
 
+int ocfs2_xattr_get_clusters(struct inode *inode, u32 v_cluster,
+                            u32 *p_cluster, u32 *num_clusters,
+                            struct ocfs2_extent_list *el);
+
 #endif  /* _EXTENT_MAP_H */
index ed38796052d201f28da44d0ec60d7bf4baf078a8..8d3225a78073f86c682ed21e9b3098a1fef6b843 100644 (file)
@@ -55,6 +55,7 @@
 #include "mmap.h"
 #include "suballoc.h"
 #include "super.h"
+#include "xattr.h"
 
 #include "buffer_head_io.h"
 
@@ -184,7 +185,7 @@ static int ocfs2_sync_file(struct file *file,
                goto bail;
 
        journal = osb->journal->j_journal;
-       err = journal_force_commit(journal);
+       err = jbd2_journal_force_commit(journal);
 
 bail:
        mlog_exit(err);
@@ -488,7 +489,7 @@ bail:
 }
 
 /*
- * extend allocation only here.
+ * extend file allocation only here.
  * we'll update all the disk stuff, and oip->alloc_size
  *
  * expect stuff to be locked, a transaction started and enough data /
@@ -497,189 +498,25 @@ bail:
  * Will return -EAGAIN, and a reason if a restart is needed.
  * If passed in, *reason will always be set, even in error.
  */
-int ocfs2_do_extend_allocation(struct ocfs2_super *osb,
-                              struct inode *inode,
-                              u32 *logical_offset,
-                              u32 clusters_to_add,
-                              int mark_unwritten,
-                              struct buffer_head *fe_bh,
-                              handle_t *handle,
-                              struct ocfs2_alloc_context *data_ac,
-                              struct ocfs2_alloc_context *meta_ac,
-                              enum ocfs2_alloc_restarted *reason_ret)
+int ocfs2_add_inode_data(struct ocfs2_super *osb,
+                        struct inode *inode,
+                        u32 *logical_offset,
+                        u32 clusters_to_add,
+                        int mark_unwritten,
+                        struct buffer_head *fe_bh,
+                        handle_t *handle,
+                        struct ocfs2_alloc_context *data_ac,
+                        struct ocfs2_alloc_context *meta_ac,
+                        enum ocfs2_alloc_restarted *reason_ret)
 {
-       int status = 0;
-       int free_extents;
-       struct ocfs2_dinode *fe = (struct ocfs2_dinode *) fe_bh->b_data;
-       enum ocfs2_alloc_restarted reason = RESTART_NONE;
-       u32 bit_off, num_bits;
-       u64 block;
-       u8 flags = 0;
-
-       BUG_ON(!clusters_to_add);
-
-       if (mark_unwritten)
-               flags = OCFS2_EXT_UNWRITTEN;
-
-       free_extents = ocfs2_num_free_extents(osb, inode, fe);
-       if (free_extents < 0) {
-               status = free_extents;
-               mlog_errno(status);
-               goto leave;
-       }
-
-       /* there are two cases which could cause us to EAGAIN in the
-        * we-need-more-metadata case:
-        * 1) we haven't reserved *any*
-        * 2) we are so fragmented, we've needed to add metadata too
-        *    many times. */
-       if (!free_extents && !meta_ac) {
-               mlog(0, "we haven't reserved any metadata!\n");
-               status = -EAGAIN;
-               reason = RESTART_META;
-               goto leave;
-       } else if ((!free_extents)
-                  && (ocfs2_alloc_context_bits_left(meta_ac)
-                      < ocfs2_extend_meta_needed(fe))) {
-               mlog(0, "filesystem is really fragmented...\n");
-               status = -EAGAIN;
-               reason = RESTART_META;
-               goto leave;
-       }
-
-       status = __ocfs2_claim_clusters(osb, handle, data_ac, 1,
-                                       clusters_to_add, &bit_off, &num_bits);
-       if (status < 0) {
-               if (status != -ENOSPC)
-                       mlog_errno(status);
-               goto leave;
-       }
-
-       BUG_ON(num_bits > clusters_to_add);
-
-       /* reserve our write early -- insert_extent may update the inode */
-       status = ocfs2_journal_access(handle, inode, fe_bh,
-                                     OCFS2_JOURNAL_ACCESS_WRITE);
-       if (status < 0) {
-               mlog_errno(status);
-               goto leave;
-       }
-
-       block = ocfs2_clusters_to_blocks(osb->sb, bit_off);
-       mlog(0, "Allocating %u clusters at block %u for inode %llu\n",
-            num_bits, bit_off, (unsigned long long)OCFS2_I(inode)->ip_blkno);
-       status = ocfs2_insert_extent(osb, handle, inode, fe_bh,
-                                    *logical_offset, block, num_bits,
-                                    flags, meta_ac);
-       if (status < 0) {
-               mlog_errno(status);
-               goto leave;
-       }
-
-       status = ocfs2_journal_dirty(handle, fe_bh);
-       if (status < 0) {
-               mlog_errno(status);
-               goto leave;
-       }
-
-       clusters_to_add -= num_bits;
-       *logical_offset += num_bits;
-
-       if (clusters_to_add) {
-               mlog(0, "need to alloc once more, clusters = %u, wanted = "
-                    "%u\n", fe->i_clusters, clusters_to_add);
-               status = -EAGAIN;
-               reason = RESTART_TRANS;
-       }
-
-leave:
-       mlog_exit(status);
-       if (reason_ret)
-               *reason_ret = reason;
-       return status;
-}
-
-/*
- * For a given allocation, determine which allocators will need to be
- * accessed, and lock them, reserving the appropriate number of bits.
- *
- * Sparse file systems call this from ocfs2_write_begin_nolock()
- * and ocfs2_allocate_unwritten_extents().
- *
- * File systems which don't support holes call this from
- * ocfs2_extend_allocation().
- */
-int ocfs2_lock_allocators(struct inode *inode, struct ocfs2_dinode *di,
-                         u32 clusters_to_add, u32 extents_to_split,
-                         struct ocfs2_alloc_context **data_ac,
-                         struct ocfs2_alloc_context **meta_ac)
-{
-       int ret = 0, num_free_extents;
-       unsigned int max_recs_needed = clusters_to_add + 2 * extents_to_split;
-       struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
-
-       *meta_ac = NULL;
-       if (data_ac)
-               *data_ac = NULL;
-
-       BUG_ON(clusters_to_add != 0 && data_ac == NULL);
-
-       mlog(0, "extend inode %llu, i_size = %lld, di->i_clusters = %u, "
-            "clusters_to_add = %u, extents_to_split = %u\n",
-            (unsigned long long)OCFS2_I(inode)->ip_blkno, (long long)i_size_read(inode),
-            le32_to_cpu(di->i_clusters), clusters_to_add, extents_to_split);
-
-       num_free_extents = ocfs2_num_free_extents(osb, inode, di);
-       if (num_free_extents < 0) {
-               ret = num_free_extents;
-               mlog_errno(ret);
-               goto out;
-       }
-
-       /*
-        * Sparse allocation file systems need to be more conservative
-        * with reserving room for expansion - the actual allocation
-        * happens while we've got a journal handle open so re-taking
-        * a cluster lock (because we ran out of room for another
-        * extent) will violate ordering rules.
-        *
-        * Most of the time we'll only be seeing this 1 cluster at a time
-        * anyway.
-        *
-        * Always lock for any unwritten extents - we might want to
-        * add blocks during a split.
-        */
-       if (!num_free_extents ||
-           (ocfs2_sparse_alloc(osb) && num_free_extents < max_recs_needed)) {
-               ret = ocfs2_reserve_new_metadata(osb, di, meta_ac);
-               if (ret < 0) {
-                       if (ret != -ENOSPC)
-                               mlog_errno(ret);
-                       goto out;
-               }
-       }
-
-       if (clusters_to_add == 0)
-               goto out;
-
-       ret = ocfs2_reserve_clusters(osb, clusters_to_add, data_ac);
-       if (ret < 0) {
-               if (ret != -ENOSPC)
-                       mlog_errno(ret);
-               goto out;
-       }
-
-out:
-       if (ret) {
-               if (*meta_ac) {
-                       ocfs2_free_alloc_context(*meta_ac);
-                       *meta_ac = NULL;
-               }
+       int ret;
+       struct ocfs2_extent_tree et;
 
-               /*
-                * We cannot have an error and a non null *data_ac.
-                */
-       }
+       ocfs2_init_dinode_extent_tree(&et, inode, fe_bh);
+       ret = ocfs2_add_clusters_in_btree(osb, inode, logical_offset,
+                                          clusters_to_add, mark_unwritten,
+                                          &et, handle,
+                                          data_ac, meta_ac, reason_ret);
 
        return ret;
 }
@@ -698,6 +535,7 @@ static int __ocfs2_extend_allocation(struct inode *inode, u32 logical_start,
        struct ocfs2_alloc_context *meta_ac = NULL;
        enum ocfs2_alloc_restarted why;
        struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
+       struct ocfs2_extent_tree et;
 
        mlog_entry("(clusters_to_add = %u)\n", clusters_to_add);
 
@@ -707,8 +545,7 @@ static int __ocfs2_extend_allocation(struct inode *inode, u32 logical_start,
         */
        BUG_ON(mark_unwritten && !ocfs2_sparse_alloc(osb));
 
-       status = ocfs2_read_block(osb, OCFS2_I(inode)->ip_blkno, &bh,
-                                 OCFS2_BH_CACHED, inode);
+       status = ocfs2_read_block(inode, OCFS2_I(inode)->ip_blkno, &bh);
        if (status < 0) {
                mlog_errno(status);
                goto leave;
@@ -724,14 +561,21 @@ static int __ocfs2_extend_allocation(struct inode *inode, u32 logical_start,
 restart_all:
        BUG_ON(le32_to_cpu(fe->i_clusters) != OCFS2_I(inode)->ip_clusters);
 
-       status = ocfs2_lock_allocators(inode, fe, clusters_to_add, 0, &data_ac,
-                                      &meta_ac);
+       mlog(0, "extend inode %llu, i_size = %lld, di->i_clusters = %u, "
+            "clusters_to_add = %u\n",
+            (unsigned long long)OCFS2_I(inode)->ip_blkno,
+            (long long)i_size_read(inode), le32_to_cpu(fe->i_clusters),
+            clusters_to_add);
+       ocfs2_init_dinode_extent_tree(&et, inode, bh);
+       status = ocfs2_lock_allocators(inode, &et, clusters_to_add, 0,
+                                      &data_ac, &meta_ac);
        if (status) {
                mlog_errno(status);
                goto leave;
        }
 
-       credits = ocfs2_calc_extend_credits(osb->sb, fe, clusters_to_add);
+       credits = ocfs2_calc_extend_credits(osb->sb, &fe->id2.i_list,
+                                           clusters_to_add);
        handle = ocfs2_start_trans(osb, credits);
        if (IS_ERR(handle)) {
                status = PTR_ERR(handle);
@@ -753,16 +597,16 @@ restarted_transaction:
 
        prev_clusters = OCFS2_I(inode)->ip_clusters;
 
-       status = ocfs2_do_extend_allocation(osb,
-                                           inode,
-                                           &logical_start,
-                                           clusters_to_add,
-                                           mark_unwritten,
-                                           bh,
-                                           handle,
-                                           data_ac,
-                                           meta_ac,
-                                           &why);
+       status = ocfs2_add_inode_data(osb,
+                                     inode,
+                                     &logical_start,
+                                     clusters_to_add,
+                                     mark_unwritten,
+                                     bh,
+                                     handle,
+                                     data_ac,
+                                     meta_ac,
+                                     &why);
        if ((status < 0) && (status != -EAGAIN)) {
                if (status != -ENOSPC)
                        mlog_errno(status);
@@ -789,7 +633,7 @@ restarted_transaction:
                        mlog(0, "restarting transaction.\n");
                        /* TODO: This can be more intelligent. */
                        credits = ocfs2_calc_extend_credits(osb->sb,
-                                                           fe,
+                                                           &fe->id2.i_list,
                                                            clusters_to_add);
                        status = ocfs2_extend_trans(handle, credits);
                        if (status < 0) {
@@ -826,10 +670,8 @@ leave:
                restart_func = 0;
                goto restart_all;
        }
-       if (bh) {
-               brelse(bh);
-               bh = NULL;
-       }
+       brelse(bh);
+       bh = NULL;
 
        mlog_exit(status);
        return status;
@@ -1096,9 +938,15 @@ int ocfs2_setattr(struct dentry *dentry, struct iattr *attr)
                        goto bail_unlock;
                }
 
-               if (i_size_read(inode) > attr->ia_size)
+               if (i_size_read(inode) > attr->ia_size) {
+                       if (ocfs2_should_order_data(inode)) {
+                               status = ocfs2_begin_ordered_truncate(inode,
+                                                                     attr->ia_size);
+                               if (status)
+                                       goto bail_unlock;
+                       }
                        status = ocfs2_truncate_file(inode, bh, attr->ia_size);
-               else
+               else
                        status = ocfs2_extend_file(inode, bh, attr->ia_size);
                if (status < 0) {
                        if (status != -ENOSPC)
@@ -1140,8 +988,7 @@ bail_unlock_rw:
        if (size_change)
                ocfs2_rw_unlock(inode, 1);
 bail:
-       if (bh)
-               brelse(bh);
+       brelse(bh);
 
        mlog_exit(status);
        return status;
@@ -1284,8 +1131,7 @@ static int ocfs2_write_remove_suid(struct inode *inode)
        struct buffer_head *bh = NULL;
        struct ocfs2_inode_info *oi = OCFS2_I(inode);
 
-       ret = ocfs2_read_block(OCFS2_SB(inode->i_sb),
-                              oi->ip_blkno, &bh, OCFS2_BH_CACHED, inode);
+       ret = ocfs2_read_block(inode, oi->ip_blkno, &bh);
        if (ret < 0) {
                mlog_errno(ret);
                goto out;
@@ -1311,9 +1157,8 @@ static int ocfs2_allocate_unwritten_extents(struct inode *inode,
        struct buffer_head *di_bh = NULL;
 
        if (OCFS2_I(inode)->ip_dyn_features & OCFS2_INLINE_DATA_FL) {
-               ret = ocfs2_read_block(OCFS2_SB(inode->i_sb),
-                                      OCFS2_I(inode)->ip_blkno, &di_bh,
-                                      OCFS2_BH_CACHED, inode);
+               ret = ocfs2_read_block(inode, OCFS2_I(inode)->ip_blkno,
+                                      &di_bh);
                if (ret) {
                        mlog_errno(ret);
                        goto out;
@@ -1394,8 +1239,11 @@ static int __ocfs2_remove_inode_range(struct inode *inode,
        handle_t *handle;
        struct ocfs2_alloc_context *meta_ac = NULL;
        struct ocfs2_dinode *di = (struct ocfs2_dinode *)di_bh->b_data;
+       struct ocfs2_extent_tree et;
 
-       ret = ocfs2_lock_allocators(inode, di, 0, 1, NULL, &meta_ac);
+       ocfs2_init_dinode_extent_tree(&et, inode, di_bh);
+
+       ret = ocfs2_lock_allocators(inode, &et, 0, 1, NULL, &meta_ac);
        if (ret) {
                mlog_errno(ret);
                return ret;
@@ -1425,7 +1273,7 @@ static int __ocfs2_remove_inode_range(struct inode *inode,
                goto out;
        }
 
-       ret = ocfs2_remove_extent(inode, di_bh, cpos, len, handle, meta_ac,
+       ret = ocfs2_remove_extent(inode, &et, cpos, len, handle, meta_ac,
                                  dealloc);
        if (ret) {
                mlog_errno(ret);
@@ -2040,7 +1888,7 @@ out_dio:
                 */
                if (old_size != i_size_read(inode) ||
                    old_clusters != OCFS2_I(inode)->ip_clusters) {
-                       ret = journal_force_commit(osb->journal->j_journal);
+                       ret = jbd2_journal_force_commit(osb->journal->j_journal);
                        if (ret < 0)
                                written = ret;
                }
@@ -2227,6 +2075,10 @@ const struct inode_operations ocfs2_file_iops = {
        .setattr        = ocfs2_setattr,
        .getattr        = ocfs2_getattr,
        .permission     = ocfs2_permission,
+       .setxattr       = generic_setxattr,
+       .getxattr       = generic_getxattr,
+       .listxattr      = ocfs2_listxattr,
+       .removexattr    = generic_removexattr,
        .fallocate      = ocfs2_fallocate,
        .fiemap         = ocfs2_fiemap,
 };
@@ -2237,6 +2089,10 @@ const struct inode_operations ocfs2_special_file_iops = {
        .permission     = ocfs2_permission,
 };
 
+/*
+ * Other than ->lock, keep ocfs2_fops and ocfs2_dops in sync with
+ * ocfs2_fops_no_plocks and ocfs2_dops_no_plocks!
+ */
 const struct file_operations ocfs2_fops = {
        .llseek         = generic_file_llseek,
        .read           = do_sync_read,
@@ -2251,6 +2107,7 @@ const struct file_operations ocfs2_fops = {
 #ifdef CONFIG_COMPAT
        .compat_ioctl   = ocfs2_compat_ioctl,
 #endif
+       .lock           = ocfs2_lock,
        .flock          = ocfs2_flock,
        .splice_read    = ocfs2_file_splice_read,
        .splice_write   = ocfs2_file_splice_write,
@@ -2266,6 +2123,52 @@ const struct file_operations ocfs2_dops = {
        .unlocked_ioctl = ocfs2_ioctl,
 #ifdef CONFIG_COMPAT
        .compat_ioctl   = ocfs2_compat_ioctl,
+#endif
+       .lock           = ocfs2_lock,
+       .flock          = ocfs2_flock,
+};
+
+/*
+ * POSIX-lockless variants of our file_operations.
+ *
+ * These will be used if the underlying cluster stack does not support
+ * posix file locking, if the user passes the "localflocks" mount
+ * option, or if we have a local-only fs.
+ *
+ * ocfs2_flock is in here because all stacks handle UNIX file locks,
+ * so we still want it in the case of no stack support for
+ * plocks. Internally, it will do the right thing when asked to ignore
+ * the cluster.
+ */
+const struct file_operations ocfs2_fops_no_plocks = {
+       .llseek         = generic_file_llseek,
+       .read           = do_sync_read,
+       .write          = do_sync_write,
+       .mmap           = ocfs2_mmap,
+       .fsync          = ocfs2_sync_file,
+       .release        = ocfs2_file_release,
+       .open           = ocfs2_file_open,
+       .aio_read       = ocfs2_file_aio_read,
+       .aio_write      = ocfs2_file_aio_write,
+       .unlocked_ioctl = ocfs2_ioctl,
+#ifdef CONFIG_COMPAT
+       .compat_ioctl   = ocfs2_compat_ioctl,
+#endif
+       .flock          = ocfs2_flock,
+       .splice_read    = ocfs2_file_splice_read,
+       .splice_write   = ocfs2_file_splice_write,
+};
+
+const struct file_operations ocfs2_dops_no_plocks = {
+       .llseek         = generic_file_llseek,
+       .read           = generic_read_dir,
+       .readdir        = ocfs2_readdir,
+       .fsync          = ocfs2_sync_file,
+       .release        = ocfs2_dir_release,
+       .open           = ocfs2_dir_open,
+       .unlocked_ioctl = ocfs2_ioctl,
+#ifdef CONFIG_COMPAT
+       .compat_ioctl   = ocfs2_compat_ioctl,
 #endif
        .flock          = ocfs2_flock,
 };
index 1e27b4d017ea49401ec43445a29d62bcd6b219ce..e92382cbca5fb94c20b963d465c90853a8ab84de 100644 (file)
 
 extern const struct file_operations ocfs2_fops;
 extern const struct file_operations ocfs2_dops;
+extern const struct file_operations ocfs2_fops_no_plocks;
+extern const struct file_operations ocfs2_dops_no_plocks;
 extern const struct inode_operations ocfs2_file_iops;
 extern const struct inode_operations ocfs2_special_file_iops;
 struct ocfs2_alloc_context;
+enum ocfs2_alloc_restarted;
 
 struct ocfs2_file_private {
        struct file             *fp_file;
@@ -38,27 +41,18 @@ struct ocfs2_file_private {
        struct ocfs2_lock_res   fp_flock;
 };
 
-enum ocfs2_alloc_restarted {
-       RESTART_NONE = 0,
-       RESTART_TRANS,
-       RESTART_META
-};
-int ocfs2_do_extend_allocation(struct ocfs2_super *osb,
-                              struct inode *inode,
-                              u32 *logical_offset,
-                              u32 clusters_to_add,
-                              int mark_unwritten,
-                              struct buffer_head *fe_bh,
-                              handle_t *handle,
-                              struct ocfs2_alloc_context *data_ac,
-                              struct ocfs2_alloc_context *meta_ac,
-                              enum ocfs2_alloc_restarted *reason_ret);
+int ocfs2_add_inode_data(struct ocfs2_super *osb,
+                        struct inode *inode,
+                        u32 *logical_offset,
+                        u32 clusters_to_add,
+                        int mark_unwritten,
+                        struct buffer_head *fe_bh,
+                        handle_t *handle,
+                        struct ocfs2_alloc_context *data_ac,
+                        struct ocfs2_alloc_context *meta_ac,
+                        enum ocfs2_alloc_restarted *reason_ret);
 int ocfs2_extend_no_holes(struct inode *inode, u64 new_i_size,
                          u64 zero_to);
-int ocfs2_lock_allocators(struct inode *inode, struct ocfs2_dinode *di,
-                         u32 clusters_to_add, u32 extents_to_split,
-                         struct ocfs2_alloc_context **data_ac,
-                         struct ocfs2_alloc_context **meta_ac);
 int ocfs2_setattr(struct dentry *dentry, struct iattr *attr);
 int ocfs2_getattr(struct vfsmount *mnt, struct dentry *dentry,
                  struct kstat *stat);
index 7e9e4c79aec7bf4b3e4488190f47972821a3945f..4903688f72a9314f03ab66183de3104b54a3eed1 100644 (file)
@@ -49,6 +49,7 @@
 #include "symlink.h"
 #include "sysfile.h"
 #include "uptodate.h"
+#include "xattr.h"
 
 #include "buffer_head_io.h"
 
@@ -219,6 +220,7 @@ int ocfs2_populate_inode(struct inode *inode, struct ocfs2_dinode *fe,
        struct super_block *sb;
        struct ocfs2_super *osb;
        int status = -EINVAL;
+       int use_plocks = 1;
 
        mlog_entry("(0x%p, size:%llu)\n", inode,
                   (unsigned long long)le64_to_cpu(fe->i_size));
@@ -226,6 +228,10 @@ int ocfs2_populate_inode(struct inode *inode, struct ocfs2_dinode *fe,
        sb = inode->i_sb;
        osb = OCFS2_SB(sb);
 
+       if ((osb->s_mount_opt & OCFS2_MOUNT_LOCALFLOCKS) ||
+           ocfs2_mount_local(osb) || !ocfs2_stack_supports_plocks())
+               use_plocks = 0;
+
        /* this means that read_inode cannot create a superblock inode
         * today.  change if needed. */
        if (!OCFS2_IS_VALID_DINODE(fe) ||
@@ -295,13 +301,19 @@ int ocfs2_populate_inode(struct inode *inode, struct ocfs2_dinode *fe,
 
        switch (inode->i_mode & S_IFMT) {
            case S_IFREG:
-                   inode->i_fop = &ocfs2_fops;
+                   if (use_plocks)
+                           inode->i_fop = &ocfs2_fops;
+                   else
+                           inode->i_fop = &ocfs2_fops_no_plocks;
                    inode->i_op = &ocfs2_file_iops;
                    i_size_write(inode, le64_to_cpu(fe->i_size));
                    break;
            case S_IFDIR:
                    inode->i_op = &ocfs2_dir_iops;
-                   inode->i_fop = &ocfs2_dops;
+                   if (use_plocks)
+                           inode->i_fop = &ocfs2_dops;
+                   else
+                           inode->i_fop = &ocfs2_dops_no_plocks;
                    i_size_write(inode, le64_to_cpu(fe->i_size));
                    break;
            case S_IFLNK:
@@ -448,8 +460,11 @@ static int ocfs2_read_locked_inode(struct inode *inode,
                }
        }
 
-       status = ocfs2_read_block(osb, args->fi_blkno, &bh, 0,
-                                 can_lock ? inode : NULL);
+       if (can_lock)
+               status = ocfs2_read_blocks(inode, args->fi_blkno, 1, &bh,
+                                          OCFS2_BH_IGNORE_CACHE);
+       else
+               status = ocfs2_read_blocks_sync(osb, args->fi_blkno, 1, &bh);
        if (status < 0) {
                mlog_errno(status);
                goto bail;
@@ -522,6 +537,9 @@ static int ocfs2_truncate_for_delete(struct ocfs2_super *osb,
         * data and fast symlinks.
         */
        if (fe->i_clusters) {
+               if (ocfs2_should_order_data(inode))
+                       ocfs2_begin_ordered_truncate(inode, 0);
+
                handle = ocfs2_start_trans(osb, OCFS2_INODE_UPDATE_CREDITS);
                if (IS_ERR(handle)) {
                        status = PTR_ERR(handle);
@@ -730,6 +748,13 @@ static int ocfs2_wipe_inode(struct inode *inode,
                goto bail_unlock_dir;
        }
 
+       /*Free extended attribute resources associated with this inode.*/
+       status = ocfs2_xattr_remove(inode, di_bh);
+       if (status < 0) {
+               mlog_errno(status);
+               goto bail_unlock_dir;
+       }
+
        status = ocfs2_remove_inode(inode, di_bh, orphan_dir_inode,
                                    orphan_dir_bh);
        if (status < 0)
@@ -1081,6 +1106,8 @@ void ocfs2_clear_inode(struct inode *inode)
        oi->ip_last_trans = 0;
        oi->ip_dir_start_lookup = 0;
        oi->ip_blkno = 0ULL;
+       jbd2_journal_release_jbd_inode(OCFS2_SB(inode->i_sb)->journal->j_journal,
+                                      &oi->ip_jinode);
 
 bail:
        mlog_exit_void();
@@ -1106,58 +1133,6 @@ void ocfs2_drop_inode(struct inode *inode)
        mlog_exit_void();
 }
 
-/*
- * TODO: this should probably be merged into ocfs2_get_block
- *
- * However, you now need to pay attention to the cont_prepare_write()
- * stuff in ocfs2_get_block (that is, ocfs2_get_block pretty much
- * expects never to extend).
- */
-struct buffer_head *ocfs2_bread(struct inode *inode,
-                               int block, int *err, int reada)
-{
-       struct buffer_head *bh = NULL;
-       int tmperr;
-       u64 p_blkno;
-       int readflags = OCFS2_BH_CACHED;
-
-       if (reada)
-               readflags |= OCFS2_BH_READAHEAD;
-
-       if (((u64)block << inode->i_sb->s_blocksize_bits) >=
-           i_size_read(inode)) {
-               BUG_ON(!reada);
-               return NULL;
-       }
-
-       down_read(&OCFS2_I(inode)->ip_alloc_sem);
-       tmperr = ocfs2_extent_map_get_blocks(inode, block, &p_blkno, NULL,
-                                            NULL);
-       up_read(&OCFS2_I(inode)->ip_alloc_sem);
-       if (tmperr < 0) {
-               mlog_errno(tmperr);
-               goto fail;
-       }
-
-       tmperr = ocfs2_read_block(OCFS2_SB(inode->i_sb), p_blkno, &bh,
-                                 readflags, inode);
-       if (tmperr < 0)
-               goto fail;
-
-       tmperr = 0;
-
-       *err = 0;
-       return bh;
-
-fail:
-       if (bh) {
-               brelse(bh);
-               bh = NULL;
-       }
-       *err = -EIO;
-       return NULL;
-}
-
 /*
  * This is called from our getattr.
  */
index 390a85596aa01841a5546e9464aa7a24cfcc23ea..2f37af9bcc4aa262197ca0f3108941c4d7d551fb 100644 (file)
@@ -40,6 +40,9 @@ struct ocfs2_inode_info
        /* protects allocation changes on this inode. */
        struct rw_semaphore             ip_alloc_sem;
 
+       /* protects extended attribute changes on this inode */
+       struct rw_semaphore             ip_xattr_sem;
+
        /* These fields are protected by ip_lock */
        spinlock_t                      ip_lock;
        u32                             ip_open_count;
@@ -68,6 +71,7 @@ struct ocfs2_inode_info
        struct ocfs2_extent_map         ip_extent_map;
 
        struct inode                    vfs_inode;
+       struct jbd2_inode               ip_jinode;
 };
 
 /*
@@ -113,8 +117,6 @@ extern struct kmem_cache *ocfs2_inode_cache;
 
 extern const struct address_space_operations ocfs2_aops;
 
-struct buffer_head *ocfs2_bread(struct inode *inode, int block,
-                               int *err, int reada);
 void ocfs2_clear_inode(struct inode *inode);
 void ocfs2_delete_inode(struct inode *inode);
 void ocfs2_drop_inode(struct inode *inode);
index 7b142f0ce995bd07a5116567a0b2b56e65b8dd1b..9fcd36dcc9a05920e5cc3f2d4d001d5bc8a82120 100644 (file)
@@ -102,8 +102,7 @@ bail_unlock:
 bail:
        mutex_unlock(&inode->i_mutex);
 
-       if (bh)
-               brelse(bh);
+       brelse(bh);
 
        mlog_exit(status);
        return status;
index c47bc2a809c2493703563f7f88e2b4f8a5bf1f9c..81e40677eecbddf989722e51f0336f43d6f84ff8 100644 (file)
@@ -215,9 +215,9 @@ static int ocfs2_commit_cache(struct ocfs2_super *osb)
                goto finally;
        }
 
-       journal_lock_updates(journal->j_journal);
-       status = journal_flush(journal->j_journal);
-       journal_unlock_updates(journal->j_journal);
+       jbd2_journal_lock_updates(journal->j_journal);
+       status = jbd2_journal_flush(journal->j_journal);
+       jbd2_journal_unlock_updates(journal->j_journal);
        if (status < 0) {
                up_write(&journal->j_trans_barrier);
                mlog_errno(status);
@@ -264,7 +264,7 @@ handle_t *ocfs2_start_trans(struct ocfs2_super *osb, int max_buffs)
 
        down_read(&osb->journal->j_trans_barrier);
 
-       handle = journal_start(journal, max_buffs);
+       handle = jbd2_journal_start(journal, max_buffs);
        if (IS_ERR(handle)) {
                up_read(&osb->journal->j_trans_barrier);
 
@@ -290,7 +290,7 @@ int ocfs2_commit_trans(struct ocfs2_super *osb,
 
        BUG_ON(!handle);
 
-       ret = journal_stop(handle);
+       ret = jbd2_journal_stop(handle);
        if (ret < 0)
                mlog_errno(ret);
 
@@ -304,7 +304,7 @@ int ocfs2_commit_trans(struct ocfs2_super *osb,
  * transaction. extend_trans will either extend the current handle by
  * nblocks, or commit it and start a new one with nblocks credits.
  *
- * This might call journal_restart() which will commit dirty buffers
+ * This might call jbd2_journal_restart() which will commit dirty buffers
  * and then restart the transaction. Before calling
  * ocfs2_extend_trans(), any changed blocks should have been
  * dirtied. After calling it, all blocks which need to be changed must
@@ -332,7 +332,7 @@ int ocfs2_extend_trans(handle_t *handle, int nblocks)
 #ifdef CONFIG_OCFS2_DEBUG_FS
        status = 1;
 #else
-       status = journal_extend(handle, nblocks);
+       status = jbd2_journal_extend(handle, nblocks);
        if (status < 0) {
                mlog_errno(status);
                goto bail;
@@ -340,8 +340,10 @@ int ocfs2_extend_trans(handle_t *handle, int nblocks)
 #endif
 
        if (status > 0) {
-               mlog(0, "journal_extend failed, trying journal_restart\n");
-               status = journal_restart(handle, nblocks);
+               mlog(0,
+                    "jbd2_journal_extend failed, trying "
+                    "jbd2_journal_restart\n");
+               status = jbd2_journal_restart(handle, nblocks);
                if (status < 0) {
                        mlog_errno(status);
                        goto bail;
@@ -393,11 +395,11 @@ int ocfs2_journal_access(handle_t *handle,
        switch (type) {
        case OCFS2_JOURNAL_ACCESS_CREATE:
        case OCFS2_JOURNAL_ACCESS_WRITE:
-               status = journal_get_write_access(handle, bh);
+               status = jbd2_journal_get_write_access(handle, bh);
                break;
 
        case OCFS2_JOURNAL_ACCESS_UNDO:
-               status = journal_get_undo_access(handle, bh);
+               status = jbd2_journal_get_undo_access(handle, bh);
                break;
 
        default:
@@ -422,7 +424,7 @@ int ocfs2_journal_dirty(handle_t *handle,
        mlog_entry("(bh->b_blocknr=%llu)\n",
                   (unsigned long long)bh->b_blocknr);
 
-       status = journal_dirty_metadata(handle, bh);
+       status = jbd2_journal_dirty_metadata(handle, bh);
        if (status < 0)
                mlog(ML_ERROR, "Could not dirty metadata buffer. "
                     "(bh->b_blocknr=%llu)\n",
@@ -432,6 +434,7 @@ int ocfs2_journal_dirty(handle_t *handle,
        return status;
 }
 
+#ifdef CONFIG_OCFS2_COMPAT_JBD
 int ocfs2_journal_dirty_data(handle_t *handle,
                             struct buffer_head *bh)
 {
@@ -443,8 +446,9 @@ int ocfs2_journal_dirty_data(handle_t *handle,
 
        return err;
 }
+#endif
 
-#define OCFS2_DEFAULT_COMMIT_INTERVAL  (HZ * JBD_DEFAULT_MAX_COMMIT_AGE)
+#define OCFS2_DEFAULT_COMMIT_INTERVAL  (HZ * JBD2_DEFAULT_MAX_COMMIT_AGE)
 
 void ocfs2_set_journal_params(struct ocfs2_super *osb)
 {
@@ -457,9 +461,9 @@ void ocfs2_set_journal_params(struct ocfs2_super *osb)
        spin_lock(&journal->j_state_lock);
        journal->j_commit_interval = commit_interval;
        if (osb->s_mount_opt & OCFS2_MOUNT_BARRIER)
-               journal->j_flags |= JFS_BARRIER;
+               journal->j_flags |= JBD2_BARRIER;
        else
-               journal->j_flags &= ~JFS_BARRIER;
+               journal->j_flags &= ~JBD2_BARRIER;
        spin_unlock(&journal->j_state_lock);
 }
 
@@ -524,14 +528,14 @@ int ocfs2_journal_init(struct ocfs2_journal *journal, int *dirty)
        mlog(0, "inode->ip_clusters = %u\n", OCFS2_I(inode)->ip_clusters);
 
        /* call the kernels journal init function now */
-       j_journal = journal_init_inode(inode);
+       j_journal = jbd2_journal_init_inode(inode);
        if (j_journal == NULL) {
                mlog(ML_ERROR, "Linux journal layer error\n");
                status = -EINVAL;
                goto done;
        }
 
-       mlog(0, "Returned from journal_init_inode\n");
+       mlog(0, "Returned from jbd2_journal_init_inode\n");
        mlog(0, "j_journal->j_maxlen = %u\n", j_journal->j_maxlen);
 
        *dirty = (le32_to_cpu(di->id1.journal1.ij_flags) &
@@ -550,8 +554,7 @@ done:
        if (status < 0) {
                if (inode_lock)
                        ocfs2_inode_unlock(inode, 1);
-               if (bh != NULL)
-                       brelse(bh);
+               brelse(bh);
                if (inode) {
                        OCFS2_I(inode)->ip_open_count--;
                        iput(inode);
@@ -639,7 +642,7 @@ void ocfs2_journal_shutdown(struct ocfs2_super *osb)
        if (journal->j_state != OCFS2_JOURNAL_LOADED)
                goto done;
 
-       /* need to inc inode use count as journal_destroy will iput. */
+       /* need to inc inode use count - jbd2_journal_destroy will iput. */
        if (!igrab(inode))
                BUG();
 
@@ -668,9 +671,9 @@ void ocfs2_journal_shutdown(struct ocfs2_super *osb)
        BUG_ON(atomic_read(&(osb->journal->j_num_trans)) != 0);
 
        if (ocfs2_mount_local(osb)) {
-               journal_lock_updates(journal->j_journal);
-               status = journal_flush(journal->j_journal);
-               journal_unlock_updates(journal->j_journal);
+               jbd2_journal_lock_updates(journal->j_journal);
+               status = jbd2_journal_flush(journal->j_journal);
+               jbd2_journal_unlock_updates(journal->j_journal);
                if (status < 0)
                        mlog_errno(status);
        }
@@ -686,7 +689,7 @@ void ocfs2_journal_shutdown(struct ocfs2_super *osb)
        }
 
        /* Shutdown the kernel journal system */
-       journal_destroy(journal->j_journal);
+       jbd2_journal_destroy(journal->j_journal);
 
        OCFS2_I(inode)->ip_open_count--;
 
@@ -711,15 +714,15 @@ static void ocfs2_clear_journal_error(struct super_block *sb,
 {
        int olderr;
 
-       olderr = journal_errno(journal);
+       olderr = jbd2_journal_errno(journal);
        if (olderr) {
                mlog(ML_ERROR, "File system error %d recorded in "
                     "journal %u.\n", olderr, slot);
                mlog(ML_ERROR, "File system on device %s needs checking.\n",
                     sb->s_id);
 
-               journal_ack_err(journal);
-               journal_clear_err(journal);
+               jbd2_journal_ack_err(journal);
+               jbd2_journal_clear_err(journal);
        }
 }
 
@@ -734,7 +737,7 @@ int ocfs2_journal_load(struct ocfs2_journal *journal, int local, int replayed)
 
        osb = journal->j_osb;
 
-       status = journal_load(journal->j_journal);
+       status = jbd2_journal_load(journal->j_journal);
        if (status < 0) {
                mlog(ML_ERROR, "Failed to load journal!\n");
                goto done;
@@ -778,7 +781,7 @@ int ocfs2_journal_wipe(struct ocfs2_journal *journal, int full)
 
        BUG_ON(!journal);
 
-       status = journal_wipe(journal->j_journal, full);
+       status = jbd2_journal_wipe(journal->j_journal, full);
        if (status < 0) {
                mlog_errno(status);
                goto bail;
@@ -847,9 +850,8 @@ static int ocfs2_force_read_journal(struct inode *inode)
 
                /* We are reading journal data which should not
                 * be put in the uptodate cache */
-               status = ocfs2_read_blocks(OCFS2_SB(inode->i_sb),
-                                          p_blkno, p_blocks, bhs, 0,
-                                          NULL);
+               status = ocfs2_read_blocks_sync(OCFS2_SB(inode->i_sb),
+                                               p_blkno, p_blocks, bhs);
                if (status < 0) {
                        mlog_errno(status);
                        goto bail;
@@ -865,8 +867,7 @@ static int ocfs2_force_read_journal(struct inode *inode)
 
 bail:
        for(i = 0; i < CONCURRENT_JOURNAL_FILL; i++)
-               if (bhs[i])
-                       brelse(bhs[i]);
+               brelse(bhs[i]);
        mlog_exit(status);
        return status;
 }
@@ -1133,7 +1134,8 @@ static int ocfs2_read_journal_inode(struct ocfs2_super *osb,
        }
        SET_INODE_JOURNAL(inode);
 
-       status = ocfs2_read_block(osb, OCFS2_I(inode)->ip_blkno, bh, 0, inode);
+       status = ocfs2_read_blocks(inode, OCFS2_I(inode)->ip_blkno, 1, bh,
+                                  OCFS2_BH_IGNORE_CACHE);
        if (status < 0) {
                mlog_errno(status);
                goto bail;
@@ -1229,19 +1231,19 @@ static int ocfs2_replay_journal(struct ocfs2_super *osb,
        }
 
        mlog(0, "calling journal_init_inode\n");
-       journal = journal_init_inode(inode);
+       journal = jbd2_journal_init_inode(inode);
        if (journal == NULL) {
                mlog(ML_ERROR, "Linux journal layer error\n");
                status = -EIO;
                goto done;
        }
 
-       status = journal_load(journal);
+       status = jbd2_journal_load(journal);
        if (status < 0) {
                mlog_errno(status);
                if (!igrab(inode))
                        BUG();
-               journal_destroy(journal);
+               jbd2_journal_destroy(journal);
                goto done;
        }
 
@@ -1249,9 +1251,9 @@ static int ocfs2_replay_journal(struct ocfs2_super *osb,
 
        /* wipe the journal */
        mlog(0, "flushing the journal.\n");
-       journal_lock_updates(journal);
-       status = journal_flush(journal);
-       journal_unlock_updates(journal);
+       jbd2_journal_lock_updates(journal);
+       status = jbd2_journal_flush(journal);
+       jbd2_journal_unlock_updates(journal);
        if (status < 0)
                mlog_errno(status);
 
@@ -1272,7 +1274,7 @@ static int ocfs2_replay_journal(struct ocfs2_super *osb,
        if (!igrab(inode))
                BUG();
 
-       journal_destroy(journal);
+       jbd2_journal_destroy(journal);
 
 done:
        /* drop the lock on this nodes journal */
@@ -1282,8 +1284,7 @@ done:
        if (inode)
                iput(inode);
 
-       if (bh)
-               brelse(bh);
+       brelse(bh);
 
        mlog_exit(status);
        return status;
index 2178ebffa05f62c85bb8251958d9b71c48370f0d..d4d14e9a3cea123718e9314c469d6c011a5884ca 100644 (file)
 #define OCFS2_JOURNAL_H
 
 #include <linux/fs.h>
-#include <linux/jbd.h>
+#ifndef CONFIG_OCFS2_COMPAT_JBD
+# include <linux/jbd2.h>
+#else
+# include <linux/jbd.h>
+# include "ocfs2_jbd_compat.h"
+#endif
 
 enum ocfs2_journal_state {
        OCFS2_JOURNAL_FREE = 0,
@@ -215,8 +220,8 @@ static inline void ocfs2_checkpoint_inode(struct inode *inode)
  *                          buffer. Will have to call ocfs2_journal_dirty once
  *                          we've actually dirtied it. Type is one of . or .
  *  ocfs2_journal_dirty    - Mark a journalled buffer as having dirty data.
- *  ocfs2_journal_dirty_data - Indicate that a data buffer should go out before
- *                             the current handle commits.
+ *  ocfs2_jbd2_file_inode  - Mark an inode so that its data goes out before
+ *                           the current handle commits.
  */
 
 /* You must always start_trans with a number of buffs > 0, but it's
@@ -268,8 +273,10 @@ int                  ocfs2_journal_access(handle_t *handle,
  */
 int                  ocfs2_journal_dirty(handle_t *handle,
                                         struct buffer_head *bh);
+#ifdef CONFIG_OCFS2_COMPAT_JBD
 int                  ocfs2_journal_dirty_data(handle_t *handle,
                                              struct buffer_head *bh);
+#endif
 
 /*
  *  Credit Macros:
@@ -283,6 +290,9 @@ int                  ocfs2_journal_dirty_data(handle_t *handle,
 /* simple file updates like chmod, etc. */
 #define OCFS2_INODE_UPDATE_CREDITS 1
 
+/* extended attribute block update */
+#define OCFS2_XATTR_BLOCK_UPDATE_CREDITS 1
+
 /* group extend. inode update and last group update. */
 #define OCFS2_GROUP_EXTEND_CREDITS     (OCFS2_INODE_UPDATE_CREDITS + 1)
 
@@ -340,11 +350,23 @@ int                  ocfs2_journal_dirty_data(handle_t *handle,
 #define OCFS2_RENAME_CREDITS (3 * OCFS2_INODE_UPDATE_CREDITS + 3              \
                             + OCFS2_UNLINK_CREDITS)
 
+/* global bitmap dinode, group desc., relinked group,
+ * suballocator dinode, group desc., relinked group,
+ * dinode, xattr block */
+#define OCFS2_XATTR_BLOCK_CREATE_CREDITS (OCFS2_SUBALLOC_ALLOC * 2 + \
+                                         + OCFS2_INODE_UPDATE_CREDITS \
+                                         + OCFS2_XATTR_BLOCK_UPDATE_CREDITS)
+
+/*
+ * Please note that the caller must make sure that root_el is the root
+ * of extent tree. So for an inode, it should be &fe->id2.i_list. Otherwise
+ * the result may be wrong.
+ */
 static inline int ocfs2_calc_extend_credits(struct super_block *sb,
-                                           struct ocfs2_dinode *fe,
+                                           struct ocfs2_extent_list *root_el,
                                            u32 bits_wanted)
 {
-       int bitmap_blocks, sysfile_bitmap_blocks, dinode_blocks;
+       int bitmap_blocks, sysfile_bitmap_blocks, extent_blocks;
 
        /* bitmap dinode, group desc. + relinked group. */
        bitmap_blocks = OCFS2_SUBALLOC_ALLOC;
@@ -355,16 +377,16 @@ static inline int ocfs2_calc_extend_credits(struct super_block *sb,
         * however many metadata chunks needed * a remaining suballoc
         * alloc. */
        sysfile_bitmap_blocks = 1 +
-               (OCFS2_SUBALLOC_ALLOC - 1) * ocfs2_extend_meta_needed(fe);
+               (OCFS2_SUBALLOC_ALLOC - 1) * ocfs2_extend_meta_needed(root_el);
 
        /* this does not include *new* metadata blocks, which are
-        * accounted for in sysfile_bitmap_blocks. fe +
+        * accounted for in sysfile_bitmap_blocks. root_el +
         * prev. last_eb_blk + blocks along edge of tree.
         * calc_symlink_credits passes because we just need 1
         * credit for the dinode there. */
-       dinode_blocks = 1 + 1 + le16_to_cpu(fe->id2.i_list.l_tree_depth);
+       extent_blocks = 1 + 1 + le16_to_cpu(root_el->l_tree_depth);
 
-       return bitmap_blocks + sysfile_bitmap_blocks + dinode_blocks;
+       return bitmap_blocks + sysfile_bitmap_blocks + extent_blocks;
 }
 
 static inline int ocfs2_calc_symlink_credits(struct super_block *sb)
@@ -415,4 +437,16 @@ static inline int ocfs2_calc_tree_trunc_credits(struct super_block *sb,
        return credits;
 }
 
+static inline int ocfs2_jbd2_file_inode(handle_t *handle, struct inode *inode)
+{
+       return jbd2_journal_file_inode(handle, &OCFS2_I(inode)->ip_jinode);
+}
+
+static inline int ocfs2_begin_ordered_truncate(struct inode *inode,
+                                              loff_t new_size)
+{
+       return jbd2_journal_begin_ordered_truncate(&OCFS2_I(inode)->ip_jinode,
+                                                  new_size);
+}
+
 #endif /* OCFS2_JOURNAL_H */
index 28e492e4ec88b913964ffd49bcda163fb4f7690f..687b28713c3253631b3588ceb41d90ca51cfa867 100644 (file)
@@ -28,6 +28,7 @@
 #include <linux/slab.h>
 #include <linux/highmem.h>
 #include <linux/bitops.h>
+#include <linux/debugfs.h>
 
 #define MLOG_MASK_PREFIX ML_DISK_ALLOC
 #include <cluster/masklog.h>
@@ -47,8 +48,6 @@
 
 #define OCFS2_LOCAL_ALLOC(dinode)      (&((dinode)->id2.i_lab))
 
-static inline int ocfs2_local_alloc_window_bits(struct ocfs2_super *osb);
-
 static u32 ocfs2_local_alloc_count_bits(struct ocfs2_dinode *alloc);
 
 static int ocfs2_local_alloc_find_clear_bits(struct ocfs2_super *osb,
@@ -75,24 +74,129 @@ static int ocfs2_local_alloc_new_window(struct ocfs2_super *osb,
 static int ocfs2_local_alloc_slide_window(struct ocfs2_super *osb,
                                          struct inode *local_alloc_inode);
 
-static inline int ocfs2_local_alloc_window_bits(struct ocfs2_super *osb)
+#ifdef CONFIG_OCFS2_FS_STATS
+
+static int ocfs2_la_debug_open(struct inode *inode, struct file *file)
+{
+       file->private_data = inode->i_private;
+       return 0;
+}
+
+#define LA_DEBUG_BUF_SZ        PAGE_CACHE_SIZE
+#define LA_DEBUG_VER   1
+static ssize_t ocfs2_la_debug_read(struct file *file, char __user *userbuf,
+                                  size_t count, loff_t *ppos)
+{
+       static DEFINE_MUTEX(la_debug_mutex);
+       struct ocfs2_super *osb = file->private_data;
+       int written, ret;
+       char *buf = osb->local_alloc_debug_buf;
+
+       mutex_lock(&la_debug_mutex);
+       memset(buf, 0, LA_DEBUG_BUF_SZ);
+
+       written = snprintf(buf, LA_DEBUG_BUF_SZ,
+                          "0x%x\t0x%llx\t%u\t%u\t0x%x\n",
+                          LA_DEBUG_VER,
+                          (unsigned long long)osb->la_last_gd,
+                          osb->local_alloc_default_bits,
+                          osb->local_alloc_bits, osb->local_alloc_state);
+
+       ret = simple_read_from_buffer(userbuf, count, ppos, buf, written);
+
+       mutex_unlock(&la_debug_mutex);
+       return ret;
+}
+
+static const struct file_operations ocfs2_la_debug_fops = {
+       .open =         ocfs2_la_debug_open,
+       .read =         ocfs2_la_debug_read,
+};
+
+static void ocfs2_init_la_debug(struct ocfs2_super *osb)
+{
+       osb->local_alloc_debug_buf = kmalloc(LA_DEBUG_BUF_SZ, GFP_NOFS);
+       if (!osb->local_alloc_debug_buf)
+               return;
+
+       osb->local_alloc_debug = debugfs_create_file("local_alloc_stats",
+                                                    S_IFREG|S_IRUSR,
+                                                    osb->osb_debug_root,
+                                                    osb,
+                                                    &ocfs2_la_debug_fops);
+       if (!osb->local_alloc_debug) {
+               kfree(osb->local_alloc_debug_buf);
+               osb->local_alloc_debug_buf = NULL;
+       }
+}
+
+static void ocfs2_shutdown_la_debug(struct ocfs2_super *osb)
+{
+       if (osb->local_alloc_debug)
+               debugfs_remove(osb->local_alloc_debug);
+
+       if (osb->local_alloc_debug_buf)
+               kfree(osb->local_alloc_debug_buf);
+
+       osb->local_alloc_debug_buf = NULL;
+       osb->local_alloc_debug = NULL;
+}
+#else  /* CONFIG_OCFS2_FS_STATS */
+static void ocfs2_init_la_debug(struct ocfs2_super *osb)
+{
+       return;
+}
+static void ocfs2_shutdown_la_debug(struct ocfs2_super *osb)
+{
+       return;
+}
+#endif
+
+static inline int ocfs2_la_state_enabled(struct ocfs2_super *osb)
 {
-       BUG_ON(osb->s_clustersize_bits > 20);
+       return (osb->local_alloc_state == OCFS2_LA_THROTTLED ||
+               osb->local_alloc_state == OCFS2_LA_ENABLED);
+}
 
-       /* Size local alloc windows by the megabyte */
-       return osb->local_alloc_size << (20 - osb->s_clustersize_bits);
+void ocfs2_local_alloc_seen_free_bits(struct ocfs2_super *osb,
+                                     unsigned int num_clusters)
+{
+       spin_lock(&osb->osb_lock);
+       if (osb->local_alloc_state == OCFS2_LA_DISABLED ||
+           osb->local_alloc_state == OCFS2_LA_THROTTLED)
+               if (num_clusters >= osb->local_alloc_default_bits) {
+                       cancel_delayed_work(&osb->la_enable_wq);
+                       osb->local_alloc_state = OCFS2_LA_ENABLED;
+               }
+       spin_unlock(&osb->osb_lock);
+}
+
+void ocfs2_la_enable_worker(struct work_struct *work)
+{
+       struct ocfs2_super *osb =
+               container_of(work, struct ocfs2_super,
+                            la_enable_wq.work);
+       spin_lock(&osb->osb_lock);
+       osb->local_alloc_state = OCFS2_LA_ENABLED;
+       spin_unlock(&osb->osb_lock);
 }
 
 /*
  * Tell us whether a given allocation should use the local alloc
  * file. Otherwise, it has to go to the main bitmap.
+ *
+ * This function does semi-dirty reads of local alloc size and state!
+ * This is ok however, as the values are re-checked once under mutex.
  */
 int ocfs2_alloc_should_use_local(struct ocfs2_super *osb, u64 bits)
 {
-       int la_bits = ocfs2_local_alloc_window_bits(osb);
        int ret = 0;
+       int la_bits;
+
+       spin_lock(&osb->osb_lock);
+       la_bits = osb->local_alloc_bits;
 
-       if (osb->local_alloc_state != OCFS2_LA_ENABLED)
+       if (!ocfs2_la_state_enabled(osb))
                goto bail;
 
        /* la_bits should be at least twice the size (in clusters) of
@@ -106,6 +210,7 @@ int ocfs2_alloc_should_use_local(struct ocfs2_super *osb, u64 bits)
 bail:
        mlog(0, "state=%d, bits=%llu, la_bits=%d, ret=%d\n",
             osb->local_alloc_state, (unsigned long long)bits, la_bits, ret);
+       spin_unlock(&osb->osb_lock);
        return ret;
 }
 
@@ -120,14 +225,18 @@ int ocfs2_load_local_alloc(struct ocfs2_super *osb)
 
        mlog_entry_void();
 
-       if (osb->local_alloc_size == 0)
+       ocfs2_init_la_debug(osb);
+
+       if (osb->local_alloc_bits == 0)
                goto bail;
 
-       if (ocfs2_local_alloc_window_bits(osb) >= osb->bitmap_cpg) {
+       if (osb->local_alloc_bits >= osb->bitmap_cpg) {
                mlog(ML_NOTICE, "Requested local alloc window %d is larger "
                     "than max possible %u. Using defaults.\n",
-                    ocfs2_local_alloc_window_bits(osb), (osb->bitmap_cpg - 1));
-               osb->local_alloc_size = OCFS2_DEFAULT_LOCAL_ALLOC_SIZE;
+                    osb->local_alloc_bits, (osb->bitmap_cpg - 1));
+               osb->local_alloc_bits =
+                       ocfs2_megabytes_to_clusters(osb->sb,
+                                                   OCFS2_DEFAULT_LOCAL_ALLOC_SIZE);
        }
 
        /* read the alloc off disk */
@@ -139,8 +248,8 @@ int ocfs2_load_local_alloc(struct ocfs2_super *osb)
                goto bail;
        }
 
-       status = ocfs2_read_block(osb, OCFS2_I(inode)->ip_blkno,
-                                 &alloc_bh, 0, inode);
+       status = ocfs2_read_blocks(inode, OCFS2_I(inode)->ip_blkno, 1,
+                                  &alloc_bh, OCFS2_BH_IGNORE_CACHE);
        if (status < 0) {
                mlog_errno(status);
                goto bail;
@@ -185,13 +294,14 @@ int ocfs2_load_local_alloc(struct ocfs2_super *osb)
 
 bail:
        if (status < 0)
-               if (alloc_bh)
-                       brelse(alloc_bh);
+               brelse(alloc_bh);
        if (inode)
                iput(inode);
 
-       mlog(0, "Local alloc window bits = %d\n",
-            ocfs2_local_alloc_window_bits(osb));
+       if (status < 0)
+               ocfs2_shutdown_la_debug(osb);
+
+       mlog(0, "Local alloc window bits = %d\n", osb->local_alloc_bits);
 
        mlog_exit(status);
        return status;
@@ -217,6 +327,11 @@ void ocfs2_shutdown_local_alloc(struct ocfs2_super *osb)
 
        mlog_entry_void();
 
+       cancel_delayed_work(&osb->la_enable_wq);
+       flush_workqueue(ocfs2_wq);
+
+       ocfs2_shutdown_la_debug(osb);
+
        if (osb->local_alloc_state == OCFS2_LA_UNUSED)
                goto out;
 
@@ -295,8 +410,7 @@ out_commit:
        ocfs2_commit_trans(osb, handle);
 
 out_unlock:
-       if (main_bm_bh)
-               brelse(main_bm_bh);
+       brelse(main_bm_bh);
 
        ocfs2_inode_unlock(main_bm_inode, 1);
 
@@ -345,8 +459,8 @@ int ocfs2_begin_local_alloc_recovery(struct ocfs2_super *osb,
 
        mutex_lock(&inode->i_mutex);
 
-       status = ocfs2_read_block(osb, OCFS2_I(inode)->ip_blkno,
-                                 &alloc_bh, 0, inode);
+       status = ocfs2_read_blocks(inode, OCFS2_I(inode)->ip_blkno, 1,
+                                  &alloc_bh, OCFS2_BH_IGNORE_CACHE);
        if (status < 0) {
                mlog_errno(status);
                goto bail;
@@ -372,8 +486,7 @@ bail:
                *alloc_copy = NULL;
        }
 
-       if (alloc_bh)
-               brelse(alloc_bh);
+       brelse(alloc_bh);
 
        if (inode) {
                mutex_unlock(&inode->i_mutex);
@@ -441,8 +554,7 @@ out_unlock:
 out_mutex:
        mutex_unlock(&main_bm_inode->i_mutex);
 
-       if (main_bm_bh)
-               brelse(main_bm_bh);
+       brelse(main_bm_bh);
 
        iput(main_bm_inode);
 
@@ -453,8 +565,48 @@ out:
        return status;
 }
 
+/* Check to see if the local alloc window is within ac->ac_max_block */
+static int ocfs2_local_alloc_in_range(struct inode *inode,
+                                     struct ocfs2_alloc_context *ac,
+                                     u32 bits_wanted)
+{
+       struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
+       struct ocfs2_dinode *alloc;
+       struct ocfs2_local_alloc *la;
+       int start;
+       u64 block_off;
+
+       if (!ac->ac_max_block)
+               return 1;
+
+       alloc = (struct ocfs2_dinode *) osb->local_alloc_bh->b_data;
+       la = OCFS2_LOCAL_ALLOC(alloc);
+
+       start = ocfs2_local_alloc_find_clear_bits(osb, alloc, bits_wanted);
+       if (start == -1) {
+               mlog_errno(-ENOSPC);
+               return 0;
+       }
+
+       /*
+        * Converting (bm_off + start + bits_wanted) to blocks gives us
+        * the blkno just past our actual allocation.  This is perfect
+        * to compare with ac_max_block.
+        */
+       block_off = ocfs2_clusters_to_blocks(inode->i_sb,
+                                            le32_to_cpu(la->la_bm_off) +
+                                            start + bits_wanted);
+       mlog(0, "Checking %llu against %llu\n",
+            (unsigned long long)block_off,
+            (unsigned long long)ac->ac_max_block);
+       if (block_off > ac->ac_max_block)
+               return 0;
+
+       return 1;
+}
+
 /*
- * make sure we've got at least bitswanted contiguous bits in the
+ * make sure we've got at least bits_wanted contiguous bits in the
  * local alloc. You lose them when you drop i_mutex.
  *
  * We will add ourselves to the transaction passed in, but may start
@@ -485,16 +637,18 @@ int ocfs2_reserve_local_alloc_bits(struct ocfs2_super *osb,
 
        mutex_lock(&local_alloc_inode->i_mutex);
 
-       if (osb->local_alloc_state != OCFS2_LA_ENABLED) {
-               status = -ENOSPC;
-               goto bail;
-       }
-
-       if (bits_wanted > ocfs2_local_alloc_window_bits(osb)) {
-               mlog(0, "Asking for more than my max window size!\n");
+       /*
+        * We must double check state and allocator bits because
+        * another process may have changed them while holding i_mutex.
+        */
+       spin_lock(&osb->osb_lock);
+       if (!ocfs2_la_state_enabled(osb) ||
+           (bits_wanted > osb->local_alloc_bits)) {
+               spin_unlock(&osb->osb_lock);
                status = -ENOSPC;
                goto bail;
        }
+       spin_unlock(&osb->osb_lock);
 
        alloc = (struct ocfs2_dinode *) osb->local_alloc_bh->b_data;
 
@@ -522,6 +676,36 @@ int ocfs2_reserve_local_alloc_bits(struct ocfs2_super *osb,
                                mlog_errno(status);
                        goto bail;
                }
+
+               /*
+                * Under certain conditions, the window slide code
+                * might have reduced the number of bits available or
+                * disabled the the local alloc entirely. Re-check
+                * here and return -ENOSPC if necessary.
+                */
+               status = -ENOSPC;
+               if (!ocfs2_la_state_enabled(osb))
+                       goto bail;
+
+               free_bits = le32_to_cpu(alloc->id1.bitmap1.i_total) -
+                       le32_to_cpu(alloc->id1.bitmap1.i_used);
+               if (bits_wanted > free_bits)
+                       goto bail;
+       }
+
+       if (ac->ac_max_block)
+               mlog(0, "Calling in_range for max block %llu\n",
+                    (unsigned long long)ac->ac_max_block);
+
+       if (!ocfs2_local_alloc_in_range(local_alloc_inode, ac,
+                                       bits_wanted)) {
+               /*
+                * The window is outside ac->ac_max_block.
+                * This errno tells the caller to keep localalloc enabled
+                * but to get the allocation from the main bitmap.
+                */
+               status = -EFBIG;
+               goto bail;
        }
 
        ac->ac_inode = local_alloc_inode;
@@ -789,6 +973,85 @@ bail:
        return status;
 }
 
+enum ocfs2_la_event {
+       OCFS2_LA_EVENT_SLIDE,           /* Normal window slide. */
+       OCFS2_LA_EVENT_FRAGMENTED,      /* The global bitmap has
+                                        * enough bits theoretically
+                                        * free, but a contiguous
+                                        * allocation could not be
+                                        * found. */
+       OCFS2_LA_EVENT_ENOSPC,          /* Global bitmap doesn't have
+                                        * enough bits free to satisfy
+                                        * our request. */
+};
+#define OCFS2_LA_ENABLE_INTERVAL (30 * HZ)
+/*
+ * Given an event, calculate the size of our next local alloc window.
+ *
+ * This should always be called under i_mutex of the local alloc inode
+ * so that local alloc disabling doesn't race with processes trying to
+ * use the allocator.
+ *
+ * Returns the state which the local alloc was left in. This value can
+ * be ignored by some paths.
+ */
+static int ocfs2_recalc_la_window(struct ocfs2_super *osb,
+                                 enum ocfs2_la_event event)
+{
+       unsigned int bits;
+       int state;
+
+       spin_lock(&osb->osb_lock);
+       if (osb->local_alloc_state == OCFS2_LA_DISABLED) {
+               WARN_ON_ONCE(osb->local_alloc_state == OCFS2_LA_DISABLED);
+               goto out_unlock;
+       }
+
+       /*
+        * ENOSPC and fragmentation are treated similarly for now.
+        */
+       if (event == OCFS2_LA_EVENT_ENOSPC ||
+           event == OCFS2_LA_EVENT_FRAGMENTED) {
+               /*
+                * We ran out of contiguous space in the primary
+                * bitmap. Drastically reduce the number of bits used
+                * by local alloc until we have to disable it.
+                */
+               bits = osb->local_alloc_bits >> 1;
+               if (bits > ocfs2_megabytes_to_clusters(osb->sb, 1)) {
+                       /*
+                        * By setting state to THROTTLED, we'll keep
+                        * the number of local alloc bits used down
+                        * until an event occurs which would give us
+                        * reason to assume the bitmap situation might
+                        * have changed.
+                        */
+                       osb->local_alloc_state = OCFS2_LA_THROTTLED;
+                       osb->local_alloc_bits = bits;
+               } else {
+                       osb->local_alloc_state = OCFS2_LA_DISABLED;
+               }
+               queue_delayed_work(ocfs2_wq, &osb->la_enable_wq,
+                                  OCFS2_LA_ENABLE_INTERVAL);
+               goto out_unlock;
+       }
+
+       /*
+        * Don't increase the size of the local alloc window until we
+        * know we might be able to fulfill the request. Otherwise, we
+        * risk bouncing around the global bitmap during periods of
+        * low space.
+        */
+       if (osb->local_alloc_state != OCFS2_LA_THROTTLED)
+               osb->local_alloc_bits = osb->local_alloc_default_bits;
+
+out_unlock:
+       state = osb->local_alloc_state;
+       spin_unlock(&osb->osb_lock);
+
+       return state;
+}
+
 static int ocfs2_local_alloc_reserve_for_window(struct ocfs2_super *osb,
                                                struct ocfs2_alloc_context **ac,
                                                struct inode **bitmap_inode,
@@ -803,12 +1066,21 @@ static int ocfs2_local_alloc_reserve_for_window(struct ocfs2_super *osb,
                goto bail;
        }
 
-       (*ac)->ac_bits_wanted = ocfs2_local_alloc_window_bits(osb);
+retry_enospc:
+       (*ac)->ac_bits_wanted = osb->local_alloc_bits;
 
        status = ocfs2_reserve_cluster_bitmap_bits(osb, *ac);
+       if (status == -ENOSPC) {
+               if (ocfs2_recalc_la_window(osb, OCFS2_LA_EVENT_ENOSPC) ==
+                   OCFS2_LA_DISABLED)
+                       goto bail;
+
+               ocfs2_free_ac_resource(*ac);
+               memset(*ac, 0, sizeof(struct ocfs2_alloc_context));
+               goto retry_enospc;
+       }
        if (status < 0) {
-               if (status != -ENOSPC)
-                       mlog_errno(status);
+               mlog_errno(status);
                goto bail;
        }
 
@@ -849,7 +1121,7 @@ static int ocfs2_local_alloc_new_window(struct ocfs2_super *osb,
                     "one\n");
 
        mlog(0, "Allocating %u clusters for a new window.\n",
-            ocfs2_local_alloc_window_bits(osb));
+            osb->local_alloc_bits);
 
        /* Instruct the allocation code to try the most recently used
         * cluster group. We'll re-record the group used this pass
@@ -859,9 +1131,36 @@ static int ocfs2_local_alloc_new_window(struct ocfs2_super *osb,
        /* we used the generic suballoc reserve function, but we set
         * everything up nicely, so there's no reason why we can't use
         * the more specific cluster api to claim bits. */
-       status = ocfs2_claim_clusters(osb, handle, ac,
-                                     ocfs2_local_alloc_window_bits(osb),
+       status = ocfs2_claim_clusters(osb, handle, ac, osb->local_alloc_bits,
                                      &cluster_off, &cluster_count);
+       if (status == -ENOSPC) {
+retry_enospc:
+               /*
+                * Note: We could also try syncing the journal here to
+                * allow use of any free bits which the current
+                * transaction can't give us access to. --Mark
+                */
+               if (ocfs2_recalc_la_window(osb, OCFS2_LA_EVENT_FRAGMENTED) ==
+                   OCFS2_LA_DISABLED)
+                       goto bail;
+
+               status = ocfs2_claim_clusters(osb, handle, ac,
+                                             osb->local_alloc_bits,
+                                             &cluster_off,
+                                             &cluster_count);
+               if (status == -ENOSPC)
+                       goto retry_enospc;
+               /*
+                * We only shrunk the *minimum* number of in our
+                * request - it's entirely possible that the allocator
+                * might give us more than we asked for.
+                */
+               if (status == 0) {
+                       spin_lock(&osb->osb_lock);
+                       osb->local_alloc_bits = cluster_count;
+                       spin_unlock(&osb->osb_lock);
+               }
+       }
        if (status < 0) {
                if (status != -ENOSPC)
                        mlog_errno(status);
@@ -905,6 +1204,8 @@ static int ocfs2_local_alloc_slide_window(struct ocfs2_super *osb,
 
        mlog_entry_void();
 
+       ocfs2_recalc_la_window(osb, OCFS2_LA_EVENT_SLIDE);
+
        /* This will lock the main bitmap for us. */
        status = ocfs2_local_alloc_reserve_for_window(osb,
                                                      &ac,
@@ -976,8 +1277,7 @@ bail:
        if (handle)
                ocfs2_commit_trans(osb, handle);
 
-       if (main_bm_bh)
-               brelse(main_bm_bh);
+       brelse(main_bm_bh);
 
        if (main_bm_inode)
                iput(main_bm_inode);
index 3f76631e110c5a6284963cfe186b2c9a0917dabd..ac5ea9f8665342013335591dffc6177c8d193727 100644 (file)
@@ -52,4 +52,8 @@ int ocfs2_claim_local_alloc_bits(struct ocfs2_super *osb,
                                 u32 *bit_off,
                                 u32 *num_bits);
 
+void ocfs2_local_alloc_seen_free_bits(struct ocfs2_super *osb,
+                                     unsigned int num_clusters);
+void ocfs2_la_enable_worker(struct work_struct *work);
+
 #endif /* OCFS2_LOCALALLOC_H */
index 203f8714387799af46db9a3e6280cb0636a8c69b..544ac6245175f26c23249cf916c5001ae8f5d067 100644 (file)
@@ -24,6 +24,7 @@
  */
 
 #include <linux/fs.h>
+#include <linux/fcntl.h>
 
 #define MLOG_MASK_PREFIX ML_INODE
 #include <cluster/masklog.h>
@@ -32,6 +33,7 @@
 
 #include "dlmglue.h"
 #include "file.h"
+#include "inode.h"
 #include "locks.h"
 
 static int ocfs2_do_flock(struct file *file, struct inode *inode,
@@ -123,3 +125,16 @@ int ocfs2_flock(struct file *file, int cmd, struct file_lock *fl)
        else
                return ocfs2_do_flock(file, inode, cmd, fl);
 }
+
+int ocfs2_lock(struct file *file, int cmd, struct file_lock *fl)
+{
+       struct inode *inode = file->f_mapping->host;
+       struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
+
+       if (!(fl->fl_flags & FL_POSIX))
+               return -ENOLCK;
+       if (__mandatory_lock(inode))
+               return -ENOLCK;
+
+       return ocfs2_plock(osb->cconn, OCFS2_I(inode)->ip_blkno, file, cmd, fl);
+}
index 9743ef2324ec98c377473801784745681a4209b4..496d488b271f8ed9736c901582ba2da0bb7ad561 100644 (file)
@@ -27,5 +27,6 @@
 #define OCFS2_LOCKS_H
 
 int ocfs2_flock(struct file *file, int cmd, struct file_lock *fl);
+int ocfs2_lock(struct file *file, int cmd, struct file_lock *fl);
 
 #endif /* OCFS2_LOCKS_H */
index d5d808fe0140f7fb1e590c0255dd045542219bd1..485a6aa0ad39060e9eb27f2897be98026b886d4c 100644 (file)
@@ -60,6 +60,7 @@
 #include "symlink.h"
 #include "sysfile.h"
 #include "uptodate.h"
+#include "xattr.h"
 
 #include "buffer_head_io.h"
 
@@ -327,14 +328,9 @@ leave:
        if (status == -ENOSPC)
                mlog(0, "Disk is full\n");
 
-       if (new_fe_bh)
-               brelse(new_fe_bh);
-
-       if (de_bh)
-               brelse(de_bh);
-
-       if (parent_fe_bh)
-               brelse(parent_fe_bh);
+       brelse(new_fe_bh);
+       brelse(de_bh);
+       brelse(parent_fe_bh);
 
        if ((status < 0) && inode)
                iput(inode);
@@ -647,12 +643,9 @@ out_unlock_inode:
 out:
        ocfs2_inode_unlock(dir, 1);
 
-       if (de_bh)
-               brelse(de_bh);
-       if (fe_bh)
-               brelse(fe_bh);
-       if (parent_fe_bh)
-               brelse(parent_fe_bh);
+       brelse(de_bh);
+       brelse(fe_bh);
+       brelse(parent_fe_bh);
 
        mlog_exit(err);
 
@@ -851,17 +844,10 @@ leave:
                iput(orphan_dir);
        }
 
-       if (fe_bh)
-               brelse(fe_bh);
-
-       if (dirent_bh)
-               brelse(dirent_bh);
-
-       if (parent_node_bh)
-               brelse(parent_node_bh);
-
-       if (orphan_entry_bh)
-               brelse(orphan_entry_bh);
+       brelse(fe_bh);
+       brelse(dirent_bh);
+       brelse(parent_node_bh);
+       brelse(orphan_entry_bh);
 
        mlog_exit(status);
 
@@ -1372,24 +1358,15 @@ bail:
 
        if (new_inode)
                iput(new_inode);
-       if (newfe_bh)
-               brelse(newfe_bh);
-       if (old_inode_bh)
-               brelse(old_inode_bh);
-       if (old_dir_bh)
-               brelse(old_dir_bh);
-       if (new_dir_bh)
-               brelse(new_dir_bh);
-       if (new_de_bh)
-               brelse(new_de_bh);
-       if (old_de_bh)
-               brelse(old_de_bh);
-       if (old_inode_de_bh)
-               brelse(old_inode_de_bh);
-       if (orphan_entry_bh)
-               brelse(orphan_entry_bh);
-       if (insert_entry_bh)
-               brelse(insert_entry_bh);
+       brelse(newfe_bh);
+       brelse(old_inode_bh);
+       brelse(old_dir_bh);
+       brelse(new_dir_bh);
+       brelse(new_de_bh);
+       brelse(old_de_bh);
+       brelse(old_inode_de_bh);
+       brelse(orphan_entry_bh);
+       brelse(insert_entry_bh);
 
        mlog_exit(status);
 
@@ -1492,8 +1469,7 @@ bail:
 
        if (bhs) {
                for(i = 0; i < blocks; i++)
-                       if (bhs[i])
-                               brelse(bhs[i]);
+                       brelse(bhs[i]);
                kfree(bhs);
        }
 
@@ -1598,10 +1574,10 @@ static int ocfs2_symlink(struct inode *dir,
                u32 offset = 0;
 
                inode->i_op = &ocfs2_symlink_inode_operations;
-               status = ocfs2_do_extend_allocation(osb, inode, &offset, 1, 0,
-                                                   new_fe_bh,
-                                                   handle, data_ac, NULL,
-                                                   NULL);
+               status = ocfs2_add_inode_data(osb, inode, &offset, 1, 0,
+                                             new_fe_bh,
+                                             handle, data_ac, NULL,
+                                             NULL);
                if (status < 0) {
                        if (status != -ENOSPC && status != -EINTR) {
                                mlog(ML_ERROR,
@@ -1659,12 +1635,9 @@ bail:
 
        ocfs2_inode_unlock(dir, 1);
 
-       if (new_fe_bh)
-               brelse(new_fe_bh);
-       if (parent_fe_bh)
-               brelse(parent_fe_bh);
-       if (de_bh)
-               brelse(de_bh);
+       brelse(new_fe_bh);
+       brelse(parent_fe_bh);
+       brelse(de_bh);
        if (inode_ac)
                ocfs2_free_alloc_context(inode_ac);
        if (data_ac)
@@ -1759,8 +1732,7 @@ leave:
                iput(orphan_dir_inode);
        }
 
-       if (orphan_dir_bh)
-               brelse(orphan_dir_bh);
+       brelse(orphan_dir_bh);
 
        mlog_exit(status);
        return status;
@@ -1780,10 +1752,9 @@ static int ocfs2_orphan_add(struct ocfs2_super *osb,
 
        mlog_entry("(inode->i_ino = %lu)\n", inode->i_ino);
 
-       status = ocfs2_read_block(osb,
+       status = ocfs2_read_block(orphan_dir_inode,
                                  OCFS2_I(orphan_dir_inode)->ip_blkno,
-                                 &orphan_dir_bh, OCFS2_BH_CACHED,
-                                 orphan_dir_inode);
+                                 &orphan_dir_bh);
        if (status < 0) {
                mlog_errno(status);
                goto leave;
@@ -1829,8 +1800,7 @@ static int ocfs2_orphan_add(struct ocfs2_super *osb,
             (unsigned long long)OCFS2_I(inode)->ip_blkno, osb->slot_num);
 
 leave:
-       if (orphan_dir_bh)
-               brelse(orphan_dir_bh);
+       brelse(orphan_dir_bh);
 
        mlog_exit(status);
        return status;
@@ -1898,8 +1868,7 @@ int ocfs2_orphan_del(struct ocfs2_super *osb,
        }
 
 leave:
-       if (target_de_bh)
-               brelse(target_de_bh);
+       brelse(target_de_bh);
 
        mlog_exit(status);
        return status;
@@ -1918,4 +1887,8 @@ const struct inode_operations ocfs2_dir_iops = {
        .setattr        = ocfs2_setattr,
        .getattr        = ocfs2_getattr,
        .permission     = ocfs2_permission,
+       .setxattr       = generic_setxattr,
+       .getxattr       = generic_getxattr,
+       .listxattr      = ocfs2_listxattr,
+       .removexattr    = generic_removexattr,
 };
index 7f625f2b11174c4d0963f4d73971343da1ee04f6..a21a465490c40d0ee0ef3fc3d554d9f2196bdac2 100644 (file)
 #include <linux/workqueue.h>
 #include <linux/kref.h>
 #include <linux/mutex.h>
-#include <linux/jbd.h>
+#ifndef CONFIG_OCFS2_COMPAT_JBD
+# include <linux/jbd2.h>
+#else
+# include <linux/jbd.h>
+# include "ocfs2_jbd_compat.h"
+#endif
 
 /* For union ocfs2_dlm_lksb */
 #include "stackglue.h"
@@ -171,9 +176,13 @@ struct ocfs2_alloc_stats
 
 enum ocfs2_local_alloc_state
 {
-       OCFS2_LA_UNUSED = 0,
-       OCFS2_LA_ENABLED,
-       OCFS2_LA_DISABLED
+       OCFS2_LA_UNUSED = 0,    /* Local alloc will never be used for
+                                * this mountpoint. */
+       OCFS2_LA_ENABLED,       /* Local alloc is in use. */
+       OCFS2_LA_THROTTLED,     /* Local alloc is in use, but number
+                                * of bits has been reduced. */
+       OCFS2_LA_DISABLED       /* Local alloc has temporarily been
+                                * disabled. */
 };
 
 enum ocfs2_mount_options
@@ -184,6 +193,8 @@ enum ocfs2_mount_options
        OCFS2_MOUNT_ERRORS_PANIC = 1 << 3, /* Panic on errors */
        OCFS2_MOUNT_DATA_WRITEBACK = 1 << 4, /* No data ordering */
        OCFS2_MOUNT_LOCALFLOCKS = 1 << 5, /* No cluster aware user file locks */
+       OCFS2_MOUNT_NOUSERXATTR = 1 << 6, /* No user xattr */
+       OCFS2_MOUNT_INODE64 = 1 << 7,   /* Allow inode numbers > 2^32 */
 };
 
 #define OCFS2_OSB_SOFT_RO      0x0001
@@ -214,6 +225,7 @@ struct ocfs2_super
        u32 bitmap_cpg;
        u8 *uuid;
        char *uuid_str;
+       u32 uuid_hash;
        u8 *vol_label;
        u64 first_cluster_group_blkno;
        u32 fs_generation;
@@ -241,6 +253,7 @@ struct ocfs2_super
        int s_sectsize_bits;
        int s_clustersize;
        int s_clustersize_bits;
+       unsigned int s_xattr_inline_size;
 
        atomic_t vol_state;
        struct mutex recovery_lock;
@@ -252,11 +265,27 @@ struct ocfs2_super
        struct ocfs2_journal *journal;
        unsigned long osb_commit_interval;
 
-       int local_alloc_size;
-       enum ocfs2_local_alloc_state local_alloc_state;
+       struct delayed_work             la_enable_wq;
+
+       /*
+        * Must hold local alloc i_mutex and osb->osb_lock to change
+        * local_alloc_bits. Reads can be done under either lock.
+        */
+       unsigned int local_alloc_bits;
+       unsigned int local_alloc_default_bits;
+
+       enum ocfs2_local_alloc_state local_alloc_state; /* protected
+                                                        * by osb_lock */
+
        struct buffer_head *local_alloc_bh;
+
        u64 la_last_gd;
 
+#ifdef CONFIG_OCFS2_FS_STATS
+       struct dentry *local_alloc_debug;
+       char *local_alloc_debug_buf;
+#endif
+
        /* Next two fields are for local node slot recovery during
         * mount. */
        int dirty;
@@ -340,6 +369,13 @@ static inline int ocfs2_supports_inline_data(struct ocfs2_super *osb)
        return 0;
 }
 
+static inline int ocfs2_supports_xattr(struct ocfs2_super *osb)
+{
+       if (osb->s_feature_incompat & OCFS2_FEATURE_INCOMPAT_XATTR)
+               return 1;
+       return 0;
+}
+
 /* set / clear functions because cluster events can make these happen
  * in parallel so we want the transitions to be atomic. this also
  * means that any future flags osb_flags must be protected by spinlock
@@ -554,6 +590,14 @@ static inline unsigned int ocfs2_pages_per_cluster(struct super_block *sb)
        return pages_per_cluster;
 }
 
+static inline unsigned int ocfs2_megabytes_to_clusters(struct super_block *sb,
+                                                      unsigned int megs)
+{
+       BUILD_BUG_ON(OCFS2_MAX_CLUSTERSIZE > 1048576);
+
+       return megs << (20 - OCFS2_SB(sb)->s_clustersize_bits);
+}
+
 static inline void ocfs2_init_inode_steal_slot(struct ocfs2_super *osb)
 {
        spin_lock(&osb->osb_lock);
index 4f619850ccf7fd8b80bf4888fc39849c6bb93d4b..f24ce3d3f956056c2a1173dd8121076cea3eb079 100644 (file)
@@ -64,6 +64,7 @@
 #define OCFS2_INODE_SIGNATURE          "INODE01"
 #define OCFS2_EXTENT_BLOCK_SIGNATURE   "EXBLK01"
 #define OCFS2_GROUP_DESC_SIGNATURE      "GROUP01"
+#define OCFS2_XATTR_BLOCK_SIGNATURE    "XATTR01"
 
 /* Compatibility flags */
 #define OCFS2_HAS_COMPAT_FEATURE(sb,mask)                      \
@@ -90,7 +91,8 @@
                                         | OCFS2_FEATURE_INCOMPAT_SPARSE_ALLOC \
                                         | OCFS2_FEATURE_INCOMPAT_INLINE_DATA \
                                         | OCFS2_FEATURE_INCOMPAT_EXTENDED_SLOT_MAP \
-                                        | OCFS2_FEATURE_INCOMPAT_USERSPACE_STACK)
+                                        | OCFS2_FEATURE_INCOMPAT_USERSPACE_STACK \
+                                        | OCFS2_FEATURE_INCOMPAT_XATTR)
 #define OCFS2_FEATURE_RO_COMPAT_SUPP   OCFS2_FEATURE_RO_COMPAT_UNWRITTEN
 
 /*
 /* Support for data packed into inode blocks */
 #define OCFS2_FEATURE_INCOMPAT_INLINE_DATA     0x0040
 
-/* Support for the extended slot map */
-#define OCFS2_FEATURE_INCOMPAT_EXTENDED_SLOT_MAP 0x100
-
-
 /*
  * Support for alternate, userspace cluster stacks.  If set, the superblock
  * field s_cluster_info contains a tag for the alternate stack in use as
  */
 #define OCFS2_FEATURE_INCOMPAT_USERSPACE_STACK 0x0080
 
+/* Support for the extended slot map */
+#define OCFS2_FEATURE_INCOMPAT_EXTENDED_SLOT_MAP 0x100
+
+/* Support for extended attributes */
+#define OCFS2_FEATURE_INCOMPAT_XATTR           0x0200
+
 /*
  * backup superblock flag is used to indicate that this volume
  * has backup superblocks.
@@ -299,6 +303,12 @@ struct ocfs2_new_group_input {
  */
 #define OCFS2_DEFAULT_LOCAL_ALLOC_SIZE 8
 
+/*
+ * Inline extended attribute size (in bytes)
+ * The value chosen should be aligned to 16 byte boundaries.
+ */
+#define OCFS2_MIN_XATTR_INLINE_SIZE     256
+
 struct ocfs2_system_inode_info {
        char    *si_name;
        int     si_iflags;
@@ -563,7 +573,7 @@ struct ocfs2_super_block {
 /*40*/ __le16 s_max_slots;             /* Max number of simultaneous mounts
                                           before tunefs required */
        __le16 s_tunefs_flag;
-       __le32 s_reserved1;
+       __le32 s_uuid_hash;             /* hash value of uuid */
        __le64 s_first_cluster_group;   /* Block offset of 1st cluster
                                         * group header */
 /*50*/ __u8  s_label[OCFS2_MAX_VOL_LABEL_LEN]; /* Label for mounting, etc. */
@@ -571,7 +581,11 @@ struct ocfs2_super_block {
 /*A0*/  struct ocfs2_cluster_info s_cluster_info; /* Selected userspace
                                                     stack.  Only valid
                                                     with INCOMPAT flag. */
-/*B8*/  __le64 s_reserved2[17];                /* Fill out superblock */
+/*B8*/ __le16 s_xattr_inline_size;     /* extended attribute inline size
+                                          for this fs*/
+       __le16 s_reserved0;
+       __le32 s_reserved1;
+/*C0*/  __le64 s_reserved2[16];                /* Fill out superblock */
 /*140*/
 
        /*
@@ -621,7 +635,8 @@ struct ocfs2_dinode {
                                           belongs to */
        __le16 i_suballoc_bit;          /* Bit offset in suballocator
                                           block group */
-/*10*/ __le32 i_reserved0;
+/*10*/ __le16 i_reserved0;
+       __le16 i_xattr_inline_size;
        __le32 i_clusters;              /* Cluster count */
        __le32 i_uid;                   /* Owner UID */
        __le32 i_gid;                   /* Owning GID */
@@ -640,11 +655,12 @@ struct ocfs2_dinode {
        __le32 i_atime_nsec;
        __le32 i_ctime_nsec;
        __le32 i_mtime_nsec;
-       __le32 i_attr;
+/*70*/ __le32 i_attr;
        __le16 i_orphaned_slot;         /* Only valid when OCFS2_ORPHANED_FL
                                           was set in i_flags */
        __le16 i_dyn_features;
-/*70*/ __le64 i_reserved2[8];
+       __le64 i_xattr_loc;
+/*80*/ __le64 i_reserved2[7];
 /*B8*/ union {
                __le64 i_pad1;          /* Generic way to refer to this
                                           64bit union */
@@ -715,6 +731,136 @@ struct ocfs2_group_desc
 /*40*/ __u8    bg_bitmap[0];
 };
 
+/*
+ * On disk extended attribute structure for OCFS2.
+ */
+
+/*
+ * ocfs2_xattr_entry indicates one extend attribute.
+ *
+ * Note that it can be stored in inode, one block or one xattr bucket.
+ */
+struct ocfs2_xattr_entry {
+       __le32  xe_name_hash;    /* hash value of xattr prefix+suffix. */
+       __le16  xe_name_offset;  /* byte offset from the 1st etnry in the local
+                                   local xattr storage(inode, xattr block or
+                                   xattr bucket). */
+       __u8    xe_name_len;     /* xattr name len, does't include prefix. */
+       __u8    xe_type;         /* the low 7 bits indicates the name prefix's
+                                 * type and the highest 1 bits indicate whether
+                                 * the EA is stored in the local storage. */
+       __le64  xe_value_size;   /* real xattr value length. */
+};
+
+/*
+ * On disk structure for xattr header.
+ *
+ * One ocfs2_xattr_header describes how many ocfs2_xattr_entry records in
+ * the local xattr storage.
+ */
+struct ocfs2_xattr_header {
+       __le16  xh_count;                       /* contains the count of how
+                                                  many records are in the
+                                                  local xattr storage. */
+       __le16  xh_free_start;                  /* current offset for storing
+                                                  xattr. */
+       __le16  xh_name_value_len;              /* total length of name/value
+                                                  length in this bucket. */
+       __le16  xh_num_buckets;                 /* bucket nums in one extent
+                                                  record, only valid in the
+                                                  first bucket. */
+       __le64  xh_csum;
+       struct ocfs2_xattr_entry xh_entries[0]; /* xattr entry list. */
+};
+
+/*
+ * On disk structure for xattr value root.
+ *
+ * It is used when one extended attribute's size is larger, and we will save it
+ * in an outside cluster. It will stored in a b-tree like file content.
+ */
+struct ocfs2_xattr_value_root {
+/*00*/ __le32  xr_clusters;              /* clusters covered by xattr value. */
+       __le32  xr_reserved0;
+       __le64  xr_last_eb_blk;           /* Pointer to last extent block */
+/*10*/ struct ocfs2_extent_list xr_list; /* Extent record list */
+};
+
+/*
+ * On disk structure for xattr tree root.
+ *
+ * It is used when there are too many extended attributes for one file. These
+ * attributes will be organized and stored in an indexed-btree.
+ */
+struct ocfs2_xattr_tree_root {
+/*00*/ __le32  xt_clusters;              /* clusters covered by xattr. */
+       __le32  xt_reserved0;
+       __le64  xt_last_eb_blk;           /* Pointer to last extent block */
+/*10*/ struct ocfs2_extent_list xt_list; /* Extent record list */
+};
+
+#define OCFS2_XATTR_INDEXED    0x1
+#define OCFS2_HASH_SHIFT       5
+#define OCFS2_XATTR_ROUND      3
+#define OCFS2_XATTR_SIZE(size) (((size) + OCFS2_XATTR_ROUND) & \
+                               ~(OCFS2_XATTR_ROUND))
+
+#define OCFS2_XATTR_BUCKET_SIZE                        4096
+#define OCFS2_XATTR_MAX_BLOCKS_PER_BUCKET      (OCFS2_XATTR_BUCKET_SIZE \
+                                                / OCFS2_MIN_BLOCKSIZE)
+
+/*
+ * On disk structure for xattr block.
+ */
+struct ocfs2_xattr_block {
+/*00*/ __u8    xb_signature[8];     /* Signature for verification */
+       __le16  xb_suballoc_slot;    /* Slot suballocator this
+                                       block belongs to. */
+       __le16  xb_suballoc_bit;     /* Bit offset in suballocator
+                                       block group */
+       __le32  xb_fs_generation;    /* Must match super block */
+/*10*/ __le64  xb_blkno;            /* Offset on disk, in blocks */
+       __le64  xb_csum;
+/*20*/ __le16  xb_flags;            /* Indicates whether this block contains
+                                       real xattr or a xattr tree. */
+       __le16  xb_reserved0;
+       __le32  xb_reserved1;
+       __le64  xb_reserved2;
+/*30*/ union {
+               struct ocfs2_xattr_header xb_header; /* xattr header if this
+                                                       block contains xattr */
+               struct ocfs2_xattr_tree_root xb_root;/* xattr tree root if this
+                                                       block cotains xattr
+                                                       tree. */
+       } xb_attrs;
+};
+
+#define OCFS2_XATTR_ENTRY_LOCAL                0x80
+#define OCFS2_XATTR_TYPE_MASK          0x7F
+static inline void ocfs2_xattr_set_local(struct ocfs2_xattr_entry *xe,
+                                        int local)
+{
+       if (local)
+               xe->xe_type |= OCFS2_XATTR_ENTRY_LOCAL;
+       else
+               xe->xe_type &= ~OCFS2_XATTR_ENTRY_LOCAL;
+}
+
+static inline int ocfs2_xattr_is_local(struct ocfs2_xattr_entry *xe)
+{
+       return xe->xe_type & OCFS2_XATTR_ENTRY_LOCAL;
+}
+
+static inline void ocfs2_xattr_set_type(struct ocfs2_xattr_entry *xe, int type)
+{
+       xe->xe_type |= type & OCFS2_XATTR_TYPE_MASK;
+}
+
+static inline int ocfs2_xattr_get_type(struct ocfs2_xattr_entry *xe)
+{
+       return xe->xe_type & OCFS2_XATTR_TYPE_MASK;
+}
+
 #ifdef __KERNEL__
 static inline int ocfs2_fast_symlink_chars(struct super_block *sb)
 {
@@ -728,6 +874,20 @@ static inline int ocfs2_max_inline_data(struct super_block *sb)
                offsetof(struct ocfs2_dinode, id2.i_data.id_data);
 }
 
+static inline int ocfs2_max_inline_data_with_xattr(struct super_block *sb,
+                                                  struct ocfs2_dinode *di)
+{
+       unsigned int xattrsize = le16_to_cpu(di->i_xattr_inline_size);
+
+       if (le16_to_cpu(di->i_dyn_features) & OCFS2_INLINE_XATTR_FL)
+               return sb->s_blocksize -
+                       offsetof(struct ocfs2_dinode, id2.i_data.id_data) -
+                       xattrsize;
+       else
+               return sb->s_blocksize -
+                       offsetof(struct ocfs2_dinode, id2.i_data.id_data);
+}
+
 static inline int ocfs2_extent_recs_per_inode(struct super_block *sb)
 {
        int size;
@@ -738,6 +898,24 @@ static inline int ocfs2_extent_recs_per_inode(struct super_block *sb)
        return size / sizeof(struct ocfs2_extent_rec);
 }
 
+static inline int ocfs2_extent_recs_per_inode_with_xattr(
+                                               struct super_block *sb,
+                                               struct ocfs2_dinode *di)
+{
+       int size;
+       unsigned int xattrsize = le16_to_cpu(di->i_xattr_inline_size);
+
+       if (le16_to_cpu(di->i_dyn_features) & OCFS2_INLINE_XATTR_FL)
+               size = sb->s_blocksize -
+                       offsetof(struct ocfs2_dinode, id2.i_list.l_recs) -
+                       xattrsize;
+       else
+               size = sb->s_blocksize -
+                       offsetof(struct ocfs2_dinode, id2.i_list.l_recs);
+
+       return size / sizeof(struct ocfs2_extent_rec);
+}
+
 static inline int ocfs2_chain_recs_per_inode(struct super_block *sb)
 {
        int size;
@@ -801,6 +979,17 @@ static inline u64 ocfs2_backup_super_blkno(struct super_block *sb, int index)
        return 0;
 
 }
+
+static inline u16 ocfs2_xattr_recs_per_xb(struct super_block *sb)
+{
+       int size;
+
+       size = sb->s_blocksize -
+               offsetof(struct ocfs2_xattr_block,
+                        xb_attrs.xb_root.xt_list.l_recs);
+
+       return size / sizeof(struct ocfs2_extent_rec);
+}
 #else
 static inline int ocfs2_fast_symlink_chars(int blocksize)
 {
@@ -884,6 +1073,17 @@ static inline uint64_t ocfs2_backup_super_blkno(int blocksize, int index)
 
        return 0;
 }
+
+static inline int ocfs2_xattr_recs_per_xb(int blocksize)
+{
+       int size;
+
+       size = blocksize -
+               offsetof(struct ocfs2_xattr_block,
+                        xb_attrs.xb_root.xt_list.l_recs);
+
+       return size / sizeof(struct ocfs2_extent_rec);
+}
 #endif  /* __KERNEL__ */
 
 
diff --git a/fs/ocfs2/ocfs2_jbd_compat.h b/fs/ocfs2/ocfs2_jbd_compat.h
new file mode 100644 (file)
index 0000000..b91c78f
--- /dev/null
@@ -0,0 +1,82 @@
+/* -*- mode: c; c-basic-offset: 8; -*-
+ * vim: noexpandtab sw=8 ts=8 sts=0:
+ *
+ * ocfs2_jbd_compat.h
+ *
+ * Compatibility defines for JBD.
+ *
+ * Copyright (C) 2008 Oracle.  All rights reserved.
+ *
+ * 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.
+ */
+
+#ifndef OCFS2_JBD_COMPAT_H
+#define OCFS2_JBD_COMPAT_H
+
+#ifndef CONFIG_OCFS2_COMPAT_JBD
+# error Should not have been included
+#endif
+
+struct jbd2_inode {
+       unsigned int dummy;
+};
+
+#define JBD2_BARRIER                   JFS_BARRIER
+#define JBD2_DEFAULT_MAX_COMMIT_AGE    JBD_DEFAULT_MAX_COMMIT_AGE
+
+#define jbd2_journal_ack_err                   journal_ack_err
+#define jbd2_journal_clear_err                 journal_clear_err
+#define jbd2_journal_destroy                   journal_destroy
+#define jbd2_journal_dirty_metadata            journal_dirty_metadata
+#define jbd2_journal_errno                     journal_errno
+#define jbd2_journal_extend                    journal_extend
+#define jbd2_journal_flush                     journal_flush
+#define jbd2_journal_force_commit              journal_force_commit
+#define jbd2_journal_get_write_access          journal_get_write_access
+#define jbd2_journal_get_undo_access           journal_get_undo_access
+#define jbd2_journal_init_inode                        journal_init_inode
+#define jbd2_journal_invalidatepage            journal_invalidatepage
+#define jbd2_journal_load                      journal_load
+#define jbd2_journal_lock_updates              journal_lock_updates
+#define jbd2_journal_restart                   journal_restart
+#define jbd2_journal_start                     journal_start
+#define jbd2_journal_start_commit              journal_start_commit
+#define jbd2_journal_stop                      journal_stop
+#define jbd2_journal_try_to_free_buffers       journal_try_to_free_buffers
+#define jbd2_journal_unlock_updates            journal_unlock_updates
+#define jbd2_journal_wipe                      journal_wipe
+#define jbd2_log_wait_commit                   log_wait_commit
+
+static inline int jbd2_journal_file_inode(handle_t *handle,
+                                         struct jbd2_inode *inode)
+{
+       return 0;
+}
+
+static inline int jbd2_journal_begin_ordered_truncate(struct jbd2_inode *inode,
+                                                     loff_t new_size)
+{
+       return 0;
+}
+
+static inline void jbd2_journal_init_jbd_inode(struct jbd2_inode *jinode,
+                                              struct inode *inode)
+{
+       return;
+}
+
+static inline void jbd2_journal_release_jbd_inode(journal_t *journal,
+                                                 struct jbd2_inode *jinode)
+{
+       return;
+}
+
+
+#endif  /* OCFS2_JBD_COMPAT_H */
index 8166968e9015d8634f1987cd8af84f07595c86b9..ffd48db229a75323d31b250e7b5fd885fc7eff63 100644 (file)
@@ -200,7 +200,7 @@ static int update_backups(struct inode * inode, u32 clusters, char *data)
                if (cluster > clusters)
                        break;
 
-               ret = ocfs2_read_block(osb, blkno, &backup, 0, NULL);
+               ret = ocfs2_read_blocks_sync(osb, blkno, 1, &backup);
                if (ret < 0) {
                        mlog_errno(ret);
                        break;
@@ -236,8 +236,8 @@ static void ocfs2_update_super_and_backups(struct inode *inode,
         * update the superblock last.
         * It doesn't matter if the write failed.
         */
-       ret = ocfs2_read_block(osb, OCFS2_SUPER_BLOCK_BLKNO,
-                              &super_bh, 0, NULL);
+       ret = ocfs2_read_blocks_sync(osb, OCFS2_SUPER_BLOCK_BLKNO, 1,
+                                    &super_bh);
        if (ret < 0) {
                mlog_errno(ret);
                goto out;
@@ -332,8 +332,7 @@ int ocfs2_group_extend(struct inode * inode, int new_clusters)
        lgd_blkno = ocfs2_which_cluster_group(main_bm_inode,
                                              first_new_cluster - 1);
 
-       ret = ocfs2_read_block(osb, lgd_blkno, &group_bh, OCFS2_BH_CACHED,
-                              main_bm_inode);
+       ret = ocfs2_read_block(main_bm_inode, lgd_blkno, &group_bh);
        if (ret < 0) {
                mlog_errno(ret);
                goto out_unlock;
@@ -540,7 +539,7 @@ int ocfs2_group_add(struct inode *inode, struct ocfs2_new_group_input *input)
                goto out_unlock;
        }
 
-       ret = ocfs2_read_block(osb, input->group, &group_bh, 0, NULL);
+       ret = ocfs2_read_blocks_sync(osb, input->group, 1, &group_bh);
        if (ret < 0) {
                mlog(ML_ERROR, "Can't read the group descriptor # %llu "
                     "from the device.", (unsigned long long)input->group);
index bb5ff8939bf1ef28a76dfbda74715ea05a4828f7..bdda2d8f85080a5a883c1b01b4cc1fbe68d83d18 100644 (file)
@@ -150,8 +150,8 @@ int ocfs2_refresh_slot_info(struct ocfs2_super *osb)
         * be !NULL.  Thus, ocfs2_read_blocks() will ignore blocknr.  If
         * this is not true, the read of -1 (UINT64_MAX) will fail.
         */
-       ret = ocfs2_read_blocks(osb, -1, si->si_blocks, si->si_bh, 0,
-                               si->si_inode);
+       ret = ocfs2_read_blocks(si->si_inode, -1, si->si_blocks, si->si_bh,
+                               OCFS2_BH_IGNORE_CACHE);
        if (ret == 0) {
                spin_lock(&osb->osb_lock);
                ocfs2_update_slot_info(si);
@@ -404,7 +404,8 @@ static int ocfs2_map_slot_buffers(struct ocfs2_super *osb,
                     (unsigned long long)blkno);
 
                bh = NULL;  /* Acquire a fresh bh */
-               status = ocfs2_read_block(osb, blkno, &bh, 0, si->si_inode);
+               status = ocfs2_read_blocks(si->si_inode, blkno, 1, &bh,
+                                          OCFS2_BH_IGNORE_CACHE);
                if (status < 0) {
                        mlog_errno(status);
                        goto bail;
index 353fc35c6748db228172b8476c42ecac8027261c..faec2d879357dbc62f4d85124e2840d26d439be3 100644 (file)
@@ -28,6 +28,7 @@
 #include "ocfs2.h"  /* For struct ocfs2_lock_res */
 #include "stackglue.h"
 
+#include <linux/dlm_plock.h>
 
 /*
  * The control protocol starts with a handshake.  Until the handshake
@@ -746,6 +747,37 @@ static void user_dlm_dump_lksb(union ocfs2_dlm_lksb *lksb)
 {
 }
 
+static int user_plock(struct ocfs2_cluster_connection *conn,
+                     u64 ino,
+                     struct file *file,
+                     int cmd,
+                     struct file_lock *fl)
+{
+       /*
+        * This more or less just demuxes the plock request into any
+        * one of three dlm calls.
+        *
+        * Internally, fs/dlm will pass these to a misc device, which
+        * a userspace daemon will read and write to.
+        *
+        * For now, cancel requests (which happen internally only),
+        * are turned into unlocks. Most of this function taken from
+        * gfs2_lock.
+        */
+
+       if (cmd == F_CANCELLK) {
+               cmd = F_SETLK;
+               fl->fl_type = F_UNLCK;
+       }
+
+       if (IS_GETLK(cmd))
+               return dlm_posix_get(conn->cc_lockspace, ino, file, fl);
+       else if (fl->fl_type == F_UNLCK)
+               return dlm_posix_unlock(conn->cc_lockspace, ino, file, fl);
+       else
+               return dlm_posix_lock(conn->cc_lockspace, ino, file, cmd, fl);
+}
+
 /*
  * Compare a requested locking protocol version against the current one.
  *
@@ -839,6 +871,7 @@ static struct ocfs2_stack_operations ocfs2_user_plugin_ops = {
        .dlm_unlock     = user_dlm_unlock,
        .lock_status    = user_dlm_lock_status,
        .lock_lvb       = user_dlm_lvb,
+       .plock          = user_plock,
        .dump_lksb      = user_dlm_dump_lksb,
 };
 
index 07f348b8d721064a3753012dc22c7ab21a834d55..68b668b0e60a0a231436fcde4e41bd23083a032d 100644 (file)
@@ -288,6 +288,26 @@ void ocfs2_dlm_dump_lksb(union ocfs2_dlm_lksb *lksb)
 }
 EXPORT_SYMBOL_GPL(ocfs2_dlm_dump_lksb);
 
+int ocfs2_stack_supports_plocks(void)
+{
+       return active_stack && active_stack->sp_ops->plock;
+}
+EXPORT_SYMBOL_GPL(ocfs2_stack_supports_plocks);
+
+/*
+ * ocfs2_plock() can only be safely called if
+ * ocfs2_stack_supports_plocks() returned true
+ */
+int ocfs2_plock(struct ocfs2_cluster_connection *conn, u64 ino,
+               struct file *file, int cmd, struct file_lock *fl)
+{
+       WARN_ON_ONCE(active_stack->sp_ops->plock == NULL);
+       if (active_stack->sp_ops->plock)
+               return active_stack->sp_ops->plock(conn, ino, file, cmd, fl);
+       return -EOPNOTSUPP;
+}
+EXPORT_SYMBOL_GPL(ocfs2_plock);
+
 int ocfs2_cluster_connect(const char *stack_name,
                          const char *group,
                          int grouplen,
index db56281dd1be48d5282ce1aaf0c2b1472c1d8f88..c571af375ef87b98e6e24aad5d44c6fd7e114163 100644 (file)
 #include "dlm/dlmapi.h"
 #include <linux/dlm.h>
 
+/* Needed for plock-related prototypes */
+struct file;
+struct file_lock;
+
 /*
  * dlmconstants.h does not have a LOCAL flag.  We hope to remove it
  * some day, but right now we need it.  Let's fake it.  This value is larger
@@ -186,6 +190,17 @@ struct ocfs2_stack_operations {
         */
        void *(*lock_lvb)(union ocfs2_dlm_lksb *lksb);
 
+       /*
+        * Cluster-aware posix locks
+        *
+        * This is NULL for stacks which do not support posix locks.
+        */
+       int (*plock)(struct ocfs2_cluster_connection *conn,
+                    u64 ino,
+                    struct file *file,
+                    int cmd,
+                    struct file_lock *fl);
+
        /*
         * This is an optoinal debugging hook.  If provided, the
         * stack can dump debugging information about this lock.
@@ -240,6 +255,10 @@ int ocfs2_dlm_lock_status(union ocfs2_dlm_lksb *lksb);
 void *ocfs2_dlm_lvb(union ocfs2_dlm_lksb *lksb);
 void ocfs2_dlm_dump_lksb(union ocfs2_dlm_lksb *lksb);
 
+int ocfs2_stack_supports_plocks(void);
+int ocfs2_plock(struct ocfs2_cluster_connection *conn, u64 ino,
+               struct file *file, int cmd, struct file_lock *fl);
+
 void ocfs2_stack_glue_set_locking_protocol(struct ocfs2_locking_protocol *proto);
 
 
index d2d278fb9819ee3da92577a367b79f0bdc18681f..c5ff18b46b57524884d9c4b7639f8089e7d3c532 100644 (file)
@@ -62,15 +62,18 @@ static int ocfs2_block_group_fill(handle_t *handle,
                                  struct ocfs2_chain_list *cl);
 static int ocfs2_block_group_alloc(struct ocfs2_super *osb,
                                   struct inode *alloc_inode,
-                                  struct buffer_head *bh);
+                                  struct buffer_head *bh,
+                                  u64 max_block);
 
 static int ocfs2_cluster_group_search(struct inode *inode,
                                      struct buffer_head *group_bh,
                                      u32 bits_wanted, u32 min_bits,
+                                     u64 max_block,
                                      u16 *bit_off, u16 *bits_found);
 static int ocfs2_block_group_search(struct inode *inode,
                                    struct buffer_head *group_bh,
                                    u32 bits_wanted, u32 min_bits,
+                                   u64 max_block,
                                    u16 *bit_off, u16 *bits_found);
 static int ocfs2_claim_suballoc_bits(struct ocfs2_super *osb,
                                     struct ocfs2_alloc_context *ac,
@@ -110,8 +113,11 @@ static inline void ocfs2_block_to_cluster_group(struct inode *inode,
                                                u64 data_blkno,
                                                u64 *bg_blkno,
                                                u16 *bg_bit_off);
+static int ocfs2_reserve_clusters_with_limit(struct ocfs2_super *osb,
+                                            u32 bits_wanted, u64 max_block,
+                                            struct ocfs2_alloc_context **ac);
 
-static void ocfs2_free_ac_resource(struct ocfs2_alloc_context *ac)
+void ocfs2_free_ac_resource(struct ocfs2_alloc_context *ac)
 {
        struct inode *inode = ac->ac_inode;
 
@@ -124,10 +130,8 @@ static void ocfs2_free_ac_resource(struct ocfs2_alloc_context *ac)
                iput(inode);
                ac->ac_inode = NULL;
        }
-       if (ac->ac_bh) {
-               brelse(ac->ac_bh);
-               ac->ac_bh = NULL;
-       }
+       brelse(ac->ac_bh);
+       ac->ac_bh = NULL;
 }
 
 void ocfs2_free_alloc_context(struct ocfs2_alloc_context *ac)
@@ -276,7 +280,8 @@ static inline u16 ocfs2_find_smallest_chain(struct ocfs2_chain_list *cl)
  */
 static int ocfs2_block_group_alloc(struct ocfs2_super *osb,
                                   struct inode *alloc_inode,
-                                  struct buffer_head *bh)
+                                  struct buffer_head *bh,
+                                  u64 max_block)
 {
        int status, credits;
        struct ocfs2_dinode *fe = (struct ocfs2_dinode *) bh->b_data;
@@ -294,9 +299,9 @@ static int ocfs2_block_group_alloc(struct ocfs2_super *osb,
        mlog_entry_void();
 
        cl = &fe->id2.i_chain;
-       status = ocfs2_reserve_clusters(osb,
-                                       le16_to_cpu(cl->cl_cpg),
-                                       &ac);
+       status = ocfs2_reserve_clusters_with_limit(osb,
+                                                  le16_to_cpu(cl->cl_cpg),
+                                                  max_block, &ac);
        if (status < 0) {
                if (status != -ENOSPC)
                        mlog_errno(status);
@@ -394,8 +399,7 @@ bail:
        if (ac)
                ocfs2_free_alloc_context(ac);
 
-       if (bg_bh)
-               brelse(bg_bh);
+       brelse(bg_bh);
 
        mlog_exit(status);
        return status;
@@ -469,7 +473,8 @@ static int ocfs2_reserve_suballoc_bits(struct ocfs2_super *osb,
                        goto bail;
                }
 
-               status = ocfs2_block_group_alloc(osb, alloc_inode, bh);
+               status = ocfs2_block_group_alloc(osb, alloc_inode, bh,
+                                                ac->ac_max_block);
                if (status < 0) {
                        if (status != -ENOSPC)
                                mlog_errno(status);
@@ -486,16 +491,15 @@ static int ocfs2_reserve_suballoc_bits(struct ocfs2_super *osb,
        get_bh(bh);
        ac->ac_bh = bh;
 bail:
-       if (bh)
-               brelse(bh);
+       brelse(bh);
 
        mlog_exit(status);
        return status;
 }
 
-int ocfs2_reserve_new_metadata(struct ocfs2_super *osb,
-                              struct ocfs2_dinode *fe,
-                              struct ocfs2_alloc_context **ac)
+int ocfs2_reserve_new_metadata_blocks(struct ocfs2_super *osb,
+                                     int blocks,
+                                     struct ocfs2_alloc_context **ac)
 {
        int status;
        u32 slot;
@@ -507,7 +511,7 @@ int ocfs2_reserve_new_metadata(struct ocfs2_super *osb,
                goto bail;
        }
 
-       (*ac)->ac_bits_wanted = ocfs2_extend_meta_needed(fe);
+       (*ac)->ac_bits_wanted = blocks;
        (*ac)->ac_which = OCFS2_AC_USE_META;
        slot = osb->slot_num;
        (*ac)->ac_group_search = ocfs2_block_group_search;
@@ -532,6 +536,15 @@ bail:
        return status;
 }
 
+int ocfs2_reserve_new_metadata(struct ocfs2_super *osb,
+                              struct ocfs2_extent_list *root_el,
+                              struct ocfs2_alloc_context **ac)
+{
+       return ocfs2_reserve_new_metadata_blocks(osb,
+                                       ocfs2_extend_meta_needed(root_el),
+                                       ac);
+}
+
 static int ocfs2_steal_inode_from_other_nodes(struct ocfs2_super *osb,
                                              struct ocfs2_alloc_context *ac)
 {
@@ -581,6 +594,14 @@ int ocfs2_reserve_new_inode(struct ocfs2_super *osb,
 
        (*ac)->ac_group_search = ocfs2_block_group_search;
 
+       /*
+        * stat(2) can't handle i_ino > 32bits, so we tell the
+        * lower levels not to allocate us a block group past that
+        * limit.  The 'inode64' mount option avoids this behavior.
+        */
+       if (!(osb->s_mount_opt & OCFS2_MOUNT_INODE64))
+               (*ac)->ac_max_block = (u32)~0U;
+
        /*
         * slot is set when we successfully steal inode from other nodes.
         * It is reset in 3 places:
@@ -661,9 +682,9 @@ bail:
 /* Callers don't need to care which bitmap (local alloc or main) to
  * use so we figure it out for them, but unfortunately this clutters
  * things a bit. */
-int ocfs2_reserve_clusters(struct ocfs2_super *osb,
-                          u32 bits_wanted,
-                          struct ocfs2_alloc_context **ac)
+static int ocfs2_reserve_clusters_with_limit(struct ocfs2_super *osb,
+                                            u32 bits_wanted, u64 max_block,
+                                            struct ocfs2_alloc_context **ac)
 {
        int status;
 
@@ -677,24 +698,20 @@ int ocfs2_reserve_clusters(struct ocfs2_super *osb,
        }
 
        (*ac)->ac_bits_wanted = bits_wanted;
+       (*ac)->ac_max_block = max_block;
 
        status = -ENOSPC;
        if (ocfs2_alloc_should_use_local(osb, bits_wanted)) {
                status = ocfs2_reserve_local_alloc_bits(osb,
                                                        bits_wanted,
                                                        *ac);
-               if ((status < 0) && (status != -ENOSPC)) {
+               if (status == -EFBIG) {
+                       /* The local alloc window is outside ac_max_block.
+                        * use the main bitmap. */
+                       status = -ENOSPC;
+               } else if ((status < 0) && (status != -ENOSPC)) {
                        mlog_errno(status);
                        goto bail;
-               } else if (status == -ENOSPC) {
-                       /* reserve_local_bits will return enospc with
-                        * the local alloc inode still locked, so we
-                        * can change this safely here. */
-                       mlog(0, "Disabling local alloc\n");
-                       /* We set to OCFS2_LA_DISABLED so that umount
-                        * can clean up what's left of the local
-                        * allocation */
-                       osb->local_alloc_state = OCFS2_LA_DISABLED;
                }
        }
 
@@ -718,6 +735,13 @@ bail:
        return status;
 }
 
+int ocfs2_reserve_clusters(struct ocfs2_super *osb,
+                          u32 bits_wanted,
+                          struct ocfs2_alloc_context **ac)
+{
+       return ocfs2_reserve_clusters_with_limit(osb, bits_wanted, 0, ac);
+}
+
 /*
  * More or less lifted from ext3. I'll leave their description below:
  *
@@ -1000,11 +1024,14 @@ static inline int ocfs2_block_group_reasonably_empty(struct ocfs2_group_desc *bg
 static int ocfs2_cluster_group_search(struct inode *inode,
                                      struct buffer_head *group_bh,
                                      u32 bits_wanted, u32 min_bits,
+                                     u64 max_block,
                                      u16 *bit_off, u16 *bits_found)
 {
        int search = -ENOSPC;
        int ret;
+       u64 blkoff;
        struct ocfs2_group_desc *gd = (struct ocfs2_group_desc *) group_bh->b_data;
+       struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
        u16 tmp_off, tmp_found;
        unsigned int max_bits, gd_cluster_off;
 
@@ -1037,6 +1064,17 @@ static int ocfs2_cluster_group_search(struct inode *inode,
                if (ret)
                        return ret;
 
+               if (max_block) {
+                       blkoff = ocfs2_clusters_to_blocks(inode->i_sb,
+                                                         gd_cluster_off +
+                                                         tmp_off + tmp_found);
+                       mlog(0, "Checking %llu against %llu\n",
+                            (unsigned long long)blkoff,
+                            (unsigned long long)max_block);
+                       if (blkoff > max_block)
+                               return -ENOSPC;
+               }
+
                /* ocfs2_block_group_find_clear_bits() might
                 * return success, but we still want to return
                 * -ENOSPC unless it found the minimum number
@@ -1045,6 +1083,12 @@ static int ocfs2_cluster_group_search(struct inode *inode,
                        *bit_off = tmp_off;
                        *bits_found = tmp_found;
                        search = 0; /* success */
+               } else if (tmp_found) {
+                       /*
+                        * Don't show bits which we'll be returning
+                        * for allocation to the local alloc bitmap.
+                        */
+                       ocfs2_local_alloc_seen_free_bits(osb, tmp_found);
                }
        }
 
@@ -1054,19 +1098,31 @@ static int ocfs2_cluster_group_search(struct inode *inode,
 static int ocfs2_block_group_search(struct inode *inode,
                                    struct buffer_head *group_bh,
                                    u32 bits_wanted, u32 min_bits,
+                                   u64 max_block,
                                    u16 *bit_off, u16 *bits_found)
 {
        int ret = -ENOSPC;
+       u64 blkoff;
        struct ocfs2_group_desc *bg = (struct ocfs2_group_desc *) group_bh->b_data;
 
        BUG_ON(min_bits != 1);
        BUG_ON(ocfs2_is_cluster_bitmap(inode));
 
-       if (bg->bg_free_bits_count)
+       if (bg->bg_free_bits_count) {
                ret = ocfs2_block_group_find_clear_bits(OCFS2_SB(inode->i_sb),
                                                        group_bh, bits_wanted,
                                                        le16_to_cpu(bg->bg_bits),
                                                        bit_off, bits_found);
+               if (!ret && max_block) {
+                       blkoff = le64_to_cpu(bg->bg_blkno) + *bit_off +
+                               *bits_found;
+                       mlog(0, "Checking %llu against %llu\n",
+                            (unsigned long long)blkoff,
+                            (unsigned long long)max_block);
+                       if (blkoff > max_block)
+                               ret = -ENOSPC;
+               }
+       }
 
        return ret;
 }
@@ -1116,8 +1172,7 @@ static int ocfs2_search_one_group(struct ocfs2_alloc_context *ac,
        struct ocfs2_group_desc *gd;
        struct inode *alloc_inode = ac->ac_inode;
 
-       ret = ocfs2_read_block(OCFS2_SB(alloc_inode->i_sb), gd_blkno,
-                              &group_bh, OCFS2_BH_CACHED, alloc_inode);
+       ret = ocfs2_read_block(alloc_inode, gd_blkno, &group_bh);
        if (ret < 0) {
                mlog_errno(ret);
                return ret;
@@ -1131,7 +1186,7 @@ static int ocfs2_search_one_group(struct ocfs2_alloc_context *ac,
        }
 
        ret = ac->ac_group_search(alloc_inode, group_bh, bits_wanted, min_bits,
-                                 bit_off, &found);
+                                 ac->ac_max_block, bit_off, &found);
        if (ret < 0) {
                if (ret != -ENOSPC)
                        mlog_errno(ret);
@@ -1186,9 +1241,9 @@ static int ocfs2_search_chain(struct ocfs2_alloc_context *ac,
             bits_wanted, chain,
             (unsigned long long)OCFS2_I(alloc_inode)->ip_blkno);
 
-       status = ocfs2_read_block(OCFS2_SB(alloc_inode->i_sb),
+       status = ocfs2_read_block(alloc_inode,
                                  le64_to_cpu(cl->cl_recs[chain].c_blkno),
-                                 &group_bh, OCFS2_BH_CACHED, alloc_inode);
+                                 &group_bh);
        if (status < 0) {
                mlog_errno(status);
                goto bail;
@@ -1204,21 +1259,20 @@ static int ocfs2_search_chain(struct ocfs2_alloc_context *ac,
        /* for now, the chain search is a bit simplistic. We just use
         * the 1st group with any empty bits. */
        while ((status = ac->ac_group_search(alloc_inode, group_bh,
-                                            bits_wanted, min_bits, bit_off,
+                                            bits_wanted, min_bits,
+                                            ac->ac_max_block, bit_off,
                                             &tmp_bits)) == -ENOSPC) {
                if (!bg->bg_next_group)
                        break;
 
-               if (prev_group_bh) {
-                       brelse(prev_group_bh);
-                       prev_group_bh = NULL;
-               }
+               brelse(prev_group_bh);
+               prev_group_bh = NULL;
+
                next_group = le64_to_cpu(bg->bg_next_group);
                prev_group_bh = group_bh;
                group_bh = NULL;
-               status = ocfs2_read_block(OCFS2_SB(alloc_inode->i_sb),
-                                         next_group, &group_bh,
-                                         OCFS2_BH_CACHED, alloc_inode);
+               status = ocfs2_read_block(alloc_inode,
+                                         next_group, &group_bh);
                if (status < 0) {
                        mlog_errno(status);
                        goto bail;
@@ -1307,10 +1361,8 @@ static int ocfs2_search_chain(struct ocfs2_alloc_context *ac,
        *bg_blkno = le64_to_cpu(bg->bg_blkno);
        *bits_left = le16_to_cpu(bg->bg_free_bits_count);
 bail:
-       if (group_bh)
-               brelse(group_bh);
-       if (prev_group_bh)
-               brelse(prev_group_bh);
+       brelse(group_bh);
+       brelse(prev_group_bh);
 
        mlog_exit(status);
        return status;
@@ -1723,7 +1775,6 @@ int ocfs2_free_suballoc_bits(handle_t *handle,
 {
        int status = 0;
        u32 tmp_used;
-       struct ocfs2_super *osb = OCFS2_SB(alloc_inode->i_sb);
        struct ocfs2_dinode *fe = (struct ocfs2_dinode *) alloc_bh->b_data;
        struct ocfs2_chain_list *cl = &fe->id2.i_chain;
        struct buffer_head *group_bh = NULL;
@@ -1742,8 +1793,7 @@ int ocfs2_free_suballoc_bits(handle_t *handle,
             (unsigned long long)OCFS2_I(alloc_inode)->ip_blkno, count,
             (unsigned long long)bg_blkno, start_bit);
 
-       status = ocfs2_read_block(osb, bg_blkno, &group_bh, OCFS2_BH_CACHED,
-                                 alloc_inode);
+       status = ocfs2_read_block(alloc_inode, bg_blkno, &group_bh);
        if (status < 0) {
                mlog_errno(status);
                goto bail;
@@ -1784,8 +1834,7 @@ int ocfs2_free_suballoc_bits(handle_t *handle,
        }
 
 bail:
-       if (group_bh)
-               brelse(group_bh);
+       brelse(group_bh);
 
        mlog_exit(status);
        return status;
@@ -1838,9 +1887,15 @@ int ocfs2_free_clusters(handle_t *handle,
        status = ocfs2_free_suballoc_bits(handle, bitmap_inode, bitmap_bh,
                                          bg_start_bit, bg_blkno,
                                          num_clusters);
-       if (status < 0)
+       if (status < 0) {
                mlog_errno(status);
+               goto out;
+       }
 
+       ocfs2_local_alloc_seen_free_bits(OCFS2_SB(bitmap_inode->i_sb),
+                                        num_clusters);
+
+out:
        mlog_exit(status);
        return status;
 }
@@ -1891,3 +1946,84 @@ static inline void ocfs2_debug_suballoc_inode(struct ocfs2_dinode *fe)
                       (unsigned long long)fe->id2.i_chain.cl_recs[i].c_blkno);
        }
 }
+
+/*
+ * For a given allocation, determine which allocators will need to be
+ * accessed, and lock them, reserving the appropriate number of bits.
+ *
+ * Sparse file systems call this from ocfs2_write_begin_nolock()
+ * and ocfs2_allocate_unwritten_extents().
+ *
+ * File systems which don't support holes call this from
+ * ocfs2_extend_allocation().
+ */
+int ocfs2_lock_allocators(struct inode *inode,
+                         struct ocfs2_extent_tree *et,
+                         u32 clusters_to_add, u32 extents_to_split,
+                         struct ocfs2_alloc_context **data_ac,
+                         struct ocfs2_alloc_context **meta_ac)
+{
+       int ret = 0, num_free_extents;
+       unsigned int max_recs_needed = clusters_to_add + 2 * extents_to_split;
+       struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
+
+       *meta_ac = NULL;
+       if (data_ac)
+               *data_ac = NULL;
+
+       BUG_ON(clusters_to_add != 0 && data_ac == NULL);
+
+       num_free_extents = ocfs2_num_free_extents(osb, inode, et);
+       if (num_free_extents < 0) {
+               ret = num_free_extents;
+               mlog_errno(ret);
+               goto out;
+       }
+
+       /*
+        * Sparse allocation file systems need to be more conservative
+        * with reserving room for expansion - the actual allocation
+        * happens while we've got a journal handle open so re-taking
+        * a cluster lock (because we ran out of room for another
+        * extent) will violate ordering rules.
+        *
+        * Most of the time we'll only be seeing this 1 cluster at a time
+        * anyway.
+        *
+        * Always lock for any unwritten extents - we might want to
+        * add blocks during a split.
+        */
+       if (!num_free_extents ||
+           (ocfs2_sparse_alloc(osb) && num_free_extents < max_recs_needed)) {
+               ret = ocfs2_reserve_new_metadata(osb, et->et_root_el, meta_ac);
+               if (ret < 0) {
+                       if (ret != -ENOSPC)
+                               mlog_errno(ret);
+                       goto out;
+               }
+       }
+
+       if (clusters_to_add == 0)
+               goto out;
+
+       ret = ocfs2_reserve_clusters(osb, clusters_to_add, data_ac);
+       if (ret < 0) {
+               if (ret != -ENOSPC)
+                       mlog_errno(ret);
+               goto out;
+       }
+
+out:
+       if (ret) {
+               if (*meta_ac) {
+                       ocfs2_free_alloc_context(*meta_ac);
+                       *meta_ac = NULL;
+               }
+
+               /*
+                * We cannot have an error and a non null *data_ac.
+                */
+       }
+
+       return ret;
+}
index 544c600662bd074c1b76ca1c9ead16ab0f24402a..4df159d8f450330d2a2b7ed200dd8ef48c6a0f53 100644 (file)
 
 typedef int (group_search_t)(struct inode *,
                             struct buffer_head *,
-                            u32,
-                            u32,
-                            u16 *,
-                            u16 *);
+                            u32,                       /* bits_wanted */
+                            u32,                       /* min_bits */
+                            u64,                       /* max_block */
+                            u16 *,                     /* *bit_off */
+                            u16 *);                    /* *bits_found */
 
 struct ocfs2_alloc_context {
        struct inode *ac_inode;    /* which bitmap are we allocating from? */
@@ -51,6 +52,8 @@ struct ocfs2_alloc_context {
        group_search_t *ac_group_search;
 
        u64    ac_last_group;
+       u64    ac_max_block;  /* Highest block number to allocate. 0 is
+                                is the same as ~0 - unlimited */
 };
 
 void ocfs2_free_alloc_context(struct ocfs2_alloc_context *ac);
@@ -59,9 +62,17 @@ static inline int ocfs2_alloc_context_bits_left(struct ocfs2_alloc_context *ac)
        return ac->ac_bits_wanted - ac->ac_bits_given;
 }
 
+/*
+ * Please note that the caller must make sure that root_el is the root
+ * of extent tree. So for an inode, it should be &fe->id2.i_list. Otherwise
+ * the result may be wrong.
+ */
 int ocfs2_reserve_new_metadata(struct ocfs2_super *osb,
-                              struct ocfs2_dinode *fe,
+                              struct ocfs2_extent_list *root_el,
                               struct ocfs2_alloc_context **ac);
+int ocfs2_reserve_new_metadata_blocks(struct ocfs2_super *osb,
+                                     int blocks,
+                                     struct ocfs2_alloc_context **ac);
 int ocfs2_reserve_new_inode(struct ocfs2_super *osb,
                            struct ocfs2_alloc_context **ac);
 int ocfs2_reserve_clusters(struct ocfs2_super *osb,
@@ -147,6 +158,7 @@ static inline int ocfs2_is_cluster_bitmap(struct inode *inode)
  * apis above. */
 int ocfs2_reserve_cluster_bitmap_bits(struct ocfs2_super *osb,
                                      struct ocfs2_alloc_context *ac);
+void ocfs2_free_ac_resource(struct ocfs2_alloc_context *ac);
 
 /* given a cluster offset, calculate which block group it belongs to
  * and return that block offset. */
@@ -156,4 +168,8 @@ u64 ocfs2_which_cluster_group(struct inode *inode, u32 cluster);
 int ocfs2_check_group_descriptor(struct super_block *sb,
                                 struct ocfs2_dinode *di,
                                 struct ocfs2_group_desc *gd);
+int ocfs2_lock_allocators(struct inode *inode, struct ocfs2_extent_tree *et,
+                         u32 clusters_to_add, u32 extents_to_split,
+                         struct ocfs2_alloc_context **data_ac,
+                         struct ocfs2_alloc_context **meta_ac);
 #endif /* _CHAINALLOC_H_ */
index 70334d85aff1348647fbf079eb4f439cd2c505d2..304b63ac78cfcceeef4759c66c5f72952477909d 100644 (file)
@@ -64,6 +64,7 @@
 #include "sysfile.h"
 #include "uptodate.h"
 #include "ver.h"
+#include "xattr.h"
 
 #include "buffer_head_io.h"
 
@@ -154,6 +155,9 @@ enum {
        Opt_localalloc,
        Opt_localflocks,
        Opt_stack,
+       Opt_user_xattr,
+       Opt_nouser_xattr,
+       Opt_inode64,
        Opt_err,
 };
 
@@ -173,6 +177,9 @@ static const match_table_t tokens = {
        {Opt_localalloc, "localalloc=%d"},
        {Opt_localflocks, "localflocks"},
        {Opt_stack, "cluster_stack=%s"},
+       {Opt_user_xattr, "user_xattr"},
+       {Opt_nouser_xattr, "nouser_xattr"},
+       {Opt_inode64, "inode64"},
        {Opt_err, NULL}
 };
 
@@ -205,10 +212,11 @@ static int ocfs2_sync_fs(struct super_block *sb, int wait)
                ocfs2_schedule_truncate_log_flush(osb, 0);
        }
 
-       if (journal_start_commit(OCFS2_SB(sb)->journal->j_journal, &target)) {
+       if (jbd2_journal_start_commit(OCFS2_SB(sb)->journal->j_journal,
+                                     &target)) {
                if (wait)
-                       log_wait_commit(OCFS2_SB(sb)->journal->j_journal,
-                                       target);
+                       jbd2_log_wait_commit(OCFS2_SB(sb)->journal->j_journal,
+                                            target);
        }
        return 0;
 }
@@ -325,6 +333,7 @@ static struct inode *ocfs2_alloc_inode(struct super_block *sb)
        if (!oi)
                return NULL;
 
+       jbd2_journal_init_jbd_inode(&oi->ip_jinode, &oi->vfs_inode);
        return &oi->vfs_inode;
 }
 
@@ -406,6 +415,15 @@ static int ocfs2_remount(struct super_block *sb, int *flags, char *data)
                goto out;
        }
 
+       /* Probably don't want this on remount; it might
+        * mess with other nodes */
+       if (!(osb->s_mount_opt & OCFS2_MOUNT_INODE64) &&
+           (parsed_options.mount_opt & OCFS2_MOUNT_INODE64)) {
+               ret = -EINVAL;
+               mlog(ML_ERROR, "Cannot enable inode64 on remount\n");
+               goto out;
+       }
+
        /* We're going to/from readonly mode. */
        if ((*flags & MS_RDONLY) != (sb->s_flags & MS_RDONLY)) {
                /* Lock here so the check of HARD_RO and the potential
@@ -637,7 +655,8 @@ static int ocfs2_fill_super(struct super_block *sb, void *data, int silent)
        osb->s_atime_quantum = parsed_options.atime_quantum;
        osb->preferred_slot = parsed_options.slot;
        osb->osb_commit_interval = parsed_options.commit_interval;
-       osb->local_alloc_size = parsed_options.localalloc_opt;
+       osb->local_alloc_default_bits = ocfs2_megabytes_to_clusters(sb, parsed_options.localalloc_opt);
+       osb->local_alloc_bits = osb->local_alloc_default_bits;
 
        status = ocfs2_verify_userspace_stack(osb, &parsed_options);
        if (status)
@@ -743,8 +762,7 @@ static int ocfs2_fill_super(struct super_block *sb, void *data, int silent)
        return status;
 
 read_super_error:
-       if (bh != NULL)
-               brelse(bh);
+       brelse(bh);
 
        if (inode)
                iput(inode);
@@ -847,6 +865,12 @@ static int ocfs2_parse_options(struct super_block *sb,
                case Opt_data_writeback:
                        mopt->mount_opt |= OCFS2_MOUNT_DATA_WRITEBACK;
                        break;
+               case Opt_user_xattr:
+                       mopt->mount_opt &= ~OCFS2_MOUNT_NOUSERXATTR;
+                       break;
+               case Opt_nouser_xattr:
+                       mopt->mount_opt |= OCFS2_MOUNT_NOUSERXATTR;
+                       break;
                case Opt_atime_quantum:
                        if (match_int(&args[0], &option)) {
                                status = 0;
@@ -873,7 +897,7 @@ static int ocfs2_parse_options(struct super_block *sb,
                        if (option < 0)
                                return 0;
                        if (option == 0)
-                               option = JBD_DEFAULT_MAX_COMMIT_AGE;
+                               option = JBD2_DEFAULT_MAX_COMMIT_AGE;
                        mopt->commit_interval = HZ * option;
                        break;
                case Opt_localalloc:
@@ -918,6 +942,9 @@ static int ocfs2_parse_options(struct super_block *sb,
                               OCFS2_STACK_LABEL_LEN);
                        mopt->cluster_stack[OCFS2_STACK_LABEL_LEN] = '\0';
                        break;
+               case Opt_inode64:
+                       mopt->mount_opt |= OCFS2_MOUNT_INODE64;
+                       break;
                default:
                        mlog(ML_ERROR,
                             "Unrecognized mount option \"%s\" "
@@ -938,6 +965,7 @@ static int ocfs2_show_options(struct seq_file *s, struct vfsmount *mnt)
 {
        struct ocfs2_super *osb = OCFS2_SB(mnt->mnt_sb);
        unsigned long opts = osb->s_mount_opt;
+       unsigned int local_alloc_megs;
 
        if (opts & OCFS2_MOUNT_HB_LOCAL)
                seq_printf(s, ",_netdev,heartbeat=local");
@@ -970,8 +998,9 @@ static int ocfs2_show_options(struct seq_file *s, struct vfsmount *mnt)
                seq_printf(s, ",commit=%u",
                           (unsigned) (osb->osb_commit_interval / HZ));
 
-       if (osb->local_alloc_size != OCFS2_DEFAULT_LOCAL_ALLOC_SIZE)
-               seq_printf(s, ",localalloc=%d", osb->local_alloc_size);
+       local_alloc_megs = osb->local_alloc_bits >> (20 - osb->s_clustersize_bits);
+       if (local_alloc_megs != OCFS2_DEFAULT_LOCAL_ALLOC_SIZE)
+               seq_printf(s, ",localalloc=%d", local_alloc_megs);
 
        if (opts & OCFS2_MOUNT_LOCALFLOCKS)
                seq_printf(s, ",localflocks,");
@@ -980,6 +1009,14 @@ static int ocfs2_show_options(struct seq_file *s, struct vfsmount *mnt)
                seq_printf(s, ",cluster_stack=%.*s", OCFS2_STACK_LABEL_LEN,
                           osb->osb_cluster_stack);
 
+       if (opts & OCFS2_MOUNT_NOUSERXATTR)
+               seq_printf(s, ",nouser_xattr");
+       else
+               seq_printf(s, ",user_xattr");
+
+       if (opts & OCFS2_MOUNT_INODE64)
+               seq_printf(s, ",inode64");
+
        return 0;
 }
 
@@ -1132,6 +1169,7 @@ static void ocfs2_inode_init_once(void *data)
        oi->ip_dir_start_lookup = 0;
 
        init_rwsem(&oi->ip_alloc_sem);
+       init_rwsem(&oi->ip_xattr_sem);
        mutex_init(&oi->ip_io_mutex);
 
        oi->ip_blkno = 0ULL;
@@ -1375,6 +1413,7 @@ static int ocfs2_initialize_super(struct super_block *sb,
        sb->s_fs_info = osb;
        sb->s_op = &ocfs2_sops;
        sb->s_export_op = &ocfs2_export_ops;
+       sb->s_xattr = ocfs2_xattr_handlers;
        sb->s_time_gran = 1;
        sb->s_flags |= MS_NOATIME;
        /* this is needed to support O_LARGEFILE */
@@ -1421,8 +1460,12 @@ static int ocfs2_initialize_super(struct super_block *sb,
 
        osb->slot_num = OCFS2_INVALID_SLOT;
 
+       osb->s_xattr_inline_size = le16_to_cpu(
+                                       di->id2.i_super.s_xattr_inline_size);
+
        osb->local_alloc_state = OCFS2_LA_UNUSED;
        osb->local_alloc_bh = NULL;
+       INIT_DELAYED_WORK(&osb->la_enable_wq, ocfs2_la_enable_worker);
 
        init_waitqueue_head(&osb->osb_mount_event);
 
@@ -1568,6 +1611,7 @@ static int ocfs2_initialize_super(struct super_block *sb,
        osb->first_cluster_group_blkno =
                le64_to_cpu(di->id2.i_super.s_first_cluster_group);
        osb->fs_generation = le32_to_cpu(di->i_fs_generation);
+       osb->uuid_hash = le32_to_cpu(di->id2.i_super.s_uuid_hash);
        mlog(0, "vol_label: %s\n", osb->vol_label);
        mlog(0, "uuid: %s\n", osb->uuid_str);
        mlog(0, "root_blkno=%llu, system_dir_blkno=%llu\n",
index ba9dbb51d25b5ed120922d9192e7098d239d7f5c..cbd03dfdc7b987aaef8810adfbce4590e6fdacc8 100644 (file)
@@ -50,6 +50,7 @@
 #include "inode.h"
 #include "journal.h"
 #include "symlink.h"
+#include "xattr.h"
 
 #include "buffer_head_io.h"
 
@@ -83,11 +84,7 @@ static char *ocfs2_fast_symlink_getlink(struct inode *inode,
 
        mlog_entry_void();
 
-       status = ocfs2_read_block(OCFS2_SB(inode->i_sb),
-                                 OCFS2_I(inode)->ip_blkno,
-                                 bh,
-                                 OCFS2_BH_CACHED,
-                                 inode);
+       status = ocfs2_read_block(inode, OCFS2_I(inode)->ip_blkno, bh);
        if (status < 0) {
                mlog_errno(status);
                link = ERR_PTR(status);
@@ -157,8 +154,7 @@ bail:
                kunmap(page);
                page_cache_release(page);
        }
-       if (bh)
-               brelse(bh);
+       brelse(bh);
 
        return ERR_PTR(status);
 }
@@ -168,10 +164,18 @@ const struct inode_operations ocfs2_symlink_inode_operations = {
        .follow_link    = ocfs2_follow_link,
        .getattr        = ocfs2_getattr,
        .setattr        = ocfs2_setattr,
+       .setxattr       = generic_setxattr,
+       .getxattr       = generic_getxattr,
+       .listxattr      = ocfs2_listxattr,
+       .removexattr    = generic_removexattr,
 };
 const struct inode_operations ocfs2_fast_symlink_inode_operations = {
        .readlink       = ocfs2_readlink,
        .follow_link    = ocfs2_follow_link,
        .getattr        = ocfs2_getattr,
        .setattr        = ocfs2_setattr,
+       .setxattr       = generic_setxattr,
+       .getxattr       = generic_getxattr,
+       .listxattr      = ocfs2_listxattr,
+       .removexattr    = generic_removexattr,
 };
index 4da8851f2b23af737df164d0ec64133117f19508..187b99ff03688992fc0f141390ca6bec667fa027 100644 (file)
 #include <linux/highmem.h>
 #include <linux/buffer_head.h>
 #include <linux/rbtree.h>
-#include <linux/jbd.h>
+#ifndef CONFIG_OCFS2_COMPAT_JBD
+# include <linux/jbd2.h>
+#else
+# include <linux/jbd.h>
+#endif
 
 #define MLOG_MASK_PREFIX ML_UPTODATE
 
@@ -511,14 +515,10 @@ static void ocfs2_remove_metadata_tree(struct ocfs2_caching_info *ci,
        ci->ci_num_cached--;
 }
 
-/* Called when we remove a chunk of metadata from an inode. We don't
- * bother reverting things to an inlined array in the case of a remove
- * which moves us back under the limit. */
-void ocfs2_remove_from_cache(struct inode *inode,
-                            struct buffer_head *bh)
+static void ocfs2_remove_block_from_cache(struct inode *inode,
+                                         sector_t block)
 {
        int index;
-       sector_t block = bh->b_blocknr;
        struct ocfs2_meta_cache_item *item = NULL;
        struct ocfs2_inode_info *oi = OCFS2_I(inode);
        struct ocfs2_caching_info *ci = &oi->ip_metadata_cache;
@@ -544,6 +544,30 @@ void ocfs2_remove_from_cache(struct inode *inode,
                kmem_cache_free(ocfs2_uptodate_cachep, item);
 }
 
+/*
+ * Called when we remove a chunk of metadata from an inode. We don't
+ * bother reverting things to an inlined array in the case of a remove
+ * which moves us back under the limit.
+ */
+void ocfs2_remove_from_cache(struct inode *inode,
+                            struct buffer_head *bh)
+{
+       sector_t block = bh->b_blocknr;
+
+       ocfs2_remove_block_from_cache(inode, block);
+}
+
+/* Called when we remove xattr clusters from an inode. */
+void ocfs2_remove_xattr_clusters_from_cache(struct inode *inode,
+                                           sector_t block,
+                                           u32 c_len)
+{
+       unsigned int i, b_len = ocfs2_clusters_to_blocks(inode->i_sb, 1) * c_len;
+
+       for (i = 0; i < b_len; i++, block++)
+               ocfs2_remove_block_from_cache(inode, block);
+}
+
 int __init init_ocfs2_uptodate_cache(void)
 {
        ocfs2_uptodate_cachep = kmem_cache_create("ocfs2_uptodate",
index 2e73206059a857af1ed61960ae77922f40efb786..531b4b3a0c47ffa66fc680fc38dfc890cf09a34b 100644 (file)
@@ -40,6 +40,9 @@ void ocfs2_set_new_buffer_uptodate(struct inode *inode,
                                   struct buffer_head *bh);
 void ocfs2_remove_from_cache(struct inode *inode,
                             struct buffer_head *bh);
+void ocfs2_remove_xattr_clusters_from_cache(struct inode *inode,
+                                           sector_t block,
+                                           u32 c_len);
 int ocfs2_buffer_read_ahead(struct inode *inode,
                            struct buffer_head *bh);
 
diff --git a/fs/ocfs2/xattr.c b/fs/ocfs2/xattr.c
new file mode 100644 (file)
index 0000000..c25780a
--- /dev/null
@@ -0,0 +1,4834 @@
+/* -*- mode: c; c-basic-offset: 8; -*-
+ * vim: noexpandtab sw=8 ts=8 sts=0:
+ *
+ * xattr.c
+ *
+ * Copyright (C) 2008 Oracle.  All rights reserved.
+ *
+ * CREDITS:
+ * Lots of code in this file is taken from ext3.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 021110-1307, USA.
+ */
+
+#include <linux/capability.h>
+#include <linux/fs.h>
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/highmem.h>
+#include <linux/pagemap.h>
+#include <linux/uio.h>
+#include <linux/sched.h>
+#include <linux/splice.h>
+#include <linux/mount.h>
+#include <linux/writeback.h>
+#include <linux/falloc.h>
+#include <linux/sort.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/string.h>
+
+#define MLOG_MASK_PREFIX ML_XATTR
+#include <cluster/masklog.h>
+
+#include "ocfs2.h"
+#include "alloc.h"
+#include "dlmglue.h"
+#include "file.h"
+#include "symlink.h"
+#include "sysfile.h"
+#include "inode.h"
+#include "journal.h"
+#include "ocfs2_fs.h"
+#include "suballoc.h"
+#include "uptodate.h"
+#include "buffer_head_io.h"
+#include "super.h"
+#include "xattr.h"
+
+
+struct ocfs2_xattr_def_value_root {
+       struct ocfs2_xattr_value_root   xv;
+       struct ocfs2_extent_rec         er;
+};
+
+struct ocfs2_xattr_bucket {
+       struct buffer_head *bhs[OCFS2_XATTR_MAX_BLOCKS_PER_BUCKET];
+       struct ocfs2_xattr_header *xh;
+};
+
+#define OCFS2_XATTR_ROOT_SIZE  (sizeof(struct ocfs2_xattr_def_value_root))
+#define OCFS2_XATTR_INLINE_SIZE        80
+
+static struct ocfs2_xattr_def_value_root def_xv = {
+       .xv.xr_list.l_count = cpu_to_le16(1),
+};
+
+struct xattr_handler *ocfs2_xattr_handlers[] = {
+       &ocfs2_xattr_user_handler,
+       &ocfs2_xattr_trusted_handler,
+       NULL
+};
+
+static struct xattr_handler *ocfs2_xattr_handler_map[] = {
+       [OCFS2_XATTR_INDEX_USER]        = &ocfs2_xattr_user_handler,
+       [OCFS2_XATTR_INDEX_TRUSTED]     = &ocfs2_xattr_trusted_handler,
+};
+
+struct ocfs2_xattr_info {
+       int name_index;
+       const char *name;
+       const void *value;
+       size_t value_len;
+};
+
+struct ocfs2_xattr_search {
+       struct buffer_head *inode_bh;
+       /*
+        * xattr_bh point to the block buffer head which has extended attribute
+        * when extended attribute in inode, xattr_bh is equal to inode_bh.
+        */
+       struct buffer_head *xattr_bh;
+       struct ocfs2_xattr_header *header;
+       struct ocfs2_xattr_bucket bucket;
+       void *base;
+       void *end;
+       struct ocfs2_xattr_entry *here;
+       int not_found;
+};
+
+static int ocfs2_xattr_bucket_get_name_value(struct inode *inode,
+                                            struct ocfs2_xattr_header *xh,
+                                            int index,
+                                            int *block_off,
+                                            int *new_offset);
+
+static int ocfs2_xattr_index_block_find(struct inode *inode,
+                                       struct buffer_head *root_bh,
+                                       int name_index,
+                                       const char *name,
+                                       struct ocfs2_xattr_search *xs);
+
+static int ocfs2_xattr_tree_list_index_block(struct inode *inode,
+                                       struct ocfs2_xattr_tree_root *xt,
+                                       char *buffer,
+                                       size_t buffer_size);
+
+static int ocfs2_xattr_create_index_block(struct inode *inode,
+                                         struct ocfs2_xattr_search *xs);
+
+static int ocfs2_xattr_set_entry_index_block(struct inode *inode,
+                                            struct ocfs2_xattr_info *xi,
+                                            struct ocfs2_xattr_search *xs);
+
+static int ocfs2_delete_xattr_index_block(struct inode *inode,
+                                         struct buffer_head *xb_bh);
+
+static inline const char *ocfs2_xattr_prefix(int name_index)
+{
+       struct xattr_handler *handler = NULL;
+
+       if (name_index > 0 && name_index < OCFS2_XATTR_MAX)
+               handler = ocfs2_xattr_handler_map[name_index];
+
+       return handler ? handler->prefix : NULL;
+}
+
+static u32 ocfs2_xattr_name_hash(struct inode *inode,
+                                const char *name,
+                                int name_len)
+{
+       /* Get hash value of uuid from super block */
+       u32 hash = OCFS2_SB(inode->i_sb)->uuid_hash;
+       int i;
+
+       /* hash extended attribute name */
+       for (i = 0; i < name_len; i++) {
+               hash = (hash << OCFS2_HASH_SHIFT) ^
+                      (hash >> (8*sizeof(hash) - OCFS2_HASH_SHIFT)) ^
+                      *name++;
+       }
+
+       return hash;
+}
+
+/*
+ * ocfs2_xattr_hash_entry()
+ *
+ * Compute the hash of an extended attribute.
+ */
+static void ocfs2_xattr_hash_entry(struct inode *inode,
+                                  struct ocfs2_xattr_header *header,
+                                  struct ocfs2_xattr_entry *entry)
+{
+       u32 hash = 0;
+       char *name = (char *)header + le16_to_cpu(entry->xe_name_offset);
+
+       hash = ocfs2_xattr_name_hash(inode, name, entry->xe_name_len);
+       entry->xe_name_hash = cpu_to_le32(hash);
+
+       return;
+}
+
+static int ocfs2_xattr_extend_allocation(struct inode *inode,
+                                        u32 clusters_to_add,
+                                        struct buffer_head *xattr_bh,
+                                        struct ocfs2_xattr_value_root *xv)
+{
+       int status = 0;
+       int restart_func = 0;
+       int credits = 0;
+       handle_t *handle = NULL;
+       struct ocfs2_alloc_context *data_ac = NULL;
+       struct ocfs2_alloc_context *meta_ac = NULL;
+       enum ocfs2_alloc_restarted why;
+       struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
+       u32 prev_clusters, logical_start = le32_to_cpu(xv->xr_clusters);
+       struct ocfs2_extent_tree et;
+
+       mlog(0, "(clusters_to_add for xattr= %u)\n", clusters_to_add);
+
+       ocfs2_init_xattr_value_extent_tree(&et, inode, xattr_bh, xv);
+
+restart_all:
+
+       status = ocfs2_lock_allocators(inode, &et, clusters_to_add, 0,
+                                      &data_ac, &meta_ac);
+       if (status) {
+               mlog_errno(status);
+               goto leave;
+       }
+
+       credits = ocfs2_calc_extend_credits(osb->sb, et.et_root_el,
+                                           clusters_to_add);
+       handle = ocfs2_start_trans(osb, credits);
+       if (IS_ERR(handle)) {
+               status = PTR_ERR(handle);
+               handle = NULL;
+               mlog_errno(status);
+               goto leave;
+       }
+
+restarted_transaction:
+       status = ocfs2_journal_access(handle, inode, xattr_bh,
+                                     OCFS2_JOURNAL_ACCESS_WRITE);
+       if (status < 0) {
+               mlog_errno(status);
+               goto leave;
+       }
+
+       prev_clusters = le32_to_cpu(xv->xr_clusters);
+       status = ocfs2_add_clusters_in_btree(osb,
+                                            inode,
+                                            &logical_start,
+                                            clusters_to_add,
+                                            0,
+                                            &et,
+                                            handle,
+                                            data_ac,
+                                            meta_ac,
+                                            &why);
+       if ((status < 0) && (status != -EAGAIN)) {
+               if (status != -ENOSPC)
+                       mlog_errno(status);
+               goto leave;
+       }
+
+       status = ocfs2_journal_dirty(handle, xattr_bh);
+       if (status < 0) {
+               mlog_errno(status);
+               goto leave;
+       }
+
+       clusters_to_add -= le32_to_cpu(xv->xr_clusters) - prev_clusters;
+
+       if (why != RESTART_NONE && clusters_to_add) {
+               if (why == RESTART_META) {
+                       mlog(0, "restarting function.\n");
+                       restart_func = 1;
+               } else {
+                       BUG_ON(why != RESTART_TRANS);
+
+                       mlog(0, "restarting transaction.\n");
+                       /* TODO: This can be more intelligent. */
+                       credits = ocfs2_calc_extend_credits(osb->sb,
+                                                           et.et_root_el,
+                                                           clusters_to_add);
+                       status = ocfs2_extend_trans(handle, credits);
+                       if (status < 0) {
+                               /* handle still has to be committed at
+                                * this point. */
+                               status = -ENOMEM;
+                               mlog_errno(status);
+                               goto leave;
+                       }
+                       goto restarted_transaction;
+               }
+       }
+
+leave:
+       if (handle) {
+               ocfs2_commit_trans(osb, handle);
+               handle = NULL;
+       }
+       if (data_ac) {
+               ocfs2_free_alloc_context(data_ac);
+               data_ac = NULL;
+       }
+       if (meta_ac) {
+               ocfs2_free_alloc_context(meta_ac);
+               meta_ac = NULL;
+       }
+       if ((!status) && restart_func) {
+               restart_func = 0;
+               goto restart_all;
+       }
+
+       return status;
+}
+
+static int __ocfs2_remove_xattr_range(struct inode *inode,
+                                     struct buffer_head *root_bh,
+                                     struct ocfs2_xattr_value_root *xv,
+                                     u32 cpos, u32 phys_cpos, u32 len,
+                                     struct ocfs2_cached_dealloc_ctxt *dealloc)
+{
+       int ret;
+       u64 phys_blkno = ocfs2_clusters_to_blocks(inode->i_sb, phys_cpos);
+       struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
+       struct inode *tl_inode = osb->osb_tl_inode;
+       handle_t *handle;
+       struct ocfs2_alloc_context *meta_ac = NULL;
+       struct ocfs2_extent_tree et;
+
+       ocfs2_init_xattr_value_extent_tree(&et, inode, root_bh, xv);
+
+       ret = ocfs2_lock_allocators(inode, &et, 0, 1, NULL, &meta_ac);
+       if (ret) {
+               mlog_errno(ret);
+               return ret;
+       }
+
+       mutex_lock(&tl_inode->i_mutex);
+
+       if (ocfs2_truncate_log_needs_flush(osb)) {
+               ret = __ocfs2_flush_truncate_log(osb);
+               if (ret < 0) {
+                       mlog_errno(ret);
+                       goto out;
+               }
+       }
+
+       handle = ocfs2_start_trans(osb, OCFS2_REMOVE_EXTENT_CREDITS);
+       if (IS_ERR(handle)) {
+               ret = PTR_ERR(handle);
+               mlog_errno(ret);
+               goto out;
+       }
+
+       ret = ocfs2_journal_access(handle, inode, root_bh,
+                                  OCFS2_JOURNAL_ACCESS_WRITE);
+       if (ret) {
+               mlog_errno(ret);
+               goto out_commit;
+       }
+
+       ret = ocfs2_remove_extent(inode, &et, cpos, len, handle, meta_ac,
+                                 dealloc);
+       if (ret) {
+               mlog_errno(ret);
+               goto out_commit;
+       }
+
+       le32_add_cpu(&xv->xr_clusters, -len);
+
+       ret = ocfs2_journal_dirty(handle, root_bh);
+       if (ret) {
+               mlog_errno(ret);
+               goto out_commit;
+       }
+
+       ret = ocfs2_truncate_log_append(osb, handle, phys_blkno, len);
+       if (ret)
+               mlog_errno(ret);
+
+out_commit:
+       ocfs2_commit_trans(osb, handle);
+out:
+       mutex_unlock(&tl_inode->i_mutex);
+
+       if (meta_ac)
+               ocfs2_free_alloc_context(meta_ac);
+
+       return ret;
+}
+
+static int ocfs2_xattr_shrink_size(struct inode *inode,
+                                  u32 old_clusters,
+                                  u32 new_clusters,
+                                  struct buffer_head *root_bh,
+                                  struct ocfs2_xattr_value_root *xv)
+{
+       int ret = 0;
+       u32 trunc_len, cpos, phys_cpos, alloc_size;
+       u64 block;
+       struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
+       struct ocfs2_cached_dealloc_ctxt dealloc;
+
+       ocfs2_init_dealloc_ctxt(&dealloc);
+
+       if (old_clusters <= new_clusters)
+               return 0;
+
+       cpos = new_clusters;
+       trunc_len = old_clusters - new_clusters;
+       while (trunc_len) {
+               ret = ocfs2_xattr_get_clusters(inode, cpos, &phys_cpos,
+                                              &alloc_size, &xv->xr_list);
+               if (ret) {
+                       mlog_errno(ret);
+                       goto out;
+               }
+
+               if (alloc_size > trunc_len)
+                       alloc_size = trunc_len;
+
+               ret = __ocfs2_remove_xattr_range(inode, root_bh, xv, cpos,
+                                                phys_cpos, alloc_size,
+                                                &dealloc);
+               if (ret) {
+                       mlog_errno(ret);
+                       goto out;
+               }
+
+               block = ocfs2_clusters_to_blocks(inode->i_sb, phys_cpos);
+               ocfs2_remove_xattr_clusters_from_cache(inode, block,
+                                                      alloc_size);
+               cpos += alloc_size;
+               trunc_len -= alloc_size;
+       }
+
+out:
+       ocfs2_schedule_truncate_log_flush(osb, 1);
+       ocfs2_run_deallocs(osb, &dealloc);
+
+       return ret;
+}
+
+static int ocfs2_xattr_value_truncate(struct inode *inode,
+                                     struct buffer_head *root_bh,
+                                     struct ocfs2_xattr_value_root *xv,
+                                     int len)
+{
+       int ret;
+       u32 new_clusters = ocfs2_clusters_for_bytes(inode->i_sb, len);
+       u32 old_clusters = le32_to_cpu(xv->xr_clusters);
+
+       if (new_clusters == old_clusters)
+               return 0;
+
+       if (new_clusters > old_clusters)
+               ret = ocfs2_xattr_extend_allocation(inode,
+                                                   new_clusters - old_clusters,
+                                                   root_bh, xv);
+       else
+               ret = ocfs2_xattr_shrink_size(inode,
+                                             old_clusters, new_clusters,
+                                             root_bh, xv);
+
+       return ret;
+}
+
+static int ocfs2_xattr_list_entry(char *buffer, size_t size,
+                                 size_t *result, const char *prefix,
+                                 const char *name, int name_len)
+{
+       char *p = buffer + *result;
+       int prefix_len = strlen(prefix);
+       int total_len = prefix_len + name_len + 1;
+
+       *result += total_len;
+
+       /* we are just looking for how big our buffer needs to be */
+       if (!size)
+               return 0;
+
+       if (*result > size)
+               return -ERANGE;
+
+       memcpy(p, prefix, prefix_len);
+       memcpy(p + prefix_len, name, name_len);
+       p[prefix_len + name_len] = '\0';
+
+       return 0;
+}
+
+static int ocfs2_xattr_list_entries(struct inode *inode,
+                                   struct ocfs2_xattr_header *header,
+                                   char *buffer, size_t buffer_size)
+{
+       size_t result = 0;
+       int i, type, ret;
+       const char *prefix, *name;
+
+       for (i = 0 ; i < le16_to_cpu(header->xh_count); i++) {
+               struct ocfs2_xattr_entry *entry = &header->xh_entries[i];
+               type = ocfs2_xattr_get_type(entry);
+               prefix = ocfs2_xattr_prefix(type);
+
+               if (prefix) {
+                       name = (const char *)header +
+                               le16_to_cpu(entry->xe_name_offset);
+
+                       ret = ocfs2_xattr_list_entry(buffer, buffer_size,
+                                                    &result, prefix, name,
+                                                    entry->xe_name_len);
+                       if (ret)
+                               return ret;
+               }
+       }
+
+       return result;
+}
+
+static int ocfs2_xattr_ibody_list(struct inode *inode,
+                                 struct ocfs2_dinode *di,
+                                 char *buffer,
+                                 size_t buffer_size)
+{
+       struct ocfs2_xattr_header *header = NULL;
+       struct ocfs2_inode_info *oi = OCFS2_I(inode);
+       int ret = 0;
+
+       if (!(oi->ip_dyn_features & OCFS2_INLINE_XATTR_FL))
+               return ret;
+
+       header = (struct ocfs2_xattr_header *)
+                ((void *)di + inode->i_sb->s_blocksize -
+                le16_to_cpu(di->i_xattr_inline_size));
+
+       ret = ocfs2_xattr_list_entries(inode, header, buffer, buffer_size);
+
+       return ret;
+}
+
+static int ocfs2_xattr_block_list(struct inode *inode,
+                                 struct ocfs2_dinode *di,
+                                 char *buffer,
+                                 size_t buffer_size)
+{
+       struct buffer_head *blk_bh = NULL;
+       struct ocfs2_xattr_block *xb;
+       int ret = 0;
+
+       if (!di->i_xattr_loc)
+               return ret;
+
+       ret = ocfs2_read_block(inode, le64_to_cpu(di->i_xattr_loc), &blk_bh);
+       if (ret < 0) {
+               mlog_errno(ret);
+               return ret;
+       }
+       /*Verify the signature of xattr block*/
+       if (memcmp((void *)blk_bh->b_data, OCFS2_XATTR_BLOCK_SIGNATURE,
+                  strlen(OCFS2_XATTR_BLOCK_SIGNATURE))) {
+               ret = -EFAULT;
+               goto cleanup;
+       }
+
+       xb = (struct ocfs2_xattr_block *)blk_bh->b_data;
+
+       if (!(le16_to_cpu(xb->xb_flags) & OCFS2_XATTR_INDEXED)) {
+               struct ocfs2_xattr_header *header = &xb->xb_attrs.xb_header;
+               ret = ocfs2_xattr_list_entries(inode, header,
+                                              buffer, buffer_size);
+       } else {
+               struct ocfs2_xattr_tree_root *xt = &xb->xb_attrs.xb_root;
+               ret = ocfs2_xattr_tree_list_index_block(inode, xt,
+                                                  buffer, buffer_size);
+       }
+cleanup:
+       brelse(blk_bh);
+
+       return ret;
+}
+
+ssize_t ocfs2_listxattr(struct dentry *dentry,
+                       char *buffer,
+                       size_t size)
+{
+       int ret = 0, i_ret = 0, b_ret = 0;
+       struct buffer_head *di_bh = NULL;
+       struct ocfs2_dinode *di = NULL;
+       struct ocfs2_inode_info *oi = OCFS2_I(dentry->d_inode);
+
+       if (!ocfs2_supports_xattr(OCFS2_SB(dentry->d_sb)))
+               return -EOPNOTSUPP;
+
+       if (!(oi->ip_dyn_features & OCFS2_HAS_XATTR_FL))
+               return ret;
+
+       ret = ocfs2_inode_lock(dentry->d_inode, &di_bh, 0);
+       if (ret < 0) {
+               mlog_errno(ret);
+               return ret;
+       }
+
+       di = (struct ocfs2_dinode *)di_bh->b_data;
+
+       down_read(&oi->ip_xattr_sem);
+       i_ret = ocfs2_xattr_ibody_list(dentry->d_inode, di, buffer, size);
+       if (i_ret < 0)
+               b_ret = 0;
+       else {
+               if (buffer) {
+                       buffer += i_ret;
+                       size -= i_ret;
+               }
+               b_ret = ocfs2_xattr_block_list(dentry->d_inode, di,
+                                              buffer, size);
+               if (b_ret < 0)
+                       i_ret = 0;
+       }
+       up_read(&oi->ip_xattr_sem);
+       ocfs2_inode_unlock(dentry->d_inode, 0);
+
+       brelse(di_bh);
+
+       return i_ret + b_ret;
+}
+
+static int ocfs2_xattr_find_entry(int name_index,
+                                 const char *name,
+                                 struct ocfs2_xattr_search *xs)
+{
+       struct ocfs2_xattr_entry *entry;
+       size_t name_len;
+       int i, cmp = 1;
+
+       if (name == NULL)
+               return -EINVAL;
+
+       name_len = strlen(name);
+       entry = xs->here;
+       for (i = 0; i < le16_to_cpu(xs->header->xh_count); i++) {
+               cmp = name_index - ocfs2_xattr_get_type(entry);
+               if (!cmp)
+                       cmp = name_len - entry->xe_name_len;
+               if (!cmp)
+                       cmp = memcmp(name, (xs->base +
+                                    le16_to_cpu(entry->xe_name_offset)),
+                                    name_len);
+               if (cmp == 0)
+                       break;
+               entry += 1;
+       }
+       xs->here = entry;
+
+       return cmp ? -ENODATA : 0;
+}
+
+static int ocfs2_xattr_get_value_outside(struct inode *inode,
+                                        struct ocfs2_xattr_value_root *xv,
+                                        void *buffer,
+                                        size_t len)
+{
+       u32 cpos, p_cluster, num_clusters, bpc, clusters;
+       u64 blkno;
+       int i, ret = 0;
+       size_t cplen, blocksize;
+       struct buffer_head *bh = NULL;
+       struct ocfs2_extent_list *el;
+
+       el = &xv->xr_list;
+       clusters = le32_to_cpu(xv->xr_clusters);
+       bpc = ocfs2_clusters_to_blocks(inode->i_sb, 1);
+       blocksize = inode->i_sb->s_blocksize;
+
+       cpos = 0;
+       while (cpos < clusters) {
+               ret = ocfs2_xattr_get_clusters(inode, cpos, &p_cluster,
+                                              &num_clusters, el);
+               if (ret) {
+                       mlog_errno(ret);
+                       goto out;
+               }
+
+               blkno = ocfs2_clusters_to_blocks(inode->i_sb, p_cluster);
+               /* Copy ocfs2_xattr_value */
+               for (i = 0; i < num_clusters * bpc; i++, blkno++) {
+                       ret = ocfs2_read_block(inode, blkno, &bh);
+                       if (ret) {
+                               mlog_errno(ret);
+                               goto out;
+                       }
+
+                       cplen = len >= blocksize ? blocksize : len;
+                       memcpy(buffer, bh->b_data, cplen);
+                       len -= cplen;
+                       buffer += cplen;
+
+                       brelse(bh);
+                       bh = NULL;
+                       if (len == 0)
+                               break;
+               }
+               cpos += num_clusters;
+       }
+out:
+       return ret;
+}
+
+static int ocfs2_xattr_ibody_get(struct inode *inode,
+                                int name_index,
+                                const char *name,
+                                void *buffer,
+                                size_t buffer_size,
+                                struct ocfs2_xattr_search *xs)
+{
+       struct ocfs2_inode_info *oi = OCFS2_I(inode);
+       struct ocfs2_dinode *di = (struct ocfs2_dinode *)xs->inode_bh->b_data;
+       struct ocfs2_xattr_value_root *xv;
+       size_t size;
+       int ret = 0;
+
+       if (!(oi->ip_dyn_features & OCFS2_INLINE_XATTR_FL))
+               return -ENODATA;
+
+       xs->end = (void *)di + inode->i_sb->s_blocksize;
+       xs->header = (struct ocfs2_xattr_header *)
+                       (xs->end - le16_to_cpu(di->i_xattr_inline_size));
+       xs->base = (void *)xs->header;
+       xs->here = xs->header->xh_entries;
+
+       ret = ocfs2_xattr_find_entry(name_index, name, xs);
+       if (ret)
+               return ret;
+       size = le64_to_cpu(xs->here->xe_value_size);
+       if (buffer) {
+               if (size > buffer_size)
+                       return -ERANGE;
+               if (ocfs2_xattr_is_local(xs->here)) {
+                       memcpy(buffer, (void *)xs->base +
+                              le16_to_cpu(xs->here->xe_name_offset) +
+                              OCFS2_XATTR_SIZE(xs->here->xe_name_len), size);
+               } else {
+                       xv = (struct ocfs2_xattr_value_root *)
+                               (xs->base + le16_to_cpu(
+                                xs->here->xe_name_offset) +
+                               OCFS2_XATTR_SIZE(xs->here->xe_name_len));
+                       ret = ocfs2_xattr_get_value_outside(inode, xv,
+                                                           buffer, size);
+                       if (ret < 0) {
+                               mlog_errno(ret);
+                               return ret;
+                       }
+               }
+       }
+
+       return size;
+}
+
+static int ocfs2_xattr_block_get(struct inode *inode,
+                                int name_index,
+                                const char *name,
+                                void *buffer,
+                                size_t buffer_size,
+                                struct ocfs2_xattr_search *xs)
+{
+       struct ocfs2_dinode *di = (struct ocfs2_dinode *)xs->inode_bh->b_data;
+       struct buffer_head *blk_bh = NULL;
+       struct ocfs2_xattr_block *xb;
+       struct ocfs2_xattr_value_root *xv;
+       size_t size;
+       int ret = -ENODATA, name_offset, name_len, block_off, i;
+
+       if (!di->i_xattr_loc)
+               return ret;
+
+       memset(&xs->bucket, 0, sizeof(xs->bucket));
+
+       ret = ocfs2_read_block(inode, le64_to_cpu(di->i_xattr_loc), &blk_bh);
+       if (ret < 0) {
+               mlog_errno(ret);
+               return ret;
+       }
+       /*Verify the signature of xattr block*/
+       if (memcmp((void *)blk_bh->b_data, OCFS2_XATTR_BLOCK_SIGNATURE,
+                  strlen(OCFS2_XATTR_BLOCK_SIGNATURE))) {
+               ret = -EFAULT;
+               goto cleanup;
+       }
+
+       xs->xattr_bh = blk_bh;
+       xb = (struct ocfs2_xattr_block *)blk_bh->b_data;
+
+       if (!(le16_to_cpu(xb->xb_flags) & OCFS2_XATTR_INDEXED)) {
+               xs->header = &xb->xb_attrs.xb_header;
+               xs->base = (void *)xs->header;
+               xs->end = (void *)(blk_bh->b_data) + blk_bh->b_size;
+               xs->here = xs->header->xh_entries;
+
+               ret = ocfs2_xattr_find_entry(name_index, name, xs);
+       } else
+               ret = ocfs2_xattr_index_block_find(inode, blk_bh,
+                                                  name_index,
+                                                  name, xs);
+
+       if (ret)
+               goto cleanup;
+       size = le64_to_cpu(xs->here->xe_value_size);
+       if (buffer) {
+               ret = -ERANGE;
+               if (size > buffer_size)
+                       goto cleanup;
+
+               name_offset = le16_to_cpu(xs->here->xe_name_offset);
+               name_len = OCFS2_XATTR_SIZE(xs->here->xe_name_len);
+               i = xs->here - xs->header->xh_entries;
+
+               if (le16_to_cpu(xb->xb_flags) & OCFS2_XATTR_INDEXED) {
+                       ret = ocfs2_xattr_bucket_get_name_value(inode,
+                                                               xs->bucket.xh,
+                                                               i,
+                                                               &block_off,
+                                                               &name_offset);
+                       xs->base = xs->bucket.bhs[block_off]->b_data;
+               }
+               if (ocfs2_xattr_is_local(xs->here)) {
+                       memcpy(buffer, (void *)xs->base +
+                              name_offset + name_len, size);
+               } else {
+                       xv = (struct ocfs2_xattr_value_root *)
+                               (xs->base + name_offset + name_len);
+                       ret = ocfs2_xattr_get_value_outside(inode, xv,
+                                                           buffer, size);
+                       if (ret < 0) {
+                               mlog_errno(ret);
+                               goto cleanup;
+                       }
+               }
+       }
+       ret = size;
+cleanup:
+       for (i = 0; i < OCFS2_XATTR_MAX_BLOCKS_PER_BUCKET; i++)
+               brelse(xs->bucket.bhs[i]);
+       memset(&xs->bucket, 0, sizeof(xs->bucket));
+
+       brelse(blk_bh);
+       return ret;
+}
+
+/* ocfs2_xattr_get()
+ *
+ * Copy an extended attribute into the buffer provided.
+ * Buffer is NULL to compute the size of buffer required.
+ */
+int ocfs2_xattr_get(struct inode *inode,
+                   int name_index,
+                   const char *name,
+                   void *buffer,
+                   size_t buffer_size)
+{
+       int ret;
+       struct ocfs2_dinode *di = NULL;
+       struct buffer_head *di_bh = NULL;
+       struct ocfs2_inode_info *oi = OCFS2_I(inode);
+       struct ocfs2_xattr_search xis = {
+               .not_found = -ENODATA,
+       };
+       struct ocfs2_xattr_search xbs = {
+               .not_found = -ENODATA,
+       };
+
+       if (!ocfs2_supports_xattr(OCFS2_SB(inode->i_sb)))
+               return -EOPNOTSUPP;
+
+       if (!(oi->ip_dyn_features & OCFS2_HAS_XATTR_FL))
+               ret = -ENODATA;
+
+       ret = ocfs2_inode_lock(inode, &di_bh, 0);
+       if (ret < 0) {
+               mlog_errno(ret);
+               return ret;
+       }
+       xis.inode_bh = xbs.inode_bh = di_bh;
+       di = (struct ocfs2_dinode *)di_bh->b_data;
+
+       down_read(&oi->ip_xattr_sem);
+       ret = ocfs2_xattr_ibody_get(inode, name_index, name, buffer,
+                                   buffer_size, &xis);
+       if (ret == -ENODATA)
+               ret = ocfs2_xattr_block_get(inode, name_index, name, buffer,
+                                           buffer_size, &xbs);
+       up_read(&oi->ip_xattr_sem);
+       ocfs2_inode_unlock(inode, 0);
+
+       brelse(di_bh);
+
+       return ret;
+}
+
+static int __ocfs2_xattr_set_value_outside(struct inode *inode,
+                                          struct ocfs2_xattr_value_root *xv,
+                                          const void *value,
+                                          int value_len)
+{
+       int ret = 0, i, cp_len, credits;
+       u16 blocksize = inode->i_sb->s_blocksize;
+       u32 p_cluster, num_clusters;
+       u32 cpos = 0, bpc = ocfs2_clusters_to_blocks(inode->i_sb, 1);
+       u32 clusters = ocfs2_clusters_for_bytes(inode->i_sb, value_len);
+       u64 blkno;
+       struct buffer_head *bh = NULL;
+       handle_t *handle;
+
+       BUG_ON(clusters > le32_to_cpu(xv->xr_clusters));
+
+       credits = clusters * bpc;
+       handle = ocfs2_start_trans(OCFS2_SB(inode->i_sb), credits);
+       if (IS_ERR(handle)) {
+               ret = PTR_ERR(handle);
+               mlog_errno(ret);
+               goto out;
+       }
+
+       while (cpos < clusters) {
+               ret = ocfs2_xattr_get_clusters(inode, cpos, &p_cluster,
+                                              &num_clusters, &xv->xr_list);
+               if (ret) {
+                       mlog_errno(ret);
+                       goto out_commit;
+               }
+
+               blkno = ocfs2_clusters_to_blocks(inode->i_sb, p_cluster);
+
+               for (i = 0; i < num_clusters * bpc; i++, blkno++) {
+                       ret = ocfs2_read_block(inode, blkno, &bh);
+                       if (ret) {
+                               mlog_errno(ret);
+                               goto out_commit;
+                       }
+
+                       ret = ocfs2_journal_access(handle,
+                                                  inode,
+                                                  bh,
+                                                  OCFS2_JOURNAL_ACCESS_WRITE);
+                       if (ret < 0) {
+                               mlog_errno(ret);
+                               goto out_commit;
+                       }
+
+                       cp_len = value_len > blocksize ? blocksize : value_len;
+                       memcpy(bh->b_data, value, cp_len);
+                       value_len -= cp_len;
+                       value += cp_len;
+                       if (cp_len < blocksize)
+                               memset(bh->b_data + cp_len, 0,
+                                      blocksize - cp_len);
+
+                       ret = ocfs2_journal_dirty(handle, bh);
+                       if (ret < 0) {
+                               mlog_errno(ret);
+                               goto out_commit;
+                       }
+                       brelse(bh);
+                       bh = NULL;
+
+                       /*
+                        * XXX: do we need to empty all the following
+                        * blocks in this cluster?
+                        */
+                       if (!value_len)
+                               break;
+               }
+               cpos += num_clusters;
+       }
+out_commit:
+       ocfs2_commit_trans(OCFS2_SB(inode->i_sb), handle);
+out:
+       brelse(bh);
+
+       return ret;
+}
+
+static int ocfs2_xattr_cleanup(struct inode *inode,
+                              struct ocfs2_xattr_info *xi,
+                              struct ocfs2_xattr_search *xs,
+                              size_t offs)
+{
+       handle_t *handle = NULL;
+       int ret = 0;
+       size_t name_len = strlen(xi->name);
+       void *val = xs->base + offs;
+       size_t size = OCFS2_XATTR_SIZE(name_len) + OCFS2_XATTR_ROOT_SIZE;
+
+       handle = ocfs2_start_trans((OCFS2_SB(inode->i_sb)),
+                                  OCFS2_XATTR_BLOCK_UPDATE_CREDITS);
+       if (IS_ERR(handle)) {
+               ret = PTR_ERR(handle);
+               mlog_errno(ret);
+               goto out;
+       }
+       ret = ocfs2_journal_access(handle, inode, xs->xattr_bh,
+                                  OCFS2_JOURNAL_ACCESS_WRITE);
+       if (ret) {
+               mlog_errno(ret);
+               goto out_commit;
+       }
+       /* Decrease xattr count */
+       le16_add_cpu(&xs->header->xh_count, -1);
+       /* Remove the xattr entry and tree root which has already be set*/
+       memset((void *)xs->here, 0, sizeof(struct ocfs2_xattr_entry));
+       memset(val, 0, size);
+
+       ret = ocfs2_journal_dirty(handle, xs->xattr_bh);
+       if (ret < 0)
+               mlog_errno(ret);
+out_commit:
+       ocfs2_commit_trans(OCFS2_SB(inode->i_sb), handle);
+out:
+       return ret;
+}
+
+static int ocfs2_xattr_update_entry(struct inode *inode,
+                                   struct ocfs2_xattr_info *xi,
+                                   struct ocfs2_xattr_search *xs,
+                                   size_t offs)
+{
+       handle_t *handle = NULL;
+       int ret = 0;
+
+       handle = ocfs2_start_trans((OCFS2_SB(inode->i_sb)),
+                                  OCFS2_XATTR_BLOCK_UPDATE_CREDITS);
+       if (IS_ERR(handle)) {
+               ret = PTR_ERR(handle);
+               mlog_errno(ret);
+               goto out;
+       }
+       ret = ocfs2_journal_access(handle, inode, xs->xattr_bh,
+                                  OCFS2_JOURNAL_ACCESS_WRITE);
+       if (ret) {
+               mlog_errno(ret);
+               goto out_commit;
+       }
+
+       xs->here->xe_name_offset = cpu_to_le16(offs);
+       xs->here->xe_value_size = cpu_to_le64(xi->value_len);
+       if (xi->value_len <= OCFS2_XATTR_INLINE_SIZE)
+               ocfs2_xattr_set_local(xs->here, 1);
+       else
+               ocfs2_xattr_set_local(xs->here, 0);
+       ocfs2_xattr_hash_entry(inode, xs->header, xs->here);
+
+       ret = ocfs2_journal_dirty(handle, xs->xattr_bh);
+       if (ret < 0)
+               mlog_errno(ret);
+out_commit:
+       ocfs2_commit_trans(OCFS2_SB(inode->i_sb), handle);
+out:
+       return ret;
+}
+
+/*
+ * ocfs2_xattr_set_value_outside()
+ *
+ * Set large size value in B tree.
+ */
+static int ocfs2_xattr_set_value_outside(struct inode *inode,
+                                        struct ocfs2_xattr_info *xi,
+                                        struct ocfs2_xattr_search *xs,
+                                        size_t offs)
+{
+       size_t name_len = strlen(xi->name);
+       void *val = xs->base + offs;
+       struct ocfs2_xattr_value_root *xv = NULL;
+       size_t size = OCFS2_XATTR_SIZE(name_len) + OCFS2_XATTR_ROOT_SIZE;
+       int ret = 0;
+
+       memset(val, 0, size);
+       memcpy(val, xi->name, name_len);
+       xv = (struct ocfs2_xattr_value_root *)
+               (val + OCFS2_XATTR_SIZE(name_len));
+       xv->xr_clusters = 0;
+       xv->xr_last_eb_blk = 0;
+       xv->xr_list.l_tree_depth = 0;
+       xv->xr_list.l_count = cpu_to_le16(1);
+       xv->xr_list.l_next_free_rec = 0;
+
+       ret = ocfs2_xattr_value_truncate(inode, xs->xattr_bh, xv,
+                                        xi->value_len);
+       if (ret < 0) {
+               mlog_errno(ret);
+               return ret;
+       }
+       ret = __ocfs2_xattr_set_value_outside(inode, xv, xi->value,
+                                             xi->value_len);
+       if (ret < 0) {
+               mlog_errno(ret);
+               return ret;
+       }
+       ret = ocfs2_xattr_update_entry(inode, xi, xs, offs);
+       if (ret < 0)
+               mlog_errno(ret);
+
+       return ret;
+}
+
+/*
+ * ocfs2_xattr_set_entry_local()
+ *
+ * Set, replace or remove extended attribute in local.
+ */
+static void ocfs2_xattr_set_entry_local(struct inode *inode,
+                                       struct ocfs2_xattr_info *xi,
+                                       struct ocfs2_xattr_search *xs,
+                                       struct ocfs2_xattr_entry *last,
+                                       size_t min_offs)
+{
+       size_t name_len = strlen(xi->name);
+       int i;
+
+       if (xi->value && xs->not_found) {
+               /* Insert the new xattr entry. */
+               le16_add_cpu(&xs->header->xh_count, 1);
+               ocfs2_xattr_set_type(last, xi->name_index);
+               ocfs2_xattr_set_local(last, 1);
+               last->xe_name_len = name_len;
+       } else {
+               void *first_val;
+               void *val;
+               size_t offs, size;
+
+               first_val = xs->base + min_offs;
+               offs = le16_to_cpu(xs->here->xe_name_offset);
+               val = xs->base + offs;
+
+               if (le64_to_cpu(xs->here->xe_value_size) >
+                   OCFS2_XATTR_INLINE_SIZE)
+                       size = OCFS2_XATTR_SIZE(name_len) +
+                               OCFS2_XATTR_ROOT_SIZE;
+               else
+                       size = OCFS2_XATTR_SIZE(name_len) +
+                       OCFS2_XATTR_SIZE(le64_to_cpu(xs->here->xe_value_size));
+
+               if (xi->value && size == OCFS2_XATTR_SIZE(name_len) +
+                               OCFS2_XATTR_SIZE(xi->value_len)) {
+                       /* The old and the new value have the
+                          same size. Just replace the value. */
+                       ocfs2_xattr_set_local(xs->here, 1);
+                       xs->here->xe_value_size = cpu_to_le64(xi->value_len);
+                       /* Clear value bytes. */
+                       memset(val + OCFS2_XATTR_SIZE(name_len),
+                              0,
+                              OCFS2_XATTR_SIZE(xi->value_len));
+                       memcpy(val + OCFS2_XATTR_SIZE(name_len),
+                              xi->value,
+                              xi->value_len);
+                       return;
+               }
+               /* Remove the old name+value. */
+               memmove(first_val + size, first_val, val - first_val);
+               memset(first_val, 0, size);
+               xs->here->xe_name_hash = 0;
+               xs->here->xe_name_offset = 0;
+               ocfs2_xattr_set_local(xs->here, 1);
+               xs->here->xe_value_size = 0;
+
+               min_offs += size;
+
+               /* Adjust all value offsets. */
+               last = xs->header->xh_entries;
+               for (i = 0 ; i < le16_to_cpu(xs->header->xh_count); i++) {
+                       size_t o = le16_to_cpu(last->xe_name_offset);
+
+                       if (o < offs)
+                               last->xe_name_offset = cpu_to_le16(o + size);
+                       last += 1;
+               }
+
+               if (!xi->value) {
+                       /* Remove the old entry. */
+                       last -= 1;
+                       memmove(xs->here, xs->here + 1,
+                               (void *)last - (void *)xs->here);
+                       memset(last, 0, sizeof(struct ocfs2_xattr_entry));
+                       le16_add_cpu(&xs->header->xh_count, -1);
+               }
+       }
+       if (xi->value) {
+               /* Insert the new name+value. */
+               size_t size = OCFS2_XATTR_SIZE(name_len) +
+                               OCFS2_XATTR_SIZE(xi->value_len);
+               void *val = xs->base + min_offs - size;
+
+               xs->here->xe_name_offset = cpu_to_le16(min_offs - size);
+               memset(val, 0, size);
+               memcpy(val, xi->name, name_len);
+               memcpy(val + OCFS2_XATTR_SIZE(name_len),
+                      xi->value,
+                      xi->value_len);
+               xs->here->xe_value_size = cpu_to_le64(xi->value_len);
+               ocfs2_xattr_set_local(xs->here, 1);
+               ocfs2_xattr_hash_entry(inode, xs->header, xs->here);
+       }
+
+       return;
+}
+
+/*
+ * ocfs2_xattr_set_entry()
+ *
+ * Set extended attribute entry into inode or block.
+ *
+ * If extended attribute value size > OCFS2_XATTR_INLINE_SIZE,
+ * We first insert tree root(ocfs2_xattr_value_root) with set_entry_local(),
+ * then set value in B tree with set_value_outside().
+ */
+static int ocfs2_xattr_set_entry(struct inode *inode,
+                                struct ocfs2_xattr_info *xi,
+                                struct ocfs2_xattr_search *xs,
+                                int flag)
+{
+       struct ocfs2_xattr_entry *last;
+       struct ocfs2_inode_info *oi = OCFS2_I(inode);
+       struct ocfs2_dinode *di = (struct ocfs2_dinode *)xs->inode_bh->b_data;
+       size_t min_offs = xs->end - xs->base, name_len = strlen(xi->name);
+       size_t size_l = 0;
+       handle_t *handle = NULL;
+       int free, i, ret;
+       struct ocfs2_xattr_info xi_l = {
+               .name_index = xi->name_index,
+               .name = xi->name,
+               .value = xi->value,
+               .value_len = xi->value_len,
+       };
+
+       /* Compute min_offs, last and free space. */
+       last = xs->header->xh_entries;
+
+       for (i = 0 ; i < le16_to_cpu(xs->header->xh_count); i++) {
+               size_t offs = le16_to_cpu(last->xe_name_offset);
+               if (offs < min_offs)
+                       min_offs = offs;
+               last += 1;
+       }
+
+       free = min_offs - ((void *)last - xs->base) - sizeof(__u32);
+       if (free < 0)
+               return -EFAULT;
+
+       if (!xs->not_found) {
+               size_t size = 0;
+               if (ocfs2_xattr_is_local(xs->here))
+                       size = OCFS2_XATTR_SIZE(name_len) +
+                       OCFS2_XATTR_SIZE(le64_to_cpu(xs->here->xe_value_size));
+               else
+                       size = OCFS2_XATTR_SIZE(name_len) +
+                               OCFS2_XATTR_ROOT_SIZE;
+               free += (size + sizeof(struct ocfs2_xattr_entry));
+       }
+       /* Check free space in inode or block */
+       if (xi->value && xi->value_len > OCFS2_XATTR_INLINE_SIZE) {
+               if (free < sizeof(struct ocfs2_xattr_entry) +
+                          OCFS2_XATTR_SIZE(name_len) +
+                          OCFS2_XATTR_ROOT_SIZE) {
+                       ret = -ENOSPC;
+                       goto out;
+               }
+               size_l = OCFS2_XATTR_SIZE(name_len) + OCFS2_XATTR_ROOT_SIZE;
+               xi_l.value = (void *)&def_xv;
+               xi_l.value_len = OCFS2_XATTR_ROOT_SIZE;
+       } else if (xi->value) {
+               if (free < sizeof(struct ocfs2_xattr_entry) +
+                          OCFS2_XATTR_SIZE(name_len) +
+                          OCFS2_XATTR_SIZE(xi->value_len)) {
+                       ret = -ENOSPC;
+                       goto out;
+               }
+       }
+
+       if (!xs->not_found) {
+               /* For existing extended attribute */
+               size_t size = OCFS2_XATTR_SIZE(name_len) +
+                       OCFS2_XATTR_SIZE(le64_to_cpu(xs->here->xe_value_size));
+               size_t offs = le16_to_cpu(xs->here->xe_name_offset);
+               void *val = xs->base + offs;
+
+               if (ocfs2_xattr_is_local(xs->here) && size == size_l) {
+                       /* Replace existing local xattr with tree root */
+                       ret = ocfs2_xattr_set_value_outside(inode, xi, xs,
+                                                           offs);
+                       if (ret < 0)
+                               mlog_errno(ret);
+                       goto out;
+               } else if (!ocfs2_xattr_is_local(xs->here)) {
+                       /* For existing xattr which has value outside */
+                       struct ocfs2_xattr_value_root *xv = NULL;
+                       xv = (struct ocfs2_xattr_value_root *)(val +
+                               OCFS2_XATTR_SIZE(name_len));
+
+                       if (xi->value_len > OCFS2_XATTR_INLINE_SIZE) {
+                               /*
+                                * If new value need set outside also,
+                                * first truncate old value to new value,
+                                * then set new value with set_value_outside().
+                                */
+                               ret = ocfs2_xattr_value_truncate(inode,
+                                                                xs->xattr_bh,
+                                                                xv,
+                                                                xi->value_len);
+                               if (ret < 0) {
+                                       mlog_errno(ret);
+                                       goto out;
+                               }
+
+                               ret = __ocfs2_xattr_set_value_outside(inode,
+                                                               xv,
+                                                               xi->value,
+                                                               xi->value_len);
+                               if (ret < 0) {
+                                       mlog_errno(ret);
+                                       goto out;
+                               }
+
+                               ret = ocfs2_xattr_update_entry(inode,
+                                                              xi,
+                                                              xs,
+                                                              offs);
+                               if (ret < 0)
+                                       mlog_errno(ret);
+                               goto out;
+                       } else {
+                               /*
+                                * If new value need set in local,
+                                * just trucate old value to zero.
+                                */
+                                ret = ocfs2_xattr_value_truncate(inode,
+                                                                xs->xattr_bh,
+                                                                xv,
+                                                                0);
+                               if (ret < 0)
+                                       mlog_errno(ret);
+                       }
+               }
+       }
+
+       handle = ocfs2_start_trans((OCFS2_SB(inode->i_sb)),
+                                  OCFS2_INODE_UPDATE_CREDITS);
+       if (IS_ERR(handle)) {
+               ret = PTR_ERR(handle);
+               mlog_errno(ret);
+               goto out;
+       }
+
+       ret = ocfs2_journal_access(handle, inode, xs->inode_bh,
+                                  OCFS2_JOURNAL_ACCESS_WRITE);
+       if (ret) {
+               mlog_errno(ret);
+               goto out_commit;
+       }
+
+       if (!(flag & OCFS2_INLINE_XATTR_FL)) {
+               /* set extended attribute in external block. */
+               ret = ocfs2_extend_trans(handle,
+                                        OCFS2_INODE_UPDATE_CREDITS +
+                                        OCFS2_XATTR_BLOCK_UPDATE_CREDITS);
+               if (ret) {
+                       mlog_errno(ret);
+                       goto out_commit;
+               }
+               ret = ocfs2_journal_access(handle, inode, xs->xattr_bh,
+                                          OCFS2_JOURNAL_ACCESS_WRITE);
+               if (ret) {
+                       mlog_errno(ret);
+                       goto out_commit;
+               }
+       }
+
+       /*
+        * Set value in local, include set tree root in local.
+        * This is the first step for value size >INLINE_SIZE.
+        */
+       ocfs2_xattr_set_entry_local(inode, &xi_l, xs, last, min_offs);
+
+       if (!(flag & OCFS2_INLINE_XATTR_FL)) {
+               ret = ocfs2_journal_dirty(handle, xs->xattr_bh);
+               if (ret < 0) {
+                       mlog_errno(ret);
+                       goto out_commit;
+               }
+       }
+
+       if (!(oi->ip_dyn_features & OCFS2_INLINE_XATTR_FL) &&
+           (flag & OCFS2_INLINE_XATTR_FL)) {
+               struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
+               unsigned int xattrsize = osb->s_xattr_inline_size;
+
+               /*
+                * Adjust extent record count or inline data size
+                * to reserve space for extended attribute.
+                */
+               if (oi->ip_dyn_features & OCFS2_INLINE_DATA_FL) {
+                       struct ocfs2_inline_data *idata = &di->id2.i_data;
+                       le16_add_cpu(&idata->id_count, -xattrsize);
+               } else if (!(ocfs2_inode_is_fast_symlink(inode))) {
+                       struct ocfs2_extent_list *el = &di->id2.i_list;
+                       le16_add_cpu(&el->l_count, -(xattrsize /
+                                       sizeof(struct ocfs2_extent_rec)));
+               }
+               di->i_xattr_inline_size = cpu_to_le16(xattrsize);
+       }
+       /* Update xattr flag */
+       spin_lock(&oi->ip_lock);
+       oi->ip_dyn_features |= flag;
+       di->i_dyn_features = cpu_to_le16(oi->ip_dyn_features);
+       spin_unlock(&oi->ip_lock);
+       /* Update inode ctime */
+       inode->i_ctime = CURRENT_TIME;
+       di->i_ctime = cpu_to_le64(inode->i_ctime.tv_sec);
+       di->i_ctime_nsec = cpu_to_le32(inode->i_ctime.tv_nsec);
+
+       ret = ocfs2_journal_dirty(handle, xs->inode_bh);
+       if (ret < 0)
+               mlog_errno(ret);
+
+out_commit:
+       ocfs2_commit_trans(OCFS2_SB(inode->i_sb), handle);
+
+       if (!ret && xi->value_len > OCFS2_XATTR_INLINE_SIZE) {
+               /*
+                * Set value outside in B tree.
+                * This is the second step for value size > INLINE_SIZE.
+                */
+               size_t offs = le16_to_cpu(xs->here->xe_name_offset);
+               ret = ocfs2_xattr_set_value_outside(inode, xi, xs, offs);
+               if (ret < 0) {
+                       int ret2;
+
+                       mlog_errno(ret);
+                       /*
+                        * If set value outside failed, we have to clean
+                        * the junk tree root we have already set in local.
+                        */
+                       ret2 = ocfs2_xattr_cleanup(inode, xi, xs, offs);
+                       if (ret2 < 0)
+                               mlog_errno(ret2);
+               }
+       }
+out:
+       return ret;
+
+}
+
+static int ocfs2_remove_value_outside(struct inode*inode,
+                                     struct buffer_head *bh,
+                                     struct ocfs2_xattr_header *header)
+{
+       int ret = 0, i;
+
+       for (i = 0; i < le16_to_cpu(header->xh_count); i++) {
+               struct ocfs2_xattr_entry *entry = &header->xh_entries[i];
+
+               if (!ocfs2_xattr_is_local(entry)) {
+                       struct ocfs2_xattr_value_root *xv;
+                       void *val;
+
+                       val = (void *)header +
+                               le16_to_cpu(entry->xe_name_offset);
+                       xv = (struct ocfs2_xattr_value_root *)
+                               (val + OCFS2_XATTR_SIZE(entry->xe_name_len));
+                       ret = ocfs2_xattr_value_truncate(inode, bh, xv, 0);
+                       if (ret < 0) {
+                               mlog_errno(ret);
+                               return ret;
+                       }
+               }
+       }
+
+       return ret;
+}
+
+static int ocfs2_xattr_ibody_remove(struct inode *inode,
+                                   struct buffer_head *di_bh)
+{
+
+       struct ocfs2_dinode *di = (struct ocfs2_dinode *)di_bh->b_data;
+       struct ocfs2_xattr_header *header;
+       int ret;
+
+       header = (struct ocfs2_xattr_header *)
+                ((void *)di + inode->i_sb->s_blocksize -
+                le16_to_cpu(di->i_xattr_inline_size));
+
+       ret = ocfs2_remove_value_outside(inode, di_bh, header);
+
+       return ret;
+}
+
+static int ocfs2_xattr_block_remove(struct inode *inode,
+                                   struct buffer_head *blk_bh)
+{
+       struct ocfs2_xattr_block *xb;
+       int ret = 0;
+
+       xb = (struct ocfs2_xattr_block *)blk_bh->b_data;
+       if (!(le16_to_cpu(xb->xb_flags) & OCFS2_XATTR_INDEXED)) {
+               struct ocfs2_xattr_header *header = &(xb->xb_attrs.xb_header);
+               ret = ocfs2_remove_value_outside(inode, blk_bh, header);
+       } else
+               ret = ocfs2_delete_xattr_index_block(inode, blk_bh);
+
+       return ret;
+}
+
+static int ocfs2_xattr_free_block(struct inode *inode,
+                                 u64 block)
+{
+       struct inode *xb_alloc_inode;
+       struct buffer_head *xb_alloc_bh = NULL;
+       struct buffer_head *blk_bh = NULL;
+       struct ocfs2_xattr_block *xb;
+       struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
+       handle_t *handle;
+       int ret = 0;
+       u64 blk, bg_blkno;
+       u16 bit;
+
+       ret = ocfs2_read_block(inode, block, &blk_bh);
+       if (ret < 0) {
+               mlog_errno(ret);
+               goto out;
+       }
+
+       /*Verify the signature of xattr block*/
+       if (memcmp((void *)blk_bh->b_data, OCFS2_XATTR_BLOCK_SIGNATURE,
+                  strlen(OCFS2_XATTR_BLOCK_SIGNATURE))) {
+               ret = -EFAULT;
+               goto out;
+       }
+
+       ret = ocfs2_xattr_block_remove(inode, blk_bh);
+       if (ret < 0) {
+               mlog_errno(ret);
+               goto out;
+       }
+
+       xb = (struct ocfs2_xattr_block *)blk_bh->b_data;
+       blk = le64_to_cpu(xb->xb_blkno);
+       bit = le16_to_cpu(xb->xb_suballoc_bit);
+       bg_blkno = ocfs2_which_suballoc_group(blk, bit);
+
+       xb_alloc_inode = ocfs2_get_system_file_inode(osb,
+                               EXTENT_ALLOC_SYSTEM_INODE,
+                               le16_to_cpu(xb->xb_suballoc_slot));
+       if (!xb_alloc_inode) {
+               ret = -ENOMEM;
+               mlog_errno(ret);
+               goto out;
+       }
+       mutex_lock(&xb_alloc_inode->i_mutex);
+
+       ret = ocfs2_inode_lock(xb_alloc_inode, &xb_alloc_bh, 1);
+       if (ret < 0) {
+               mlog_errno(ret);
+               goto out_mutex;
+       }
+
+       handle = ocfs2_start_trans(osb, OCFS2_SUBALLOC_FREE);
+       if (IS_ERR(handle)) {
+               ret = PTR_ERR(handle);
+               mlog_errno(ret);
+               goto out_unlock;
+       }
+
+       ret = ocfs2_free_suballoc_bits(handle, xb_alloc_inode, xb_alloc_bh,
+                                      bit, bg_blkno, 1);
+       if (ret < 0)
+               mlog_errno(ret);
+
+       ocfs2_commit_trans(osb, handle);
+out_unlock:
+       ocfs2_inode_unlock(xb_alloc_inode, 1);
+       brelse(xb_alloc_bh);
+out_mutex:
+       mutex_unlock(&xb_alloc_inode->i_mutex);
+       iput(xb_alloc_inode);
+out:
+       brelse(blk_bh);
+       return ret;
+}
+
+/*
+ * ocfs2_xattr_remove()
+ *
+ * Free extended attribute resources associated with this inode.
+ */
+int ocfs2_xattr_remove(struct inode *inode, struct buffer_head *di_bh)
+{
+       struct ocfs2_inode_info *oi = OCFS2_I(inode);
+       struct ocfs2_dinode *di = (struct ocfs2_dinode *)di_bh->b_data;
+       handle_t *handle;
+       int ret;
+
+       if (!ocfs2_supports_xattr(OCFS2_SB(inode->i_sb)))
+               return 0;
+
+       if (!(oi->ip_dyn_features & OCFS2_HAS_XATTR_FL))
+               return 0;
+
+       if (oi->ip_dyn_features & OCFS2_INLINE_XATTR_FL) {
+               ret = ocfs2_xattr_ibody_remove(inode, di_bh);
+               if (ret < 0) {
+                       mlog_errno(ret);
+                       goto out;
+               }
+       }
+
+       if (di->i_xattr_loc) {
+               ret = ocfs2_xattr_free_block(inode,
+                                            le64_to_cpu(di->i_xattr_loc));
+               if (ret < 0) {
+                       mlog_errno(ret);
+                       goto out;
+               }
+       }
+
+       handle = ocfs2_start_trans((OCFS2_SB(inode->i_sb)),
+                                  OCFS2_INODE_UPDATE_CREDITS);
+       if (IS_ERR(handle)) {
+               ret = PTR_ERR(handle);
+               mlog_errno(ret);
+               goto out;
+       }
+       ret = ocfs2_journal_access(handle, inode, di_bh,
+                                  OCFS2_JOURNAL_ACCESS_WRITE);
+       if (ret) {
+               mlog_errno(ret);
+               goto out_commit;
+       }
+
+       di->i_xattr_loc = 0;
+
+       spin_lock(&oi->ip_lock);
+       oi->ip_dyn_features &= ~(OCFS2_INLINE_XATTR_FL | OCFS2_HAS_XATTR_FL);
+       di->i_dyn_features = cpu_to_le16(oi->ip_dyn_features);
+       spin_unlock(&oi->ip_lock);
+
+       ret = ocfs2_journal_dirty(handle, di_bh);
+       if (ret < 0)
+               mlog_errno(ret);
+out_commit:
+       ocfs2_commit_trans(OCFS2_SB(inode->i_sb), handle);
+out:
+       return ret;
+}
+
+static int ocfs2_xattr_has_space_inline(struct inode *inode,
+                                       struct ocfs2_dinode *di)
+{
+       struct ocfs2_inode_info *oi = OCFS2_I(inode);
+       unsigned int xattrsize = OCFS2_SB(inode->i_sb)->s_xattr_inline_size;
+       int free;
+
+       if (xattrsize < OCFS2_MIN_XATTR_INLINE_SIZE)
+               return 0;
+
+       if (oi->ip_dyn_features & OCFS2_INLINE_DATA_FL) {
+               struct ocfs2_inline_data *idata = &di->id2.i_data;
+               free = le16_to_cpu(idata->id_count) - le64_to_cpu(di->i_size);
+       } else if (ocfs2_inode_is_fast_symlink(inode)) {
+               free = ocfs2_fast_symlink_chars(inode->i_sb) -
+                       le64_to_cpu(di->i_size);
+       } else {
+               struct ocfs2_extent_list *el = &di->id2.i_list;
+               free = (le16_to_cpu(el->l_count) -
+                       le16_to_cpu(el->l_next_free_rec)) *
+                       sizeof(struct ocfs2_extent_rec);
+       }
+       if (free >= xattrsize)
+               return 1;
+
+       return 0;
+}
+
+/*
+ * ocfs2_xattr_ibody_find()
+ *
+ * Find extended attribute in inode block and
+ * fill search info into struct ocfs2_xattr_search.
+ */
+static int ocfs2_xattr_ibody_find(struct inode *inode,
+                                 int name_index,
+                                 const char *name,
+                                 struct ocfs2_xattr_search *xs)
+{
+       struct ocfs2_inode_info *oi = OCFS2_I(inode);
+       struct ocfs2_dinode *di = (struct ocfs2_dinode *)xs->inode_bh->b_data;
+       int ret;
+       int has_space = 0;
+
+       if (inode->i_sb->s_blocksize == OCFS2_MIN_BLOCKSIZE)
+               return 0;
+
+       if (!(oi->ip_dyn_features & OCFS2_INLINE_XATTR_FL)) {
+               down_read(&oi->ip_alloc_sem);
+               has_space = ocfs2_xattr_has_space_inline(inode, di);
+               up_read(&oi->ip_alloc_sem);
+               if (!has_space)
+                       return 0;
+       }
+
+       xs->xattr_bh = xs->inode_bh;
+       xs->end = (void *)di + inode->i_sb->s_blocksize;
+       if (oi->ip_dyn_features & OCFS2_INLINE_XATTR_FL)
+               xs->header = (struct ocfs2_xattr_header *)
+                       (xs->end - le16_to_cpu(di->i_xattr_inline_size));
+       else
+               xs->header = (struct ocfs2_xattr_header *)
+                       (xs->end - OCFS2_SB(inode->i_sb)->s_xattr_inline_size);
+       xs->base = (void *)xs->header;
+       xs->here = xs->header->xh_entries;
+
+       /* Find the named attribute. */
+       if (oi->ip_dyn_features & OCFS2_INLINE_XATTR_FL) {
+               ret = ocfs2_xattr_find_entry(name_index, name, xs);
+               if (ret && ret != -ENODATA)
+                       return ret;
+               xs->not_found = ret;
+       }
+
+       return 0;
+}
+
+/*
+ * ocfs2_xattr_ibody_set()
+ *
+ * Set, replace or remove an extended attribute into inode block.
+ *
+ */
+static int ocfs2_xattr_ibody_set(struct inode *inode,
+                                struct ocfs2_xattr_info *xi,
+                                struct ocfs2_xattr_search *xs)
+{
+       struct ocfs2_inode_info *oi = OCFS2_I(inode);
+       struct ocfs2_dinode *di = (struct ocfs2_dinode *)xs->inode_bh->b_data;
+       int ret;
+
+       if (inode->i_sb->s_blocksize == OCFS2_MIN_BLOCKSIZE)
+               return -ENOSPC;
+
+       down_write(&oi->ip_alloc_sem);
+       if (!(oi->ip_dyn_features & OCFS2_INLINE_XATTR_FL)) {
+               if (!ocfs2_xattr_has_space_inline(inode, di)) {
+                       ret = -ENOSPC;
+                       goto out;
+               }
+       }
+
+       ret = ocfs2_xattr_set_entry(inode, xi, xs,
+                               (OCFS2_INLINE_XATTR_FL | OCFS2_HAS_XATTR_FL));
+out:
+       up_write(&oi->ip_alloc_sem);
+
+       return ret;
+}
+
+/*
+ * ocfs2_xattr_block_find()
+ *
+ * Find extended attribute in external block and
+ * fill search info into struct ocfs2_xattr_search.
+ */
+static int ocfs2_xattr_block_find(struct inode *inode,
+                                 int name_index,
+                                 const char *name,
+                                 struct ocfs2_xattr_search *xs)
+{
+       struct ocfs2_dinode *di = (struct ocfs2_dinode *)xs->inode_bh->b_data;
+       struct buffer_head *blk_bh = NULL;
+       struct ocfs2_xattr_block *xb;
+       int ret = 0;
+
+       if (!di->i_xattr_loc)
+               return ret;
+
+       ret = ocfs2_read_block(inode, le64_to_cpu(di->i_xattr_loc), &blk_bh);
+       if (ret < 0) {
+               mlog_errno(ret);
+               return ret;
+       }
+       /*Verify the signature of xattr block*/
+       if (memcmp((void *)blk_bh->b_data, OCFS2_XATTR_BLOCK_SIGNATURE,
+                  strlen(OCFS2_XATTR_BLOCK_SIGNATURE))) {
+                       ret = -EFAULT;
+                       goto cleanup;
+       }
+
+       xs->xattr_bh = blk_bh;
+       xb = (struct ocfs2_xattr_block *)blk_bh->b_data;
+
+       if (!(le16_to_cpu(xb->xb_flags) & OCFS2_XATTR_INDEXED)) {
+               xs->header = &xb->xb_attrs.xb_header;
+               xs->base = (void *)xs->header;
+               xs->end = (void *)(blk_bh->b_data) + blk_bh->b_size;
+               xs->here = xs->header->xh_entries;
+
+               ret = ocfs2_xattr_find_entry(name_index, name, xs);
+       } else
+               ret = ocfs2_xattr_index_block_find(inode, blk_bh,
+                                                  name_index,
+                                                  name, xs);
+
+       if (ret && ret != -ENODATA) {
+               xs->xattr_bh = NULL;
+               goto cleanup;
+       }
+       xs->not_found = ret;
+       return 0;
+cleanup:
+       brelse(blk_bh);
+
+       return ret;
+}
+
+/*
+ * When all the xattrs are deleted from index btree, the ocfs2_xattr_tree
+ * will be erased and ocfs2_xattr_block will have its ocfs2_xattr_header
+ * re-initialized.
+ */
+static int ocfs2_restore_xattr_block(struct inode *inode,
+                                    struct ocfs2_xattr_search *xs)
+{
+       int ret;
+       handle_t *handle;
+       struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
+       struct ocfs2_xattr_block *xb =
+               (struct ocfs2_xattr_block *)xs->xattr_bh->b_data;
+       struct ocfs2_extent_list *el = &xb->xb_attrs.xb_root.xt_list;
+       u16 xb_flags = le16_to_cpu(xb->xb_flags);
+
+       BUG_ON(!(xb_flags & OCFS2_XATTR_INDEXED) ||
+               le16_to_cpu(el->l_next_free_rec) != 0);
+
+       handle = ocfs2_start_trans(osb, OCFS2_XATTR_BLOCK_UPDATE_CREDITS);
+       if (IS_ERR(handle)) {
+               ret = PTR_ERR(handle);
+               handle = NULL;
+               goto out;
+       }
+
+       ret = ocfs2_journal_access(handle, inode, xs->xattr_bh,
+                                  OCFS2_JOURNAL_ACCESS_WRITE);
+       if (ret < 0) {
+               mlog_errno(ret);
+               goto out_commit;
+       }
+
+       memset(&xb->xb_attrs, 0, inode->i_sb->s_blocksize -
+              offsetof(struct ocfs2_xattr_block, xb_attrs));
+
+       xb->xb_flags = cpu_to_le16(xb_flags & ~OCFS2_XATTR_INDEXED);
+
+       ocfs2_journal_dirty(handle, xs->xattr_bh);
+
+out_commit:
+       ocfs2_commit_trans(osb, handle);
+out:
+       return ret;
+}
+
+/*
+ * ocfs2_xattr_block_set()
+ *
+ * Set, replace or remove an extended attribute into external block.
+ *
+ */
+static int ocfs2_xattr_block_set(struct inode *inode,
+                                struct ocfs2_xattr_info *xi,
+                                struct ocfs2_xattr_search *xs)
+{
+       struct buffer_head *new_bh = NULL;
+       struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
+       struct ocfs2_dinode *di =  (struct ocfs2_dinode *)xs->inode_bh->b_data;
+       struct ocfs2_alloc_context *meta_ac = NULL;
+       handle_t *handle = NULL;
+       struct ocfs2_xattr_block *xblk = NULL;
+       u16 suballoc_bit_start;
+       u32 num_got;
+       u64 first_blkno;
+       int ret;
+
+       if (!xs->xattr_bh) {
+               /*
+                * Alloc one external block for extended attribute
+                * outside of inode.
+                */
+               ret = ocfs2_reserve_new_metadata_blocks(osb, 1, &meta_ac);
+               if (ret < 0) {
+                       mlog_errno(ret);
+                       goto out;
+               }
+               handle = ocfs2_start_trans(osb,
+                                          OCFS2_XATTR_BLOCK_CREATE_CREDITS);
+               if (IS_ERR(handle)) {
+                       ret = PTR_ERR(handle);
+                       mlog_errno(ret);
+                       goto out;
+               }
+               ret = ocfs2_journal_access(handle, inode, xs->inode_bh,
+                                          OCFS2_JOURNAL_ACCESS_CREATE);
+               if (ret < 0) {
+                       mlog_errno(ret);
+                       goto out_commit;
+               }
+
+               ret = ocfs2_claim_metadata(osb, handle, meta_ac, 1,
+                                          &suballoc_bit_start, &num_got,
+                                          &first_blkno);
+               if (ret < 0) {
+                       mlog_errno(ret);
+                       goto out_commit;
+               }
+
+               new_bh = sb_getblk(inode->i_sb, first_blkno);
+               ocfs2_set_new_buffer_uptodate(inode, new_bh);
+
+               ret = ocfs2_journal_access(handle, inode, new_bh,
+                                          OCFS2_JOURNAL_ACCESS_CREATE);
+               if (ret < 0) {
+                       mlog_errno(ret);
+                       goto out_commit;
+               }
+
+               /* Initialize ocfs2_xattr_block */
+               xs->xattr_bh = new_bh;
+               xblk = (struct ocfs2_xattr_block *)new_bh->b_data;
+               memset(xblk, 0, inode->i_sb->s_blocksize);
+               strcpy((void *)xblk, OCFS2_XATTR_BLOCK_SIGNATURE);
+               xblk->xb_suballoc_slot = cpu_to_le16(osb->slot_num);
+               xblk->xb_suballoc_bit = cpu_to_le16(suballoc_bit_start);
+               xblk->xb_fs_generation = cpu_to_le32(osb->fs_generation);
+               xblk->xb_blkno = cpu_to_le64(first_blkno);
+
+               xs->header = &xblk->xb_attrs.xb_header;
+               xs->base = (void *)xs->header;
+               xs->end = (void *)xblk + inode->i_sb->s_blocksize;
+               xs->here = xs->header->xh_entries;
+
+
+               ret = ocfs2_journal_dirty(handle, new_bh);
+               if (ret < 0) {
+                       mlog_errno(ret);
+                       goto out_commit;
+               }
+               di->i_xattr_loc = cpu_to_le64(first_blkno);
+               ret = ocfs2_journal_dirty(handle, xs->inode_bh);
+               if (ret < 0)
+                       mlog_errno(ret);
+out_commit:
+               ocfs2_commit_trans(osb, handle);
+out:
+               if (meta_ac)
+                       ocfs2_free_alloc_context(meta_ac);
+               if (ret < 0)
+                       return ret;
+       } else
+               xblk = (struct ocfs2_xattr_block *)xs->xattr_bh->b_data;
+
+       if (!(le16_to_cpu(xblk->xb_flags) & OCFS2_XATTR_INDEXED)) {
+               /* Set extended attribute into external block */
+               ret = ocfs2_xattr_set_entry(inode, xi, xs, OCFS2_HAS_XATTR_FL);
+               if (!ret || ret != -ENOSPC)
+                       goto end;
+
+               ret = ocfs2_xattr_create_index_block(inode, xs);
+               if (ret)
+                       goto end;
+       }
+
+       ret = ocfs2_xattr_set_entry_index_block(inode, xi, xs);
+       if (!ret && xblk->xb_attrs.xb_root.xt_list.l_next_free_rec == 0)
+               ret = ocfs2_restore_xattr_block(inode, xs);
+
+end:
+
+       return ret;
+}
+
+/*
+ * ocfs2_xattr_set()
+ *
+ * Set, replace or remove an extended attribute for this inode.
+ * value is NULL to remove an existing extended attribute, else either
+ * create or replace an extended attribute.
+ */
+int ocfs2_xattr_set(struct inode *inode,
+                   int name_index,
+                   const char *name,
+                   const void *value,
+                   size_t value_len,
+                   int flags)
+{
+       struct buffer_head *di_bh = NULL;
+       struct ocfs2_dinode *di;
+       int ret;
+       u16 i, blk_per_bucket = ocfs2_blocks_per_xattr_bucket(inode->i_sb);
+
+       struct ocfs2_xattr_info xi = {
+               .name_index = name_index,
+               .name = name,
+               .value = value,
+               .value_len = value_len,
+       };
+
+       struct ocfs2_xattr_search xis = {
+               .not_found = -ENODATA,
+       };
+
+       struct ocfs2_xattr_search xbs = {
+               .not_found = -ENODATA,
+       };
+
+       if (!ocfs2_supports_xattr(OCFS2_SB(inode->i_sb)))
+               return -EOPNOTSUPP;
+
+       ret = ocfs2_inode_lock(inode, &di_bh, 1);
+       if (ret < 0) {
+               mlog_errno(ret);
+               return ret;
+       }
+       xis.inode_bh = xbs.inode_bh = di_bh;
+       di = (struct ocfs2_dinode *)di_bh->b_data;
+
+       down_write(&OCFS2_I(inode)->ip_xattr_sem);
+       /*
+        * Scan inode and external block to find the same name
+        * extended attribute and collect search infomation.
+        */
+       ret = ocfs2_xattr_ibody_find(inode, name_index, name, &xis);
+       if (ret)
+               goto cleanup;
+       if (xis.not_found) {
+               ret = ocfs2_xattr_block_find(inode, name_index, name, &xbs);
+               if (ret)
+                       goto cleanup;
+       }
+
+       if (xis.not_found && xbs.not_found) {
+               ret = -ENODATA;
+               if (flags & XATTR_REPLACE)
+                       goto cleanup;
+               ret = 0;
+               if (!value)
+                       goto cleanup;
+       } else {
+               ret = -EEXIST;
+               if (flags & XATTR_CREATE)
+                       goto cleanup;
+       }
+
+       if (!value) {
+               /* Remove existing extended attribute */
+               if (!xis.not_found)
+                       ret = ocfs2_xattr_ibody_set(inode, &xi, &xis);
+               else if (!xbs.not_found)
+                       ret = ocfs2_xattr_block_set(inode, &xi, &xbs);
+       } else {
+               /* We always try to set extended attribute into inode first*/
+               ret = ocfs2_xattr_ibody_set(inode, &xi, &xis);
+               if (!ret && !xbs.not_found) {
+                       /*
+                        * If succeed and that extended attribute existing in
+                        * external block, then we will remove it.
+                        */
+                       xi.value = NULL;
+                       xi.value_len = 0;
+                       ret = ocfs2_xattr_block_set(inode, &xi, &xbs);
+               } else if (ret == -ENOSPC) {
+                       if (di->i_xattr_loc && !xbs.xattr_bh) {
+                               ret = ocfs2_xattr_block_find(inode, name_index,
+                                                            name, &xbs);
+                               if (ret)
+                                       goto cleanup;
+                       }
+                       /*
+                        * If no space in inode, we will set extended attribute
+                        * into external block.
+                        */
+                       ret = ocfs2_xattr_block_set(inode, &xi, &xbs);
+                       if (ret)
+                               goto cleanup;
+                       if (!xis.not_found) {
+                               /*
+                                * If succeed and that extended attribute
+                                * existing in inode, we will remove it.
+                                */
+                               xi.value = NULL;
+                               xi.value_len = 0;
+                               ret = ocfs2_xattr_ibody_set(inode, &xi, &xis);
+                       }
+               }
+       }
+cleanup:
+       up_write(&OCFS2_I(inode)->ip_xattr_sem);
+       ocfs2_inode_unlock(inode, 1);
+       brelse(di_bh);
+       brelse(xbs.xattr_bh);
+       for (i = 0; i < blk_per_bucket; i++)
+               brelse(xbs.bucket.bhs[i]);
+
+       return ret;
+}
+
+/*
+ * Find the xattr extent rec which may contains name_hash.
+ * e_cpos will be the first name hash of the xattr rec.
+ * el must be the ocfs2_xattr_header.xb_attrs.xb_root.xt_list.
+ */
+static int ocfs2_xattr_get_rec(struct inode *inode,
+                              u32 name_hash,
+                              u64 *p_blkno,
+                              u32 *e_cpos,
+                              u32 *num_clusters,
+                              struct ocfs2_extent_list *el)
+{
+       int ret = 0, i;
+       struct buffer_head *eb_bh = NULL;
+       struct ocfs2_extent_block *eb;
+       struct ocfs2_extent_rec *rec = NULL;
+       u64 e_blkno = 0;
+
+       if (el->l_tree_depth) {
+               ret = ocfs2_find_leaf(inode, el, name_hash, &eb_bh);
+               if (ret) {
+                       mlog_errno(ret);
+                       goto out;
+               }
+
+               eb = (struct ocfs2_extent_block *) eb_bh->b_data;
+               el = &eb->h_list;
+
+               if (el->l_tree_depth) {
+                       ocfs2_error(inode->i_sb,
+                                   "Inode %lu has non zero tree depth in "
+                                   "xattr tree block %llu\n", inode->i_ino,
+                                   (unsigned long long)eb_bh->b_blocknr);
+                       ret = -EROFS;
+                       goto out;
+               }
+       }
+
+       for (i = le16_to_cpu(el->l_next_free_rec) - 1; i >= 0; i--) {
+               rec = &el->l_recs[i];
+
+               if (le32_to_cpu(rec->e_cpos) <= name_hash) {
+                       e_blkno = le64_to_cpu(rec->e_blkno);
+                       break;
+               }
+       }
+
+       if (!e_blkno) {
+               ocfs2_error(inode->i_sb, "Inode %lu has bad extent "
+                           "record (%u, %u, 0) in xattr", inode->i_ino,
+                           le32_to_cpu(rec->e_cpos),
+                           ocfs2_rec_clusters(el, rec));
+               ret = -EROFS;
+               goto out;
+       }
+
+       *p_blkno = le64_to_cpu(rec->e_blkno);
+       *num_clusters = le16_to_cpu(rec->e_leaf_clusters);
+       if (e_cpos)
+               *e_cpos = le32_to_cpu(rec->e_cpos);
+out:
+       brelse(eb_bh);
+       return ret;
+}
+
+typedef int (xattr_bucket_func)(struct inode *inode,
+                               struct ocfs2_xattr_bucket *bucket,
+                               void *para);
+
+static int ocfs2_find_xe_in_bucket(struct inode *inode,
+                                  struct buffer_head *header_bh,
+                                  int name_index,
+                                  const char *name,
+                                  u32 name_hash,
+                                  u16 *xe_index,
+                                  int *found)
+{
+       int i, ret = 0, cmp = 1, block_off, new_offset;
+       struct ocfs2_xattr_header *xh =
+                       (struct ocfs2_xattr_header *)header_bh->b_data;
+       size_t name_len = strlen(name);
+       struct ocfs2_xattr_entry *xe = NULL;
+       struct buffer_head *name_bh = NULL;
+       char *xe_name;
+
+       /*
+        * We don't use binary search in the bucket because there
+        * may be multiple entries with the same name hash.
+        */
+       for (i = 0; i < le16_to_cpu(xh->xh_count); i++) {
+               xe = &xh->xh_entries[i];
+
+               if (name_hash > le32_to_cpu(xe->xe_name_hash))
+                       continue;
+               else if (name_hash < le32_to_cpu(xe->xe_name_hash))
+                       break;
+
+               cmp = name_index - ocfs2_xattr_get_type(xe);
+               if (!cmp)
+                       cmp = name_len - xe->xe_name_len;
+               if (cmp)
+                       continue;
+
+               ret = ocfs2_xattr_bucket_get_name_value(inode,
+                                                       xh,
+                                                       i,
+                                                       &block_off,
+                                                       &new_offset);
+               if (ret) {
+                       mlog_errno(ret);
+                       break;
+               }
+
+               ret = ocfs2_read_block(inode, header_bh->b_blocknr + block_off,
+                                      &name_bh);
+               if (ret) {
+                       mlog_errno(ret);
+                       break;
+               }
+               xe_name = name_bh->b_data + new_offset;
+
+               cmp = memcmp(name, xe_name, name_len);
+               brelse(name_bh);
+               name_bh = NULL;
+
+               if (cmp == 0) {
+                       *xe_index = i;
+                       *found = 1;
+                       ret = 0;
+                       break;
+               }
+       }
+
+       return ret;
+}
+
+/*
+ * Find the specified xattr entry in a series of buckets.
+ * This series start from p_blkno and last for num_clusters.
+ * The ocfs2_xattr_header.xh_num_buckets of the first bucket contains
+ * the num of the valid buckets.
+ *
+ * Return the buffer_head this xattr should reside in. And if the xattr's
+ * hash is in the gap of 2 buckets, return the lower bucket.
+ */
+static int ocfs2_xattr_bucket_find(struct inode *inode,
+                                  int name_index,
+                                  const char *name,
+                                  u32 name_hash,
+                                  u64 p_blkno,
+                                  u32 first_hash,
+                                  u32 num_clusters,
+                                  struct ocfs2_xattr_search *xs)
+{
+       int ret, found = 0;
+       struct buffer_head *bh = NULL;
+       struct buffer_head *lower_bh = NULL;
+       struct ocfs2_xattr_header *xh = NULL;
+       struct ocfs2_xattr_entry *xe = NULL;
+       u16 index = 0;
+       u16 blk_per_bucket = ocfs2_blocks_per_xattr_bucket(inode->i_sb);
+       int low_bucket = 0, bucket, high_bucket;
+       u32 last_hash;
+       u64 blkno;
+
+       ret = ocfs2_read_block(inode, p_blkno, &bh);
+       if (ret) {
+               mlog_errno(ret);
+               goto out;
+       }
+
+       xh = (struct ocfs2_xattr_header *)bh->b_data;
+       high_bucket = le16_to_cpu(xh->xh_num_buckets) - 1;
+
+       while (low_bucket <= high_bucket) {
+               brelse(bh);
+               bh = NULL;
+               bucket = (low_bucket + high_bucket) / 2;
+
+               blkno = p_blkno + bucket * blk_per_bucket;
+
+               ret = ocfs2_read_block(inode, blkno, &bh);
+               if (ret) {
+                       mlog_errno(ret);
+                       goto out;
+               }
+
+               xh = (struct ocfs2_xattr_header *)bh->b_data;
+               xe = &xh->xh_entries[0];
+               if (name_hash < le32_to_cpu(xe->xe_name_hash)) {
+                       high_bucket = bucket - 1;
+                       continue;
+               }
+
+               /*
+                * Check whether the hash of the last entry in our
+                * bucket is larger than the search one. for an empty
+                * bucket, the last one is also the first one.
+                */
+               if (xh->xh_count)
+                       xe = &xh->xh_entries[le16_to_cpu(xh->xh_count) - 1];
+
+               last_hash = le32_to_cpu(xe->xe_name_hash);
+
+               /* record lower_bh which may be the insert place. */
+               brelse(lower_bh);
+               lower_bh = bh;
+               bh = NULL;
+
+               if (name_hash > le32_to_cpu(xe->xe_name_hash)) {
+                       low_bucket = bucket + 1;
+                       continue;
+               }
+
+               /* the searched xattr should reside in this bucket if exists. */
+               ret = ocfs2_find_xe_in_bucket(inode, lower_bh,
+                                             name_index, name, name_hash,
+                                             &index, &found);
+               if (ret) {
+                       mlog_errno(ret);
+                       goto out;
+               }
+               break;
+       }
+
+       /*
+        * Record the bucket we have found.
+        * When the xattr's hash value is in the gap of 2 buckets, we will
+        * always set it to the previous bucket.
+        */
+       if (!lower_bh) {
+               /*
+                * We can't find any bucket whose first name_hash is less
+                * than the find name_hash.
+                */
+               BUG_ON(bh->b_blocknr != p_blkno);
+               lower_bh = bh;
+               bh = NULL;
+       }
+       xs->bucket.bhs[0] = lower_bh;
+       xs->bucket.xh = (struct ocfs2_xattr_header *)
+                                       xs->bucket.bhs[0]->b_data;
+       lower_bh = NULL;
+
+       xs->header = xs->bucket.xh;
+       xs->base = xs->bucket.bhs[0]->b_data;
+       xs->end = xs->base + inode->i_sb->s_blocksize;
+
+       if (found) {
+               /*
+                * If we have found the xattr enty, read all the blocks in
+                * this bucket.
+                */
+               ret = ocfs2_read_blocks(inode, xs->bucket.bhs[0]->b_blocknr + 1,
+                                       blk_per_bucket - 1, &xs->bucket.bhs[1],
+                                       OCFS2_BH_CACHED);
+               if (ret) {
+                       mlog_errno(ret);
+                       goto out;
+               }
+
+               xs->here = &xs->header->xh_entries[index];
+               mlog(0, "find xattr %s in bucket %llu, entry = %u\n", name,
+                    (unsigned long long)xs->bucket.bhs[0]->b_blocknr, index);
+       } else
+               ret = -ENODATA;
+
+out:
+       brelse(bh);
+       brelse(lower_bh);
+       return ret;
+}
+
+static int ocfs2_xattr_index_block_find(struct inode *inode,
+                                       struct buffer_head *root_bh,
+                                       int name_index,
+                                       const char *name,
+                                       struct ocfs2_xattr_search *xs)
+{
+       int ret;
+       struct ocfs2_xattr_block *xb =
+                       (struct ocfs2_xattr_block *)root_bh->b_data;
+       struct ocfs2_xattr_tree_root *xb_root = &xb->xb_attrs.xb_root;
+       struct ocfs2_extent_list *el = &xb_root->xt_list;
+       u64 p_blkno = 0;
+       u32 first_hash, num_clusters = 0;
+       u32 name_hash = ocfs2_xattr_name_hash(inode, name, strlen(name));
+
+       if (le16_to_cpu(el->l_next_free_rec) == 0)
+               return -ENODATA;
+
+       mlog(0, "find xattr %s, hash = %u, index = %d in xattr tree\n",
+            name, name_hash, name_index);
+
+       ret = ocfs2_xattr_get_rec(inode, name_hash, &p_blkno, &first_hash,
+                                 &num_clusters, el);
+       if (ret) {
+               mlog_errno(ret);
+               goto out;
+       }
+
+       BUG_ON(p_blkno == 0 || num_clusters == 0 || first_hash > name_hash);
+
+       mlog(0, "find xattr extent rec %u clusters from %llu, the first hash "
+            "in the rec is %u\n", num_clusters, p_blkno, first_hash);
+
+       ret = ocfs2_xattr_bucket_find(inode, name_index, name, name_hash,
+                                     p_blkno, first_hash, num_clusters, xs);
+
+out:
+       return ret;
+}
+
+static int ocfs2_iterate_xattr_buckets(struct inode *inode,
+                                      u64 blkno,
+                                      u32 clusters,
+                                      xattr_bucket_func *func,
+                                      void *para)
+{
+       int i, j, ret = 0;
+       int blk_per_bucket = ocfs2_blocks_per_xattr_bucket(inode->i_sb);
+       u32 bpc = ocfs2_xattr_buckets_per_cluster(OCFS2_SB(inode->i_sb));
+       u32 num_buckets = clusters * bpc;
+       struct ocfs2_xattr_bucket bucket;
+
+       memset(&bucket, 0, sizeof(bucket));
+
+       mlog(0, "iterating xattr buckets in %u clusters starting from %llu\n",
+            clusters, blkno);
+
+       for (i = 0; i < num_buckets; i++, blkno += blk_per_bucket) {
+               ret = ocfs2_read_blocks(inode, blkno, blk_per_bucket,
+                                       bucket.bhs, OCFS2_BH_CACHED);
+               if (ret) {
+                       mlog_errno(ret);
+                       goto out;
+               }
+
+               bucket.xh = (struct ocfs2_xattr_header *)bucket.bhs[0]->b_data;
+               /*
+                * The real bucket num in this series of blocks is stored
+                * in the 1st bucket.
+                */
+               if (i == 0)
+                       num_buckets = le16_to_cpu(bucket.xh->xh_num_buckets);
+
+               mlog(0, "iterating xattr bucket %llu, first hash %u\n", blkno,
+                    le32_to_cpu(bucket.xh->xh_entries[0].xe_name_hash));
+               if (func) {
+                       ret = func(inode, &bucket, para);
+                       if (ret) {
+                               mlog_errno(ret);
+                               break;
+                       }
+               }
+
+               for (j = 0; j < blk_per_bucket; j++)
+                       brelse(bucket.bhs[j]);
+               memset(&bucket, 0, sizeof(bucket));
+       }
+
+out:
+       for (j = 0; j < blk_per_bucket; j++)
+               brelse(bucket.bhs[j]);
+
+       return ret;
+}
+
+struct ocfs2_xattr_tree_list {
+       char *buffer;
+       size_t buffer_size;
+       size_t result;
+};
+
+static int ocfs2_xattr_bucket_get_name_value(struct inode *inode,
+                                            struct ocfs2_xattr_header *xh,
+                                            int index,
+                                            int *block_off,
+                                            int *new_offset)
+{
+       u16 name_offset;
+
+       if (index < 0 || index >= le16_to_cpu(xh->xh_count))
+               return -EINVAL;
+
+       name_offset = le16_to_cpu(xh->xh_entries[index].xe_name_offset);
+
+       *block_off = name_offset >> inode->i_sb->s_blocksize_bits;
+       *new_offset = name_offset % inode->i_sb->s_blocksize;
+
+       return 0;
+}
+
+static int ocfs2_list_xattr_bucket(struct inode *inode,
+                                  struct ocfs2_xattr_bucket *bucket,
+                                  void *para)
+{
+       int ret = 0, type;
+       struct ocfs2_xattr_tree_list *xl = (struct ocfs2_xattr_tree_list *)para;
+       int i, block_off, new_offset;
+       const char *prefix, *name;
+
+       for (i = 0 ; i < le16_to_cpu(bucket->xh->xh_count); i++) {
+               struct ocfs2_xattr_entry *entry = &bucket->xh->xh_entries[i];
+               type = ocfs2_xattr_get_type(entry);
+               prefix = ocfs2_xattr_prefix(type);
+
+               if (prefix) {
+                       ret = ocfs2_xattr_bucket_get_name_value(inode,
+                                                               bucket->xh,
+                                                               i,
+                                                               &block_off,
+                                                               &new_offset);
+                       if (ret)
+                               break;
+
+                       name = (const char *)bucket->bhs[block_off]->b_data +
+                               new_offset;
+                       ret = ocfs2_xattr_list_entry(xl->buffer,
+                                                    xl->buffer_size,
+                                                    &xl->result,
+                                                    prefix, name,
+                                                    entry->xe_name_len);
+                       if (ret)
+                               break;
+               }
+       }
+
+       return ret;
+}
+
+static int ocfs2_xattr_tree_list_index_block(struct inode *inode,
+                                            struct ocfs2_xattr_tree_root *xt,
+                                            char *buffer,
+                                            size_t buffer_size)
+{
+       struct ocfs2_extent_list *el = &xt->xt_list;
+       int ret = 0;
+       u32 name_hash = UINT_MAX, e_cpos = 0, num_clusters = 0;
+       u64 p_blkno = 0;
+       struct ocfs2_xattr_tree_list xl = {
+               .buffer = buffer,
+               .buffer_size = buffer_size,
+               .result = 0,
+       };
+
+       if (le16_to_cpu(el->l_next_free_rec) == 0)
+               return 0;
+
+       while (name_hash > 0) {
+               ret = ocfs2_xattr_get_rec(inode, name_hash, &p_blkno,
+                                         &e_cpos, &num_clusters, el);
+               if (ret) {
+                       mlog_errno(ret);
+                       goto out;
+               }
+
+               ret = ocfs2_iterate_xattr_buckets(inode, p_blkno, num_clusters,
+                                                 ocfs2_list_xattr_bucket,
+                                                 &xl);
+               if (ret) {
+                       mlog_errno(ret);
+                       goto out;
+               }
+
+               if (e_cpos == 0)
+                       break;
+
+               name_hash = e_cpos - 1;
+       }
+
+       ret = xl.result;
+out:
+       return ret;
+}
+
+static int cmp_xe(const void *a, const void *b)
+{
+       const struct ocfs2_xattr_entry *l = a, *r = b;
+       u32 l_hash = le32_to_cpu(l->xe_name_hash);
+       u32 r_hash = le32_to_cpu(r->xe_name_hash);
+
+       if (l_hash > r_hash)
+               return 1;
+       if (l_hash < r_hash)
+               return -1;
+       return 0;
+}
+
+static void swap_xe(void *a, void *b, int size)
+{
+       struct ocfs2_xattr_entry *l = a, *r = b, tmp;
+
+       tmp = *l;
+       memcpy(l, r, sizeof(struct ocfs2_xattr_entry));
+       memcpy(r, &tmp, sizeof(struct ocfs2_xattr_entry));
+}
+
+/*
+ * When the ocfs2_xattr_block is filled up, new bucket will be created
+ * and all the xattr entries will be moved to the new bucket.
+ * Note: we need to sort the entries since they are not saved in order
+ * in the ocfs2_xattr_block.
+ */
+static void ocfs2_cp_xattr_block_to_bucket(struct inode *inode,
+                                          struct buffer_head *xb_bh,
+                                          struct buffer_head *xh_bh,
+                                          struct buffer_head *data_bh)
+{
+       int i, blocksize = inode->i_sb->s_blocksize;
+       u16 offset, size, off_change;
+       struct ocfs2_xattr_entry *xe;
+       struct ocfs2_xattr_block *xb =
+                               (struct ocfs2_xattr_block *)xb_bh->b_data;
+       struct ocfs2_xattr_header *xb_xh = &xb->xb_attrs.xb_header;
+       struct ocfs2_xattr_header *xh =
+                               (struct ocfs2_xattr_header *)xh_bh->b_data;
+       u16 count = le16_to_cpu(xb_xh->xh_count);
+       char *target = xh_bh->b_data, *src = xb_bh->b_data;
+
+       mlog(0, "cp xattr from block %llu to bucket %llu\n",
+            (unsigned long long)xb_bh->b_blocknr,
+            (unsigned long long)xh_bh->b_blocknr);
+
+       memset(xh_bh->b_data, 0, blocksize);
+       if (data_bh)
+               memset(data_bh->b_data, 0, blocksize);
+       /*
+        * Since the xe_name_offset is based on ocfs2_xattr_header,
+        * there is a offset change corresponding to the change of
+        * ocfs2_xattr_header's position.
+        */
+       off_change = offsetof(struct ocfs2_xattr_block, xb_attrs.xb_header);
+       xe = &xb_xh->xh_entries[count - 1];
+       offset = le16_to_cpu(xe->xe_name_offset) + off_change;
+       size = blocksize - offset;
+
+       /* copy all the names and values. */
+       if (data_bh)
+               target = data_bh->b_data;
+       memcpy(target + offset, src + offset, size);
+
+       /* Init new header now. */
+       xh->xh_count = xb_xh->xh_count;
+       xh->xh_num_buckets = cpu_to_le16(1);
+       xh->xh_name_value_len = cpu_to_le16(size);
+       xh->xh_free_start = cpu_to_le16(OCFS2_XATTR_BUCKET_SIZE - size);
+
+       /* copy all the entries. */
+       target = xh_bh->b_data;
+       offset = offsetof(struct ocfs2_xattr_header, xh_entries);
+       size = count * sizeof(struct ocfs2_xattr_entry);
+       memcpy(target + offset, (char *)xb_xh + offset, size);
+
+       /* Change the xe offset for all the xe because of the move. */
+       off_change = OCFS2_XATTR_BUCKET_SIZE - blocksize +
+                offsetof(struct ocfs2_xattr_block, xb_attrs.xb_header);
+       for (i = 0; i < count; i++)
+               le16_add_cpu(&xh->xh_entries[i].xe_name_offset, off_change);
+
+       mlog(0, "copy entry: start = %u, size = %u, offset_change = %u\n",
+            offset, size, off_change);
+
+       sort(target + offset, count, sizeof(struct ocfs2_xattr_entry),
+            cmp_xe, swap_xe);
+}
+
+/*
+ * After we move xattr from block to index btree, we have to
+ * update ocfs2_xattr_search to the new xe and base.
+ *
+ * When the entry is in xattr block, xattr_bh indicates the storage place.
+ * While if the entry is in index b-tree, "bucket" indicates the
+ * real place of the xattr.
+ */
+static int ocfs2_xattr_update_xattr_search(struct inode *inode,
+                                          struct ocfs2_xattr_search *xs,
+                                          struct buffer_head *old_bh,
+                                          struct buffer_head *new_bh)
+{
+       int ret = 0;
+       char *buf = old_bh->b_data;
+       struct ocfs2_xattr_block *old_xb = (struct ocfs2_xattr_block *)buf;
+       struct ocfs2_xattr_header *old_xh = &old_xb->xb_attrs.xb_header;
+       int i, blocksize = inode->i_sb->s_blocksize;
+       u16 blk_per_bucket = ocfs2_blocks_per_xattr_bucket(inode->i_sb);
+
+       xs->bucket.bhs[0] = new_bh;
+       get_bh(new_bh);
+       xs->bucket.xh = (struct ocfs2_xattr_header *)xs->bucket.bhs[0]->b_data;
+       xs->header = xs->bucket.xh;
+
+       xs->base = new_bh->b_data;
+       xs->end = xs->base + inode->i_sb->s_blocksize;
+
+       if (!xs->not_found) {
+               if (OCFS2_XATTR_BUCKET_SIZE != blocksize) {
+                       ret = ocfs2_read_blocks(inode,
+                                       xs->bucket.bhs[0]->b_blocknr + 1,
+                                       blk_per_bucket - 1, &xs->bucket.bhs[1],
+                                       OCFS2_BH_CACHED);
+                       if (ret) {
+                               mlog_errno(ret);
+                               return ret;
+                       }
+
+                       i = xs->here - old_xh->xh_entries;
+                       xs->here = &xs->header->xh_entries[i];
+               }
+       }
+
+       return ret;
+}
+
+static int ocfs2_xattr_create_index_block(struct inode *inode,
+                                         struct ocfs2_xattr_search *xs)
+{
+       int ret, credits = OCFS2_SUBALLOC_ALLOC;
+       u32 bit_off, len;
+       u64 blkno;
+       handle_t *handle;
+       struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
+       struct ocfs2_inode_info *oi = OCFS2_I(inode);
+       struct ocfs2_alloc_context *data_ac;
+       struct buffer_head *xh_bh = NULL, *data_bh = NULL;
+       struct buffer_head *xb_bh = xs->xattr_bh;
+       struct ocfs2_xattr_block *xb =
+                       (struct ocfs2_xattr_block *)xb_bh->b_data;
+       struct ocfs2_xattr_tree_root *xr;
+       u16 xb_flags = le16_to_cpu(xb->xb_flags);
+       u16 bpb = ocfs2_blocks_per_xattr_bucket(inode->i_sb);
+
+       mlog(0, "create xattr index block for %llu\n",
+            (unsigned long long)xb_bh->b_blocknr);
+
+       BUG_ON(xb_flags & OCFS2_XATTR_INDEXED);
+
+       ret = ocfs2_reserve_clusters(osb, 1, &data_ac);
+       if (ret) {
+               mlog_errno(ret);
+               goto out;
+       }
+
+       /*
+        * XXX:
+        * We can use this lock for now, and maybe move to a dedicated mutex
+        * if performance becomes a problem later.
+        */
+       down_write(&oi->ip_alloc_sem);
+
+       /*
+        * 3 more credits, one for xattr block update, one for the 1st block
+        * of the new xattr bucket and one for the value/data.
+        */
+       credits += 3;
+       handle = ocfs2_start_trans(osb, credits);
+       if (IS_ERR(handle)) {
+               ret = PTR_ERR(handle);
+               mlog_errno(ret);
+               goto out_sem;
+       }
+
+       ret = ocfs2_journal_access(handle, inode, xb_bh,
+                                  OCFS2_JOURNAL_ACCESS_WRITE);
+       if (ret) {
+               mlog_errno(ret);
+               goto out_commit;
+       }
+
+       ret = ocfs2_claim_clusters(osb, handle, data_ac, 1, &bit_off, &len);
+       if (ret) {
+               mlog_errno(ret);
+               goto out_commit;
+       }
+
+       /*
+        * The bucket may spread in many blocks, and
+        * we will only touch the 1st block and the last block
+        * in the whole bucket(one for entry and one for data).
+        */
+       blkno = ocfs2_clusters_to_blocks(inode->i_sb, bit_off);
+
+       mlog(0, "allocate 1 cluster from %llu to xattr block\n", blkno);
+
+       xh_bh = sb_getblk(inode->i_sb, blkno);
+       if (!xh_bh) {
+               ret = -EIO;
+               mlog_errno(ret);
+               goto out_commit;
+       }
+
+       ocfs2_set_new_buffer_uptodate(inode, xh_bh);
+
+       ret = ocfs2_journal_access(handle, inode, xh_bh,
+                                  OCFS2_JOURNAL_ACCESS_CREATE);
+       if (ret) {
+               mlog_errno(ret);
+               goto out_commit;
+       }
+
+       if (bpb > 1) {
+               data_bh = sb_getblk(inode->i_sb, blkno + bpb - 1);
+               if (!data_bh) {
+                       ret = -EIO;
+                       mlog_errno(ret);
+                       goto out_commit;
+               }
+
+               ocfs2_set_new_buffer_uptodate(inode, data_bh);
+
+               ret = ocfs2_journal_access(handle, inode, data_bh,
+                                          OCFS2_JOURNAL_ACCESS_CREATE);
+               if (ret) {
+                       mlog_errno(ret);
+                       goto out_commit;
+               }
+       }
+
+       ocfs2_cp_xattr_block_to_bucket(inode, xb_bh, xh_bh, data_bh);
+
+       ocfs2_journal_dirty(handle, xh_bh);
+       if (data_bh)
+               ocfs2_journal_dirty(handle, data_bh);
+
+       ocfs2_xattr_update_xattr_search(inode, xs, xb_bh, xh_bh);
+
+       /* Change from ocfs2_xattr_header to ocfs2_xattr_tree_root */
+       memset(&xb->xb_attrs, 0, inode->i_sb->s_blocksize -
+              offsetof(struct ocfs2_xattr_block, xb_attrs));
+
+       xr = &xb->xb_attrs.xb_root;
+       xr->xt_clusters = cpu_to_le32(1);
+       xr->xt_last_eb_blk = 0;
+       xr->xt_list.l_tree_depth = 0;
+       xr->xt_list.l_count = cpu_to_le16(ocfs2_xattr_recs_per_xb(inode->i_sb));
+       xr->xt_list.l_next_free_rec = cpu_to_le16(1);
+
+       xr->xt_list.l_recs[0].e_cpos = 0;
+       xr->xt_list.l_recs[0].e_blkno = cpu_to_le64(blkno);
+       xr->xt_list.l_recs[0].e_leaf_clusters = cpu_to_le16(1);
+
+       xb->xb_flags = cpu_to_le16(xb_flags | OCFS2_XATTR_INDEXED);
+
+       ret = ocfs2_journal_dirty(handle, xb_bh);
+       if (ret) {
+               mlog_errno(ret);
+               goto out_commit;
+       }
+
+out_commit:
+       ocfs2_commit_trans(osb, handle);
+
+out_sem:
+       up_write(&oi->ip_alloc_sem);
+
+out:
+       if (data_ac)
+               ocfs2_free_alloc_context(data_ac);
+
+       brelse(xh_bh);
+       brelse(data_bh);
+
+       return ret;
+}
+
+static int cmp_xe_offset(const void *a, const void *b)
+{
+       const struct ocfs2_xattr_entry *l = a, *r = b;
+       u32 l_name_offset = le16_to_cpu(l->xe_name_offset);
+       u32 r_name_offset = le16_to_cpu(r->xe_name_offset);
+
+       if (l_name_offset < r_name_offset)
+               return 1;
+       if (l_name_offset > r_name_offset)
+               return -1;
+       return 0;
+}
+
+/*
+ * defrag a xattr bucket if we find that the bucket has some
+ * holes beteen name/value pairs.
+ * We will move all the name/value pairs to the end of the bucket
+ * so that we can spare some space for insertion.
+ */
+static int ocfs2_defrag_xattr_bucket(struct inode *inode,
+                                    struct ocfs2_xattr_bucket *bucket)
+{
+       int ret, i;
+       size_t end, offset, len, value_len;
+       struct ocfs2_xattr_header *xh;
+       char *entries, *buf, *bucket_buf = NULL;
+       u64 blkno = bucket->bhs[0]->b_blocknr;
+       u16 blk_per_bucket = ocfs2_blocks_per_xattr_bucket(inode->i_sb);
+       u16 xh_free_start;
+       size_t blocksize = inode->i_sb->s_blocksize;
+       handle_t *handle;
+       struct buffer_head **bhs;
+       struct ocfs2_xattr_entry *xe;
+
+       bhs = kzalloc(sizeof(struct buffer_head *) * blk_per_bucket,
+                       GFP_NOFS);
+       if (!bhs)
+               return -ENOMEM;
+
+       ret = ocfs2_read_blocks(inode, blkno, blk_per_bucket, bhs,
+                               OCFS2_BH_CACHED);
+       if (ret)
+               goto out;
+
+       /*
+        * In order to make the operation more efficient and generic,
+        * we copy all the blocks into a contiguous memory and do the
+        * defragment there, so if anything is error, we will not touch
+        * the real block.
+        */
+       bucket_buf = kmalloc(OCFS2_XATTR_BUCKET_SIZE, GFP_NOFS);
+       if (!bucket_buf) {
+               ret = -EIO;
+               goto out;
+       }
+
+       buf = bucket_buf;
+       for (i = 0; i < blk_per_bucket; i++, buf += blocksize)
+               memcpy(buf, bhs[i]->b_data, blocksize);
+
+       handle = ocfs2_start_trans((OCFS2_SB(inode->i_sb)), blk_per_bucket);
+       if (IS_ERR(handle)) {
+               ret = PTR_ERR(handle);
+               handle = NULL;
+               mlog_errno(ret);
+               goto out;
+       }
+
+       for (i = 0; i < blk_per_bucket; i++) {
+               ret = ocfs2_journal_access(handle, inode, bhs[i],
+                                          OCFS2_JOURNAL_ACCESS_WRITE);
+               if (ret < 0) {
+                       mlog_errno(ret);
+                       goto commit;
+               }
+       }
+
+       xh = (struct ocfs2_xattr_header *)bucket_buf;
+       entries = (char *)xh->xh_entries;
+       xh_free_start = le16_to_cpu(xh->xh_free_start);
+
+       mlog(0, "adjust xattr bucket in %llu, count = %u, "
+            "xh_free_start = %u, xh_name_value_len = %u.\n",
+            blkno, le16_to_cpu(xh->xh_count), xh_free_start,
+            le16_to_cpu(xh->xh_name_value_len));
+
+       /*
+        * sort all the entries by their offset.
+        * the largest will be the first, so that we can
+        * move them to the end one by one.
+        */
+       sort(entries, le16_to_cpu(xh->xh_count),
+            sizeof(struct ocfs2_xattr_entry),
+            cmp_xe_offset, swap_xe);
+
+       /* Move all name/values to the end of the bucket. */
+       xe = xh->xh_entries;
+       end = OCFS2_XATTR_BUCKET_SIZE;
+       for (i = 0; i < le16_to_cpu(xh->xh_count); i++, xe++) {
+               offset = le16_to_cpu(xe->xe_name_offset);
+               if (ocfs2_xattr_is_local(xe))
+                       value_len = OCFS2_XATTR_SIZE(
+                                       le64_to_cpu(xe->xe_value_size));
+               else
+                       value_len = OCFS2_XATTR_ROOT_SIZE;
+               len = OCFS2_XATTR_SIZE(xe->xe_name_len) + value_len;
+
+               /*
+                * We must make sure that the name/value pair
+                * exist in the same block. So adjust end to
+                * the previous block end if needed.
+                */
+               if (((end - len) / blocksize !=
+                       (end - 1) / blocksize))
+                       end = end - end % blocksize;
+
+               if (end > offset + len) {
+                       memmove(bucket_buf + end - len,
+                               bucket_buf + offset, len);
+                       xe->xe_name_offset = cpu_to_le16(end - len);
+               }
+
+               mlog_bug_on_msg(end < offset + len, "Defrag check failed for "
+                               "bucket %llu\n", (unsigned long long)blkno);
+
+               end -= len;
+       }
+
+       mlog_bug_on_msg(xh_free_start > end, "Defrag check failed for "
+                       "bucket %llu\n", (unsigned long long)blkno);
+
+       if (xh_free_start == end)
+               goto commit;
+
+       memset(bucket_buf + xh_free_start, 0, end - xh_free_start);
+       xh->xh_free_start = cpu_to_le16(end);
+
+       /* sort the entries by their name_hash. */
+       sort(entries, le16_to_cpu(xh->xh_count),
+            sizeof(struct ocfs2_xattr_entry),
+            cmp_xe, swap_xe);
+
+       buf = bucket_buf;
+       for (i = 0; i < blk_per_bucket; i++, buf += blocksize) {
+               memcpy(bhs[i]->b_data, buf, blocksize);
+               ocfs2_journal_dirty(handle, bhs[i]);
+       }
+
+commit:
+       ocfs2_commit_trans(OCFS2_SB(inode->i_sb), handle);
+out:
+
+       if (bhs) {
+               for (i = 0; i < blk_per_bucket; i++)
+                       brelse(bhs[i]);
+       }
+       kfree(bhs);
+
+       kfree(bucket_buf);
+       return ret;
+}
+
+/*
+ * Move half nums of the xattr bucket in the previous cluster to this new
+ * cluster. We only touch the last cluster of the previous extend record.
+ *
+ * first_bh is the first buffer_head of a series of bucket in the same
+ * extent rec and header_bh is the header of one bucket in this cluster.
+ * They will be updated if we move the data header_bh contains to the new
+ * cluster. first_hash will be set as the 1st xe's name_hash of the new cluster.
+ */
+static int ocfs2_mv_xattr_bucket_cross_cluster(struct inode *inode,
+                                              handle_t *handle,
+                                              struct buffer_head **first_bh,
+                                              struct buffer_head **header_bh,
+                                              u64 new_blkno,
+                                              u64 prev_blkno,
+                                              u32 num_clusters,
+                                              u32 *first_hash)
+{
+       int i, ret, credits;
+       struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
+       int bpc = ocfs2_clusters_to_blocks(inode->i_sb, 1);
+       int num_buckets = ocfs2_xattr_buckets_per_cluster(osb);
+       int blocksize = inode->i_sb->s_blocksize;
+       struct buffer_head *old_bh, *new_bh, *prev_bh, *new_first_bh = NULL;
+       struct ocfs2_xattr_header *new_xh;
+       struct ocfs2_xattr_header *xh =
+                       (struct ocfs2_xattr_header *)((*first_bh)->b_data);
+
+       BUG_ON(le16_to_cpu(xh->xh_num_buckets) < num_buckets);
+       BUG_ON(OCFS2_XATTR_BUCKET_SIZE == osb->s_clustersize);
+
+       prev_bh = *first_bh;
+       get_bh(prev_bh);
+       xh = (struct ocfs2_xattr_header *)prev_bh->b_data;
+
+       prev_blkno += (num_clusters - 1) * bpc + bpc / 2;
+
+       mlog(0, "move half of xattrs in cluster %llu to %llu\n",
+            prev_blkno, new_blkno);
+
+       /*
+        * We need to update the 1st half of the new cluster and
+        * 1 more for the update of the 1st bucket of the previous
+        * extent record.
+        */
+       credits = bpc / 2 + 1;
+       ret = ocfs2_extend_trans(handle, credits);
+       if (ret) {
+               mlog_errno(ret);
+               goto out;
+       }
+
+       ret = ocfs2_journal_access(handle, inode, prev_bh,
+                                  OCFS2_JOURNAL_ACCESS_WRITE);
+       if (ret) {
+               mlog_errno(ret);
+               goto out;
+       }
+
+       for (i = 0; i < bpc / 2; i++, prev_blkno++, new_blkno++) {
+               old_bh = new_bh = NULL;
+               new_bh = sb_getblk(inode->i_sb, new_blkno);
+               if (!new_bh) {
+                       ret = -EIO;
+                       mlog_errno(ret);
+                       goto out;
+               }
+
+               ocfs2_set_new_buffer_uptodate(inode, new_bh);
+
+               ret = ocfs2_journal_access(handle, inode, new_bh,
+                                          OCFS2_JOURNAL_ACCESS_CREATE);
+               if (ret < 0) {
+                       mlog_errno(ret);
+                       brelse(new_bh);
+                       goto out;
+               }
+
+               ret = ocfs2_read_block(inode, prev_blkno, &old_bh);
+               if (ret < 0) {
+                       mlog_errno(ret);
+                       brelse(new_bh);
+                       goto out;
+               }
+
+               memcpy(new_bh->b_data, old_bh->b_data, blocksize);
+
+               if (i == 0) {
+                       new_xh = (struct ocfs2_xattr_header *)new_bh->b_data;
+                       new_xh->xh_num_buckets = cpu_to_le16(num_buckets / 2);
+
+                       if (first_hash)
+                               *first_hash = le32_to_cpu(
+                                       new_xh->xh_entries[0].xe_name_hash);
+                       new_first_bh = new_bh;
+                       get_bh(new_first_bh);
+               }
+
+               ocfs2_journal_dirty(handle, new_bh);
+
+               if (*header_bh == old_bh) {
+                       brelse(*header_bh);
+                       *header_bh = new_bh;
+                       get_bh(*header_bh);
+
+                       brelse(*first_bh);
+                       *first_bh = new_first_bh;
+                       get_bh(*first_bh);
+               }
+               brelse(new_bh);
+               brelse(old_bh);
+       }
+
+       le16_add_cpu(&xh->xh_num_buckets, -(num_buckets / 2));
+
+       ocfs2_journal_dirty(handle, prev_bh);
+out:
+       brelse(prev_bh);
+       brelse(new_first_bh);
+       return ret;
+}
+
+static int ocfs2_read_xattr_bucket(struct inode *inode,
+                                  u64 blkno,
+                                  struct buffer_head **bhs,
+                                  int new)
+{
+       int ret = 0;
+       u16 i, blk_per_bucket = ocfs2_blocks_per_xattr_bucket(inode->i_sb);
+
+       if (!new)
+               return ocfs2_read_blocks(inode, blkno,
+                                        blk_per_bucket, bhs,
+                                        OCFS2_BH_CACHED);
+
+       for (i = 0; i < blk_per_bucket; i++) {
+               bhs[i] = sb_getblk(inode->i_sb, blkno + i);
+               if (bhs[i] == NULL) {
+                       ret = -EIO;
+                       mlog_errno(ret);
+                       break;
+               }
+               ocfs2_set_new_buffer_uptodate(inode, bhs[i]);
+       }
+
+       return ret;
+}
+
+/*
+ * Move half num of the xattrs in old bucket(blk) to new bucket(new_blk).
+ * first_hash will record the 1st hash of the new bucket.
+ */
+static int ocfs2_half_xattr_bucket(struct inode *inode,
+                                  handle_t *handle,
+                                  u64 blk,
+                                  u64 new_blk,
+                                  u32 *first_hash,
+                                  int new_bucket_head)
+{
+       int ret, i;
+       u16 count, start, len, name_value_len, xe_len, name_offset;
+       u16 blk_per_bucket = ocfs2_blocks_per_xattr_bucket(inode->i_sb);
+       struct buffer_head **s_bhs, **t_bhs = NULL;
+       struct ocfs2_xattr_header *xh;
+       struct ocfs2_xattr_entry *xe;
+       int blocksize = inode->i_sb->s_blocksize;
+
+       mlog(0, "move half of xattrs from bucket %llu to %llu\n",
+            blk, new_blk);
+
+       s_bhs = kcalloc(blk_per_bucket, sizeof(struct buffer_head *), GFP_NOFS);
+       if (!s_bhs)
+               return -ENOMEM;
+
+       ret = ocfs2_read_xattr_bucket(inode, blk, s_bhs, 0);
+       if (ret) {
+               mlog_errno(ret);
+               goto out;
+       }
+
+       ret = ocfs2_journal_access(handle, inode, s_bhs[0],
+                                  OCFS2_JOURNAL_ACCESS_WRITE);
+       if (ret) {
+               mlog_errno(ret);
+               goto out;
+       }
+
+       t_bhs = kcalloc(blk_per_bucket, sizeof(struct buffer_head *), GFP_NOFS);
+       if (!t_bhs) {
+               ret = -ENOMEM;
+               goto out;
+       }
+
+       ret = ocfs2_read_xattr_bucket(inode, new_blk, t_bhs, new_bucket_head);
+       if (ret) {
+               mlog_errno(ret);
+               goto out;
+       }
+
+       for (i = 0; i < blk_per_bucket; i++) {
+               ret = ocfs2_journal_access(handle, inode, t_bhs[i],
+                                          OCFS2_JOURNAL_ACCESS_CREATE);
+               if (ret) {
+                       mlog_errno(ret);
+                       goto out;
+               }
+       }
+
+       /* copy the whole bucket to the new first. */
+       for (i = 0; i < blk_per_bucket; i++)
+               memcpy(t_bhs[i]->b_data, s_bhs[i]->b_data, blocksize);
+
+       /* update the new bucket. */
+       xh = (struct ocfs2_xattr_header *)t_bhs[0]->b_data;
+       count = le16_to_cpu(xh->xh_count);
+       start = count / 2;
+
+       /*
+        * Calculate the total name/value len and xh_free_start for
+        * the old bucket first.
+        */
+       name_offset = OCFS2_XATTR_BUCKET_SIZE;
+       name_value_len = 0;
+       for (i = 0; i < start; i++) {
+               xe = &xh->xh_entries[i];
+               xe_len = OCFS2_XATTR_SIZE(xe->xe_name_len);
+               if (ocfs2_xattr_is_local(xe))
+                       xe_len +=
+                          OCFS2_XATTR_SIZE(le64_to_cpu(xe->xe_value_size));
+               else
+                       xe_len += OCFS2_XATTR_ROOT_SIZE;
+               name_value_len += xe_len;
+               if (le16_to_cpu(xe->xe_name_offset) < name_offset)
+                       name_offset = le16_to_cpu(xe->xe_name_offset);
+       }
+
+       /*
+        * Now begin the modification to the new bucket.
+        *
+        * In the new bucket, We just move the xattr entry to the beginning
+        * and don't touch the name/value. So there will be some holes in the
+        * bucket, and they will be removed when ocfs2_defrag_xattr_bucket is
+        * called.
+        */
+       xe = &xh->xh_entries[start];
+       len = sizeof(struct ocfs2_xattr_entry) * (count - start);
+       mlog(0, "mv xattr entry len %d from %d to %d\n", len,
+            (int)((char *)xe - (char *)xh),
+            (int)((char *)xh->xh_entries - (char *)xh));
+       memmove((char *)xh->xh_entries, (char *)xe, len);
+       xe = &xh->xh_entries[count - start];
+       len = sizeof(struct ocfs2_xattr_entry) * start;
+       memset((char *)xe, 0, len);
+
+       le16_add_cpu(&xh->xh_count, -start);
+       le16_add_cpu(&xh->xh_name_value_len, -name_value_len);
+
+       /* Calculate xh_free_start for the new bucket. */
+       xh->xh_free_start = cpu_to_le16(OCFS2_XATTR_BUCKET_SIZE);
+       for (i = 0; i < le16_to_cpu(xh->xh_count); i++) {
+               xe = &xh->xh_entries[i];
+               xe_len = OCFS2_XATTR_SIZE(xe->xe_name_len);
+               if (ocfs2_xattr_is_local(xe))
+                       xe_len +=
+                          OCFS2_XATTR_SIZE(le64_to_cpu(xe->xe_value_size));
+               else
+                       xe_len += OCFS2_XATTR_ROOT_SIZE;
+               if (le16_to_cpu(xe->xe_name_offset) <
+                   le16_to_cpu(xh->xh_free_start))
+                       xh->xh_free_start = xe->xe_name_offset;
+       }
+
+       /* set xh->xh_num_buckets for the new xh. */
+       if (new_bucket_head)
+               xh->xh_num_buckets = cpu_to_le16(1);
+       else
+               xh->xh_num_buckets = 0;
+
+       for (i = 0; i < blk_per_bucket; i++) {
+               ocfs2_journal_dirty(handle, t_bhs[i]);
+               if (ret)
+                       mlog_errno(ret);
+       }
+
+       /* store the first_hash of the new bucket. */
+       if (first_hash)
+               *first_hash = le32_to_cpu(xh->xh_entries[0].xe_name_hash);
+
+       /*
+        * Now only update the 1st block of the old bucket.
+        * Please note that the entry has been sorted already above.
+        */
+       xh = (struct ocfs2_xattr_header *)s_bhs[0]->b_data;
+       memset(&xh->xh_entries[start], 0,
+              sizeof(struct ocfs2_xattr_entry) * (count - start));
+       xh->xh_count = cpu_to_le16(start);
+       xh->xh_free_start = cpu_to_le16(name_offset);
+       xh->xh_name_value_len = cpu_to_le16(name_value_len);
+
+       ocfs2_journal_dirty(handle, s_bhs[0]);
+       if (ret)
+               mlog_errno(ret);
+
+out:
+       if (s_bhs) {
+               for (i = 0; i < blk_per_bucket; i++)
+                       brelse(s_bhs[i]);
+       }
+       kfree(s_bhs);
+
+       if (t_bhs) {
+               for (i = 0; i < blk_per_bucket; i++)
+                       brelse(t_bhs[i]);
+       }
+       kfree(t_bhs);
+
+       return ret;
+}
+
+/*
+ * Copy xattr from one bucket to another bucket.
+ *
+ * The caller must make sure that the journal transaction
+ * has enough space for journaling.
+ */
+static int ocfs2_cp_xattr_bucket(struct inode *inode,
+                                handle_t *handle,
+                                u64 s_blkno,
+                                u64 t_blkno,
+                                int t_is_new)
+{
+       int ret, i;
+       int blk_per_bucket = ocfs2_blocks_per_xattr_bucket(inode->i_sb);
+       int blocksize = inode->i_sb->s_blocksize;
+       struct buffer_head **s_bhs, **t_bhs = NULL;
+
+       BUG_ON(s_blkno == t_blkno);
+
+       mlog(0, "cp bucket %llu to %llu, target is %d\n",
+            s_blkno, t_blkno, t_is_new);
+
+       s_bhs = kzalloc(sizeof(struct buffer_head *) * blk_per_bucket,
+                       GFP_NOFS);
+       if (!s_bhs)
+               return -ENOMEM;
+
+       ret = ocfs2_read_xattr_bucket(inode, s_blkno, s_bhs, 0);
+       if (ret)
+               goto out;
+
+       t_bhs = kzalloc(sizeof(struct buffer_head *) * blk_per_bucket,
+                       GFP_NOFS);
+       if (!t_bhs) {
+               ret = -ENOMEM;
+               goto out;
+       }
+
+       ret = ocfs2_read_xattr_bucket(inode, t_blkno, t_bhs, t_is_new);
+       if (ret)
+               goto out;
+
+       for (i = 0; i < blk_per_bucket; i++) {
+               ret = ocfs2_journal_access(handle, inode, t_bhs[i],
+                                          OCFS2_JOURNAL_ACCESS_WRITE);
+               if (ret)
+                       goto out;
+       }
+
+       for (i = 0; i < blk_per_bucket; i++) {
+               memcpy(t_bhs[i]->b_data, s_bhs[i]->b_data, blocksize);
+               ocfs2_journal_dirty(handle, t_bhs[i]);
+       }
+
+out:
+       if (s_bhs) {
+               for (i = 0; i < blk_per_bucket; i++)
+                       brelse(s_bhs[i]);
+       }
+       kfree(s_bhs);
+
+       if (t_bhs) {
+               for (i = 0; i < blk_per_bucket; i++)
+                       brelse(t_bhs[i]);
+       }
+       kfree(t_bhs);
+
+       return ret;
+}
+
+/*
+ * Copy one xattr cluster from src_blk to to_blk.
+ * The to_blk will become the first bucket header of the cluster, so its
+ * xh_num_buckets will be initialized as the bucket num in the cluster.
+ */
+static int ocfs2_cp_xattr_cluster(struct inode *inode,
+                                 handle_t *handle,
+                                 struct buffer_head *first_bh,
+                                 u64 src_blk,
+                                 u64 to_blk,
+                                 u32 *first_hash)
+{
+       int i, ret, credits;
+       struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
+       int bpc = ocfs2_clusters_to_blocks(inode->i_sb, 1);
+       int num_buckets = ocfs2_xattr_buckets_per_cluster(osb);
+       struct buffer_head *bh = NULL;
+       struct ocfs2_xattr_header *xh;
+       u64 to_blk_start = to_blk;
+
+       mlog(0, "cp xattrs from cluster %llu to %llu\n", src_blk, to_blk);
+
+       /*
+        * We need to update the new cluster and 1 more for the update of
+        * the 1st bucket of the previous extent rec.
+        */
+       credits = bpc + 1;
+       ret = ocfs2_extend_trans(handle, credits);
+       if (ret) {
+               mlog_errno(ret);
+               goto out;
+       }
+
+       ret = ocfs2_journal_access(handle, inode, first_bh,
+                                  OCFS2_JOURNAL_ACCESS_WRITE);
+       if (ret) {
+               mlog_errno(ret);
+               goto out;
+       }
+
+       for (i = 0; i < num_buckets; i++) {
+               ret = ocfs2_cp_xattr_bucket(inode, handle,
+                                           src_blk, to_blk, 1);
+               if (ret) {
+                       mlog_errno(ret);
+                       goto out;
+               }
+
+               src_blk += ocfs2_blocks_per_xattr_bucket(inode->i_sb);
+               to_blk += ocfs2_blocks_per_xattr_bucket(inode->i_sb);
+       }
+
+       /* update the old bucket header. */
+       xh = (struct ocfs2_xattr_header *)first_bh->b_data;
+       le16_add_cpu(&xh->xh_num_buckets, -num_buckets);
+
+       ocfs2_journal_dirty(handle, first_bh);
+
+       /* update the new bucket header. */
+       ret = ocfs2_read_block(inode, to_blk_start, &bh);
+       if (ret < 0) {
+               mlog_errno(ret);
+               goto out;
+       }
+
+       ret = ocfs2_journal_access(handle, inode, bh,
+                                  OCFS2_JOURNAL_ACCESS_WRITE);
+       if (ret) {
+               mlog_errno(ret);
+               goto out;
+       }
+
+       xh = (struct ocfs2_xattr_header *)bh->b_data;
+       xh->xh_num_buckets = cpu_to_le16(num_buckets);
+
+       ocfs2_journal_dirty(handle, bh);
+
+       if (first_hash)
+               *first_hash = le32_to_cpu(xh->xh_entries[0].xe_name_hash);
+out:
+       brelse(bh);
+       return ret;
+}
+
+/*
+ * Move half of the xattrs in this cluster to the new cluster.
+ * This function should only be called when bucket size == cluster size.
+ * Otherwise ocfs2_mv_xattr_bucket_cross_cluster should be used instead.
+ */
+static int ocfs2_half_xattr_cluster(struct inode *inode,
+                                   handle_t *handle,
+                                   u64 prev_blk,
+                                   u64 new_blk,
+                                   u32 *first_hash)
+{
+       u16 blk_per_bucket = ocfs2_blocks_per_xattr_bucket(inode->i_sb);
+       int ret, credits = 2 * blk_per_bucket;
+
+       BUG_ON(OCFS2_XATTR_BUCKET_SIZE < OCFS2_SB(inode->i_sb)->s_clustersize);
+
+       ret = ocfs2_extend_trans(handle, credits);
+       if (ret) {
+               mlog_errno(ret);
+               return ret;
+       }
+
+       /* Move half of the xattr in start_blk to the next bucket. */
+       return  ocfs2_half_xattr_bucket(inode, handle, prev_blk,
+                                       new_blk, first_hash, 1);
+}
+
+/*
+ * Move some xattrs from the old cluster to the new one since they are not
+ * contiguous in ocfs2 xattr tree.
+ *
+ * new_blk starts a new separate cluster, and we will move some xattrs from
+ * prev_blk to it. v_start will be set as the first name hash value in this
+ * new cluster so that it can be used as e_cpos during tree insertion and
+ * don't collide with our original b-tree operations. first_bh and header_bh
+ * will also be updated since they will be used in ocfs2_extend_xattr_bucket
+ * to extend the insert bucket.
+ *
+ * The problem is how much xattr should we move to the new one and when should
+ * we update first_bh and header_bh?
+ * 1. If cluster size > bucket size, that means the previous cluster has more
+ *    than 1 bucket, so just move half nums of bucket into the new cluster and
+ *    update the first_bh and header_bh if the insert bucket has been moved
+ *    to the new cluster.
+ * 2. If cluster_size == bucket_size:
+ *    a) If the previous extent rec has more than one cluster and the insert
+ *       place isn't in the last cluster, copy the entire last cluster to the
+ *       new one. This time, we don't need to upate the first_bh and header_bh
+ *       since they will not be moved into the new cluster.
+ *    b) Otherwise, move the bottom half of the xattrs in the last cluster into
+ *       the new one. And we set the extend flag to zero if the insert place is
+ *       moved into the new allocated cluster since no extend is needed.
+ */
+static int ocfs2_adjust_xattr_cross_cluster(struct inode *inode,
+                                           handle_t *handle,
+                                           struct buffer_head **first_bh,
+                                           struct buffer_head **header_bh,
+                                           u64 new_blk,
+                                           u64 prev_blk,
+                                           u32 prev_clusters,
+                                           u32 *v_start,
+                                           int *extend)
+{
+       int ret = 0;
+       int bpc = ocfs2_clusters_to_blocks(inode->i_sb, 1);
+
+       mlog(0, "adjust xattrs from cluster %llu len %u to %llu\n",
+            prev_blk, prev_clusters, new_blk);
+
+       if (ocfs2_xattr_buckets_per_cluster(OCFS2_SB(inode->i_sb)) > 1)
+               ret = ocfs2_mv_xattr_bucket_cross_cluster(inode,
+                                                         handle,
+                                                         first_bh,
+                                                         header_bh,
+                                                         new_blk,
+                                                         prev_blk,
+                                                         prev_clusters,
+                                                         v_start);
+       else {
+               u64 last_blk = prev_blk + bpc * (prev_clusters - 1);
+
+               if (prev_clusters > 1 && (*header_bh)->b_blocknr != last_blk)
+                       ret = ocfs2_cp_xattr_cluster(inode, handle, *first_bh,
+                                                    last_blk, new_blk,
+                                                    v_start);
+               else {
+                       ret = ocfs2_half_xattr_cluster(inode, handle,
+                                                      last_blk, new_blk,
+                                                      v_start);
+
+                       if ((*header_bh)->b_blocknr == last_blk && extend)
+                               *extend = 0;
+               }
+       }
+
+       return ret;
+}
+
+/*
+ * Add a new cluster for xattr storage.
+ *
+ * If the new cluster is contiguous with the previous one, it will be
+ * appended to the same extent record, and num_clusters will be updated.
+ * If not, we will insert a new extent for it and move some xattrs in
+ * the last cluster into the new allocated one.
+ * We also need to limit the maximum size of a btree leaf, otherwise we'll
+ * lose the benefits of hashing because we'll have to search large leaves.
+ * So now the maximum size is OCFS2_MAX_XATTR_TREE_LEAF_SIZE(or clustersize,
+ * if it's bigger).
+ *
+ * first_bh is the first block of the previous extent rec and header_bh
+ * indicates the bucket we will insert the new xattrs. They will be updated
+ * when the header_bh is moved into the new cluster.
+ */
+static int ocfs2_add_new_xattr_cluster(struct inode *inode,
+                                      struct buffer_head *root_bh,
+                                      struct buffer_head **first_bh,
+                                      struct buffer_head **header_bh,
+                                      u32 *num_clusters,
+                                      u32 prev_cpos,
+                                      u64 prev_blkno,
+                                      int *extend)
+{
+       int ret, credits;
+       u16 bpc = ocfs2_clusters_to_blocks(inode->i_sb, 1);
+       u32 prev_clusters = *num_clusters;
+       u32 clusters_to_add = 1, bit_off, num_bits, v_start = 0;
+       u64 block;
+       handle_t *handle = NULL;
+       struct ocfs2_alloc_context *data_ac = NULL;
+       struct ocfs2_alloc_context *meta_ac = NULL;
+       struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
+       struct ocfs2_extent_tree et;
+
+       mlog(0, "Add new xattr cluster for %llu, previous xattr hash = %u, "
+            "previous xattr blkno = %llu\n",
+            (unsigned long long)OCFS2_I(inode)->ip_blkno,
+            prev_cpos, prev_blkno);
+
+       ocfs2_init_xattr_tree_extent_tree(&et, inode, root_bh);
+
+       ret = ocfs2_lock_allocators(inode, &et, clusters_to_add, 0,
+                                   &data_ac, &meta_ac);
+       if (ret) {
+               mlog_errno(ret);
+               goto leave;
+       }
+
+       credits = ocfs2_calc_extend_credits(osb->sb, et.et_root_el,
+                                           clusters_to_add);
+       handle = ocfs2_start_trans(osb, credits);
+       if (IS_ERR(handle)) {
+               ret = PTR_ERR(handle);
+               handle = NULL;
+               mlog_errno(ret);
+               goto leave;
+       }
+
+       ret = ocfs2_journal_access(handle, inode, root_bh,
+                                  OCFS2_JOURNAL_ACCESS_WRITE);
+       if (ret < 0) {
+               mlog_errno(ret);
+               goto leave;
+       }
+
+       ret = __ocfs2_claim_clusters(osb, handle, data_ac, 1,
+                                    clusters_to_add, &bit_off, &num_bits);
+       if (ret < 0) {
+               if (ret != -ENOSPC)
+                       mlog_errno(ret);
+               goto leave;
+       }
+
+       BUG_ON(num_bits > clusters_to_add);
+
+       block = ocfs2_clusters_to_blocks(osb->sb, bit_off);
+       mlog(0, "Allocating %u clusters at block %u for xattr in inode %llu\n",
+            num_bits, bit_off, (unsigned long long)OCFS2_I(inode)->ip_blkno);
+
+       if (prev_blkno + prev_clusters * bpc == block &&
+           (prev_clusters + num_bits) << osb->s_clustersize_bits <=
+            OCFS2_MAX_XATTR_TREE_LEAF_SIZE) {
+               /*
+                * If this cluster is contiguous with the old one and
+                * adding this new cluster, we don't surpass the limit of
+                * OCFS2_MAX_XATTR_TREE_LEAF_SIZE, cool. We will let it be
+                * initialized and used like other buckets in the previous
+                * cluster.
+                * So add it as a contiguous one. The caller will handle
+                * its init process.
+                */
+               v_start = prev_cpos + prev_clusters;
+               *num_clusters = prev_clusters + num_bits;
+               mlog(0, "Add contiguous %u clusters to previous extent rec.\n",
+                    num_bits);
+       } else {
+               ret = ocfs2_adjust_xattr_cross_cluster(inode,
+                                                      handle,
+                                                      first_bh,
+                                                      header_bh,
+                                                      block,
+                                                      prev_blkno,
+                                                      prev_clusters,
+                                                      &v_start,
+                                                      extend);
+               if (ret) {
+                       mlog_errno(ret);
+                       goto leave;
+               }
+       }
+
+       if (handle->h_buffer_credits < credits) {
+               /*
+                * The journal has been restarted before, and don't
+                * have enough space for the insertion, so extend it
+                * here.
+                */
+               ret = ocfs2_extend_trans(handle, credits);
+               if (ret) {
+                       mlog_errno(ret);
+                       goto leave;
+               }
+       }
+       mlog(0, "Insert %u clusters at block %llu for xattr at %u\n",
+            num_bits, block, v_start);
+       ret = ocfs2_insert_extent(osb, handle, inode, &et, v_start, block,
+                                 num_bits, 0, meta_ac);
+       if (ret < 0) {
+               mlog_errno(ret);
+               goto leave;
+       }
+
+       ret = ocfs2_journal_dirty(handle, root_bh);
+       if (ret < 0) {
+               mlog_errno(ret);
+               goto leave;
+       }
+
+leave:
+       if (handle)
+               ocfs2_commit_trans(osb, handle);
+       if (data_ac)
+               ocfs2_free_alloc_context(data_ac);
+       if (meta_ac)
+               ocfs2_free_alloc_context(meta_ac);
+
+       return ret;
+}
+
+/*
+ * Extend a new xattr bucket and move xattrs to the end one by one until
+ * We meet with start_bh. Only move half of the xattrs to the bucket after it.
+ */
+static int ocfs2_extend_xattr_bucket(struct inode *inode,
+                                    struct buffer_head *first_bh,
+                                    struct buffer_head *start_bh,
+                                    u32 num_clusters)
+{
+       int ret, credits;
+       struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
+       u16 blk_per_bucket = ocfs2_blocks_per_xattr_bucket(inode->i_sb);
+       u64 start_blk = start_bh->b_blocknr, end_blk;
+       u32 num_buckets = num_clusters * ocfs2_xattr_buckets_per_cluster(osb);
+       handle_t *handle;
+       struct ocfs2_xattr_header *first_xh =
+                               (struct ocfs2_xattr_header *)first_bh->b_data;
+       u16 bucket = le16_to_cpu(first_xh->xh_num_buckets);
+
+       mlog(0, "extend xattr bucket in %llu, xattr extend rec starting "
+            "from %llu, len = %u\n", start_blk,
+            (unsigned long long)first_bh->b_blocknr, num_clusters);
+
+       BUG_ON(bucket >= num_buckets);
+
+       end_blk = first_bh->b_blocknr + (bucket - 1) * blk_per_bucket;
+
+       /*
+        * We will touch all the buckets after the start_bh(include it).
+        * Add one more bucket and modify the first_bh.
+        */
+       credits = end_blk - start_blk + 2 * blk_per_bucket + 1;
+       handle = ocfs2_start_trans(osb, credits);
+       if (IS_ERR(handle)) {
+               ret = PTR_ERR(handle);
+               handle = NULL;
+               mlog_errno(ret);
+               goto out;
+       }
+
+       ret = ocfs2_journal_access(handle, inode, first_bh,
+                                  OCFS2_JOURNAL_ACCESS_WRITE);
+       if (ret) {
+               mlog_errno(ret);
+               goto commit;
+       }
+
+       while (end_blk != start_blk) {
+               ret = ocfs2_cp_xattr_bucket(inode, handle, end_blk,
+                                           end_blk + blk_per_bucket, 0);
+               if (ret)
+                       goto commit;
+               end_blk -= blk_per_bucket;
+       }
+
+       /* Move half of the xattr in start_blk to the next bucket. */
+       ret = ocfs2_half_xattr_bucket(inode, handle, start_blk,
+                                     start_blk + blk_per_bucket, NULL, 0);
+
+       le16_add_cpu(&first_xh->xh_num_buckets, 1);
+       ocfs2_journal_dirty(handle, first_bh);
+
+commit:
+       ocfs2_commit_trans(osb, handle);
+out:
+       return ret;
+}
+
+/*
+ * Add new xattr bucket in an extent record and adjust the buckets accordingly.
+ * xb_bh is the ocfs2_xattr_block.
+ * We will move all the buckets starting from header_bh to the next place. As
+ * for this one, half num of its xattrs will be moved to the next one.
+ *
+ * We will allocate a new cluster if current cluster is full and adjust
+ * header_bh and first_bh if the insert place is moved to the new cluster.
+ */
+static int ocfs2_add_new_xattr_bucket(struct inode *inode,
+                                     struct buffer_head *xb_bh,
+                                     struct buffer_head *header_bh)
+{
+       struct ocfs2_xattr_header *first_xh = NULL;
+       struct buffer_head *first_bh = NULL;
+       struct ocfs2_xattr_block *xb =
+                       (struct ocfs2_xattr_block *)xb_bh->b_data;
+       struct ocfs2_xattr_tree_root *xb_root = &xb->xb_attrs.xb_root;
+       struct ocfs2_extent_list *el = &xb_root->xt_list;
+       struct ocfs2_xattr_header *xh =
+                       (struct ocfs2_xattr_header *)header_bh->b_data;
+       u32 name_hash = le32_to_cpu(xh->xh_entries[0].xe_name_hash);
+       struct super_block *sb = inode->i_sb;
+       struct ocfs2_super *osb = OCFS2_SB(sb);
+       int ret, num_buckets, extend = 1;
+       u64 p_blkno;
+       u32 e_cpos, num_clusters;
+
+       mlog(0, "Add new xattr bucket starting form %llu\n",
+            (unsigned long long)header_bh->b_blocknr);
+
+       /*
+        * Add refrence for header_bh here because it may be
+        * changed in ocfs2_add_new_xattr_cluster and we need
+        * to free it in the end.
+        */
+       get_bh(header_bh);
+
+       ret = ocfs2_xattr_get_rec(inode, name_hash, &p_blkno, &e_cpos,
+                                 &num_clusters, el);
+       if (ret) {
+               mlog_errno(ret);
+               goto out;
+       }
+
+       ret = ocfs2_read_block(inode, p_blkno, &first_bh);
+       if (ret) {
+               mlog_errno(ret);
+               goto out;
+       }
+
+       num_buckets = ocfs2_xattr_buckets_per_cluster(osb) * num_clusters;
+       first_xh = (struct ocfs2_xattr_header *)first_bh->b_data;
+
+       if (num_buckets == le16_to_cpu(first_xh->xh_num_buckets)) {
+               ret = ocfs2_add_new_xattr_cluster(inode,
+                                                 xb_bh,
+                                                 &first_bh,
+                                                 &header_bh,
+                                                 &num_clusters,
+                                                 e_cpos,
+                                                 p_blkno,
+                                                 &extend);
+               if (ret) {
+                       mlog_errno(ret);
+                       goto out;
+               }
+       }
+
+       if (extend)
+               ret = ocfs2_extend_xattr_bucket(inode,
+                                               first_bh,
+                                               header_bh,
+                                               num_clusters);
+       if (ret)
+               mlog_errno(ret);
+out:
+       brelse(first_bh);
+       brelse(header_bh);
+       return ret;
+}
+
+static inline char *ocfs2_xattr_bucket_get_val(struct inode *inode,
+                                       struct ocfs2_xattr_bucket *bucket,
+                                       int offs)
+{
+       int block_off = offs >> inode->i_sb->s_blocksize_bits;
+
+       offs = offs % inode->i_sb->s_blocksize;
+       return bucket->bhs[block_off]->b_data + offs;
+}
+
+/*
+ * Handle the normal xattr set, including replace, delete and new.
+ *
+ * Note: "local" indicates the real data's locality. So we can't
+ * just its bucket locality by its length.
+ */
+static void ocfs2_xattr_set_entry_normal(struct inode *inode,
+                                        struct ocfs2_xattr_info *xi,
+                                        struct ocfs2_xattr_search *xs,
+                                        u32 name_hash,
+                                        int local)
+{
+       struct ocfs2_xattr_entry *last, *xe;
+       int name_len = strlen(xi->name);
+       struct ocfs2_xattr_header *xh = xs->header;
+       u16 count = le16_to_cpu(xh->xh_count), start;
+       size_t blocksize = inode->i_sb->s_blocksize;
+       char *val;
+       size_t offs, size, new_size;
+
+       last = &xh->xh_entries[count];
+       if (!xs->not_found) {
+               xe = xs->here;
+               offs = le16_to_cpu(xe->xe_name_offset);
+               if (ocfs2_xattr_is_local(xe))
+                       size = OCFS2_XATTR_SIZE(name_len) +
+                       OCFS2_XATTR_SIZE(le64_to_cpu(xe->xe_value_size));
+               else
+                       size = OCFS2_XATTR_SIZE(name_len) +
+                       OCFS2_XATTR_SIZE(OCFS2_XATTR_ROOT_SIZE);
+
+               /*
+                * If the new value will be stored outside, xi->value has been
+                * initalized as an empty ocfs2_xattr_value_root, and the same
+                * goes with xi->value_len, so we can set new_size safely here.
+                * See ocfs2_xattr_set_in_bucket.
+                */
+               new_size = OCFS2_XATTR_SIZE(name_len) +
+                          OCFS2_XATTR_SIZE(xi->value_len);
+
+               le16_add_cpu(&xh->xh_name_value_len, -size);
+               if (xi->value) {
+                       if (new_size > size)
+                               goto set_new_name_value;
+
+                       /* Now replace the old value with new one. */
+                       if (local)
+                               xe->xe_value_size = cpu_to_le64(xi->value_len);
+                       else
+                               xe->xe_value_size = 0;
+
+                       val = ocfs2_xattr_bucket_get_val(inode,
+                                                        &xs->bucket, offs);
+                       memset(val + OCFS2_XATTR_SIZE(name_len), 0,
+                              size - OCFS2_XATTR_SIZE(name_len));
+                       if (OCFS2_XATTR_SIZE(xi->value_len) > 0)
+                               memcpy(val + OCFS2_XATTR_SIZE(name_len),
+                                      xi->value, xi->value_len);
+
+                       le16_add_cpu(&xh->xh_name_value_len, new_size);
+                       ocfs2_xattr_set_local(xe, local);
+                       return;
+               } else {
+                       /*
+                        * Remove the old entry if there is more than one.
+                        * We don't remove the last entry so that we can
+                        * use it to indicate the hash value of the empty
+                        * bucket.
+                        */
+                       last -= 1;
+                       le16_add_cpu(&xh->xh_count, -1);
+                       if (xh->xh_count) {
+                               memmove(xe, xe + 1,
+                                       (void *)last - (void *)xe);
+                               memset(last, 0,
+                                      sizeof(struct ocfs2_xattr_entry));
+                       } else
+                               xh->xh_free_start =
+                                       cpu_to_le16(OCFS2_XATTR_BUCKET_SIZE);
+
+                       return;
+               }
+       } else {
+               /* find a new entry for insert. */
+               int low = 0, high = count - 1, tmp;
+               struct ocfs2_xattr_entry *tmp_xe;
+
+               while (low <= high && count) {
+                       tmp = (low + high) / 2;
+                       tmp_xe = &xh->xh_entries[tmp];
+
+                       if (name_hash > le32_to_cpu(tmp_xe->xe_name_hash))
+                               low = tmp + 1;
+                       else if (name_hash <
+                                le32_to_cpu(tmp_xe->xe_name_hash))
+                               high = tmp - 1;
+                       else {
+                               low = tmp;
+                               break;
+                       }
+               }
+
+               xe = &xh->xh_entries[low];
+               if (low != count)
+                       memmove(xe + 1, xe, (void *)last - (void *)xe);
+
+               le16_add_cpu(&xh->xh_count, 1);
+               memset(xe, 0, sizeof(struct ocfs2_xattr_entry));
+               xe->xe_name_hash = cpu_to_le32(name_hash);
+               xe->xe_name_len = name_len;
+               ocfs2_xattr_set_type(xe, xi->name_index);
+       }
+
+set_new_name_value:
+       /* Insert the new name+value. */
+       size = OCFS2_XATTR_SIZE(name_len) + OCFS2_XATTR_SIZE(xi->value_len);
+
+       /*
+        * We must make sure that the name/value pair
+        * exists in the same block.
+        */
+       offs = le16_to_cpu(xh->xh_free_start);
+       start = offs - size;
+
+       if (start >> inode->i_sb->s_blocksize_bits !=
+           (offs - 1) >> inode->i_sb->s_blocksize_bits) {
+               offs = offs - offs % blocksize;
+               xh->xh_free_start = cpu_to_le16(offs);
+       }
+
+       val = ocfs2_xattr_bucket_get_val(inode,
+                                        &xs->bucket, offs - size);
+       xe->xe_name_offset = cpu_to_le16(offs - size);
+
+       memset(val, 0, size);
+       memcpy(val, xi->name, name_len);
+       memcpy(val + OCFS2_XATTR_SIZE(name_len), xi->value, xi->value_len);
+
+       xe->xe_value_size = cpu_to_le64(xi->value_len);
+       ocfs2_xattr_set_local(xe, local);
+       xs->here = xe;
+       le16_add_cpu(&xh->xh_free_start, -size);
+       le16_add_cpu(&xh->xh_name_value_len, size);
+
+       return;
+}
+
+static int ocfs2_xattr_bucket_handle_journal(struct inode *inode,
+                                            handle_t *handle,
+                                            struct ocfs2_xattr_search *xs,
+                                            struct buffer_head **bhs,
+                                            u16 bh_num)
+{
+       int ret = 0, off, block_off;
+       struct ocfs2_xattr_entry *xe = xs->here;
+
+       /*
+        * First calculate all the blocks we should journal_access
+        * and journal_dirty. The first block should always be touched.
+        */
+       ret = ocfs2_journal_dirty(handle, bhs[0]);
+       if (ret)
+               mlog_errno(ret);
+
+       /* calc the data. */
+       off = le16_to_cpu(xe->xe_name_offset);
+       block_off = off >> inode->i_sb->s_blocksize_bits;
+       ret = ocfs2_journal_dirty(handle, bhs[block_off]);
+       if (ret)
+               mlog_errno(ret);
+
+       return ret;
+}
+
+/*
+ * Set the xattr entry in the specified bucket.
+ * The bucket is indicated by xs->bucket and it should have the enough
+ * space for the xattr insertion.
+ */
+static int ocfs2_xattr_set_entry_in_bucket(struct inode *inode,
+                                          struct ocfs2_xattr_info *xi,
+                                          struct ocfs2_xattr_search *xs,
+                                          u32 name_hash,
+                                          int local)
+{
+       int i, ret;
+       handle_t *handle = NULL;
+       u16 blk_per_bucket = ocfs2_blocks_per_xattr_bucket(inode->i_sb);
+       struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
+
+       mlog(0, "Set xattr entry len = %lu index = %d in bucket %llu\n",
+            (unsigned long)xi->value_len, xi->name_index,
+            (unsigned long long)xs->bucket.bhs[0]->b_blocknr);
+
+       if (!xs->bucket.bhs[1]) {
+               ret = ocfs2_read_blocks(inode,
+                                       xs->bucket.bhs[0]->b_blocknr + 1,
+                                       blk_per_bucket - 1, &xs->bucket.bhs[1],
+                                       OCFS2_BH_CACHED);
+               if (ret) {
+                       mlog_errno(ret);
+                       goto out;
+               }
+       }
+
+       handle = ocfs2_start_trans(osb, blk_per_bucket);
+       if (IS_ERR(handle)) {
+               ret = PTR_ERR(handle);
+               handle = NULL;
+               mlog_errno(ret);
+               goto out;
+       }
+
+       for (i = 0; i < blk_per_bucket; i++) {
+               ret = ocfs2_journal_access(handle, inode, xs->bucket.bhs[i],
+                                          OCFS2_JOURNAL_ACCESS_WRITE);
+               if (ret < 0) {
+                       mlog_errno(ret);
+                       goto out;
+               }
+       }
+
+       ocfs2_xattr_set_entry_normal(inode, xi, xs, name_hash, local);
+
+       /*Only dirty the blocks we have touched in set xattr. */
+       ret = ocfs2_xattr_bucket_handle_journal(inode, handle, xs,
+                                               xs->bucket.bhs, blk_per_bucket);
+       if (ret)
+               mlog_errno(ret);
+out:
+       ocfs2_commit_trans(osb, handle);
+
+       return ret;
+}
+
+static int ocfs2_xattr_value_update_size(struct inode *inode,
+                                        struct buffer_head *xe_bh,
+                                        struct ocfs2_xattr_entry *xe,
+                                        u64 new_size)
+{
+       int ret;
+       struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
+       handle_t *handle = NULL;
+
+       handle = ocfs2_start_trans(osb, 1);
+       if (handle == NULL) {
+               ret = -ENOMEM;
+               mlog_errno(ret);
+               goto out;
+       }
+
+       ret = ocfs2_journal_access(handle, inode, xe_bh,
+                                  OCFS2_JOURNAL_ACCESS_WRITE);
+       if (ret < 0) {
+               mlog_errno(ret);
+               goto out_commit;
+       }
+
+       xe->xe_value_size = cpu_to_le64(new_size);
+
+       ret = ocfs2_journal_dirty(handle, xe_bh);
+       if (ret < 0)
+               mlog_errno(ret);
+
+out_commit:
+       ocfs2_commit_trans(osb, handle);
+out:
+       return ret;
+}
+
+/*
+ * Truncate the specified xe_off entry in xattr bucket.
+ * bucket is indicated by header_bh and len is the new length.
+ * Both the ocfs2_xattr_value_root and the entry will be updated here.
+ *
+ * Copy the new updated xe and xe_value_root to new_xe and new_xv if needed.
+ */
+static int ocfs2_xattr_bucket_value_truncate(struct inode *inode,
+                                            struct buffer_head *header_bh,
+                                            int xe_off,
+                                            int len)
+{
+       int ret, offset;
+       u64 value_blk;
+       struct buffer_head *value_bh = NULL;
+       struct ocfs2_xattr_value_root *xv;
+       struct ocfs2_xattr_entry *xe;
+       struct ocfs2_xattr_header *xh =
+                       (struct ocfs2_xattr_header *)header_bh->b_data;
+       size_t blocksize = inode->i_sb->s_blocksize;
+
+       xe = &xh->xh_entries[xe_off];
+
+       BUG_ON(!xe || ocfs2_xattr_is_local(xe));
+
+       offset = le16_to_cpu(xe->xe_name_offset) +
+                OCFS2_XATTR_SIZE(xe->xe_name_len);
+
+       value_blk = offset / blocksize;
+
+       /* We don't allow ocfs2_xattr_value to be stored in different block. */
+       BUG_ON(value_blk != (offset + OCFS2_XATTR_ROOT_SIZE - 1) / blocksize);
+       value_blk += header_bh->b_blocknr;
+
+       ret = ocfs2_read_block(inode, value_blk, &value_bh);
+       if (ret) {
+               mlog_errno(ret);
+               goto out;
+       }
+
+       xv = (struct ocfs2_xattr_value_root *)
+               (value_bh->b_data + offset % blocksize);
+
+       mlog(0, "truncate %u in xattr bucket %llu to %d bytes.\n",
+            xe_off, (unsigned long long)header_bh->b_blocknr, len);
+       ret = ocfs2_xattr_value_truncate(inode, value_bh, xv, len);
+       if (ret) {
+               mlog_errno(ret);
+               goto out;
+       }
+
+       ret = ocfs2_xattr_value_update_size(inode, header_bh, xe, len);
+       if (ret) {
+               mlog_errno(ret);
+               goto out;
+       }
+
+out:
+       brelse(value_bh);
+       return ret;
+}
+
+static int ocfs2_xattr_bucket_value_truncate_xs(struct inode *inode,
+                                               struct ocfs2_xattr_search *xs,
+                                               int len)
+{
+       int ret, offset;
+       struct ocfs2_xattr_entry *xe = xs->here;
+       struct ocfs2_xattr_header *xh = (struct ocfs2_xattr_header *)xs->base;
+
+       BUG_ON(!xs->bucket.bhs[0] || !xe || ocfs2_xattr_is_local(xe));
+
+       offset = xe - xh->xh_entries;
+       ret = ocfs2_xattr_bucket_value_truncate(inode, xs->bucket.bhs[0],
+                                               offset, len);
+       if (ret)
+               mlog_errno(ret);
+
+       return ret;
+}
+
+static int ocfs2_xattr_bucket_set_value_outside(struct inode *inode,
+                                               struct ocfs2_xattr_search *xs,
+                                               char *val,
+                                               int value_len)
+{
+       int offset;
+       struct ocfs2_xattr_value_root *xv;
+       struct ocfs2_xattr_entry *xe = xs->here;
+
+       BUG_ON(!xs->base || !xe || ocfs2_xattr_is_local(xe));
+
+       offset = le16_to_cpu(xe->xe_name_offset) +
+                OCFS2_XATTR_SIZE(xe->xe_name_len);
+
+       xv = (struct ocfs2_xattr_value_root *)(xs->base + offset);
+
+       return __ocfs2_xattr_set_value_outside(inode, xv, val, value_len);
+}
+
+static int ocfs2_rm_xattr_cluster(struct inode *inode,
+                                 struct buffer_head *root_bh,
+                                 u64 blkno,
+                                 u32 cpos,
+                                 u32 len)
+{
+       int ret;
+       struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
+       struct inode *tl_inode = osb->osb_tl_inode;
+       handle_t *handle;
+       struct ocfs2_xattr_block *xb =
+                       (struct ocfs2_xattr_block *)root_bh->b_data;
+       struct ocfs2_alloc_context *meta_ac = NULL;
+       struct ocfs2_cached_dealloc_ctxt dealloc;
+       struct ocfs2_extent_tree et;
+
+       ocfs2_init_xattr_tree_extent_tree(&et, inode, root_bh);
+
+       ocfs2_init_dealloc_ctxt(&dealloc);
+
+       mlog(0, "rm xattr extent rec at %u len = %u, start from %llu\n",
+            cpos, len, (unsigned long long)blkno);
+
+       ocfs2_remove_xattr_clusters_from_cache(inode, blkno, len);
+
+       ret = ocfs2_lock_allocators(inode, &et, 0, 1, NULL, &meta_ac);
+       if (ret) {
+               mlog_errno(ret);
+               return ret;
+       }
+
+       mutex_lock(&tl_inode->i_mutex);
+
+       if (ocfs2_truncate_log_needs_flush(osb)) {
+               ret = __ocfs2_flush_truncate_log(osb);
+               if (ret < 0) {
+                       mlog_errno(ret);
+                       goto out;
+               }
+       }
+
+       handle = ocfs2_start_trans(osb, OCFS2_REMOVE_EXTENT_CREDITS);
+       if (handle == NULL) {
+               ret = -ENOMEM;
+               mlog_errno(ret);
+               goto out;
+       }
+
+       ret = ocfs2_journal_access(handle, inode, root_bh,
+                                  OCFS2_JOURNAL_ACCESS_WRITE);
+       if (ret) {
+               mlog_errno(ret);
+               goto out_commit;
+       }
+
+       ret = ocfs2_remove_extent(inode, &et, cpos, len, handle, meta_ac,
+                                 &dealloc);
+       if (ret) {
+               mlog_errno(ret);
+               goto out_commit;
+       }
+
+       le32_add_cpu(&xb->xb_attrs.xb_root.xt_clusters, -len);
+
+       ret = ocfs2_journal_dirty(handle, root_bh);
+       if (ret) {
+               mlog_errno(ret);
+               goto out_commit;
+       }
+
+       ret = ocfs2_truncate_log_append(osb, handle, blkno, len);
+       if (ret)
+               mlog_errno(ret);
+
+out_commit:
+       ocfs2_commit_trans(osb, handle);
+out:
+       ocfs2_schedule_truncate_log_flush(osb, 1);
+
+       mutex_unlock(&tl_inode->i_mutex);
+
+       if (meta_ac)
+               ocfs2_free_alloc_context(meta_ac);
+
+       ocfs2_run_deallocs(osb, &dealloc);
+
+       return ret;
+}
+
+static void ocfs2_xattr_bucket_remove_xs(struct inode *inode,
+                                        struct ocfs2_xattr_search *xs)
+{
+       handle_t *handle = NULL;
+       struct ocfs2_xattr_header *xh = xs->bucket.xh;
+       struct ocfs2_xattr_entry *last = &xh->xh_entries[
+                                               le16_to_cpu(xh->xh_count) - 1];
+       int ret = 0;
+
+       handle = ocfs2_start_trans((OCFS2_SB(inode->i_sb)), 1);
+       if (IS_ERR(handle)) {
+               ret = PTR_ERR(handle);
+               mlog_errno(ret);
+               return;
+       }
+
+       ret = ocfs2_journal_access(handle, inode, xs->bucket.bhs[0],
+                                  OCFS2_JOURNAL_ACCESS_WRITE);
+       if (ret) {
+               mlog_errno(ret);
+               goto out_commit;
+       }
+
+       /* Remove the old entry. */
+       memmove(xs->here, xs->here + 1,
+               (void *)last - (void *)xs->here);
+       memset(last, 0, sizeof(struct ocfs2_xattr_entry));
+       le16_add_cpu(&xh->xh_count, -1);
+
+       ret = ocfs2_journal_dirty(handle, xs->bucket.bhs[0]);
+       if (ret < 0)
+               mlog_errno(ret);
+out_commit:
+       ocfs2_commit_trans(OCFS2_SB(inode->i_sb), handle);
+}
+
+/*
+ * Set the xattr name/value in the bucket specified in xs.
+ *
+ * As the new value in xi may be stored in the bucket or in an outside cluster,
+ * we divide the whole process into 3 steps:
+ * 1. insert name/value in the bucket(ocfs2_xattr_set_entry_in_bucket)
+ * 2. truncate of the outside cluster(ocfs2_xattr_bucket_value_truncate_xs)
+ * 3. Set the value to the outside cluster(ocfs2_xattr_bucket_set_value_outside)
+ * 4. If the clusters for the new outside value can't be allocated, we need
+ *    to free the xattr we allocated in set.
+ */
+static int ocfs2_xattr_set_in_bucket(struct inode *inode,
+                                    struct ocfs2_xattr_info *xi,
+                                    struct ocfs2_xattr_search *xs)
+{
+       int ret, local = 1;
+       size_t value_len;
+       char *val = (char *)xi->value;
+       struct ocfs2_xattr_entry *xe = xs->here;
+       u32 name_hash = ocfs2_xattr_name_hash(inode, xi->name,
+                                             strlen(xi->name));
+
+       if (!xs->not_found && !ocfs2_xattr_is_local(xe)) {
+               /*
+                * We need to truncate the xattr storage first.
+                *
+                * If both the old and new value are stored to
+                * outside block, we only need to truncate
+                * the storage and then set the value outside.
+                *
+                * If the new value should be stored within block,
+                * we should free all the outside block first and
+                * the modification to the xattr block will be done
+                * by following steps.
+                */
+               if (xi->value_len > OCFS2_XATTR_INLINE_SIZE)
+                       value_len = xi->value_len;
+               else
+                       value_len = 0;
+
+               ret = ocfs2_xattr_bucket_value_truncate_xs(inode, xs,
+                                                          value_len);
+               if (ret)
+                       goto out;
+
+               if (value_len)
+                       goto set_value_outside;
+       }
+
+       value_len = xi->value_len;
+       /* So we have to handle the inside block change now. */
+       if (value_len > OCFS2_XATTR_INLINE_SIZE) {
+               /*
+                * If the new value will be stored outside of block,
+                * initalize a new empty value root and insert it first.
+                */
+               local = 0;
+               xi->value = &def_xv;
+               xi->value_len = OCFS2_XATTR_ROOT_SIZE;
+       }
+
+       ret = ocfs2_xattr_set_entry_in_bucket(inode, xi, xs, name_hash, local);
+       if (ret) {
+               mlog_errno(ret);
+               goto out;
+       }
+
+       if (value_len <= OCFS2_XATTR_INLINE_SIZE)
+               goto out;
+
+       /* allocate the space now for the outside block storage. */
+       ret = ocfs2_xattr_bucket_value_truncate_xs(inode, xs,
+                                                  value_len);
+       if (ret) {
+               mlog_errno(ret);
+
+               if (xs->not_found) {
+                       /*
+                        * We can't allocate enough clusters for outside
+                        * storage and we have allocated xattr already,
+                        * so need to remove it.
+                        */
+                       ocfs2_xattr_bucket_remove_xs(inode, xs);
+               }
+               goto out;
+       }
+
+set_value_outside:
+       ret = ocfs2_xattr_bucket_set_value_outside(inode, xs, val, value_len);
+out:
+       return ret;
+}
+
+/* check whether the xattr bucket is filled up with the same hash value. */
+static int ocfs2_check_xattr_bucket_collision(struct inode *inode,
+                                             struct ocfs2_xattr_bucket *bucket)
+{
+       struct ocfs2_xattr_header *xh = bucket->xh;
+
+       if (xh->xh_entries[le16_to_cpu(xh->xh_count) - 1].xe_name_hash ==
+           xh->xh_entries[0].xe_name_hash) {
+               mlog(ML_ERROR, "Too much hash collision in xattr bucket %llu, "
+                    "hash = %u\n",
+                    (unsigned long long)bucket->bhs[0]->b_blocknr,
+                    le32_to_cpu(xh->xh_entries[0].xe_name_hash));
+               return -ENOSPC;
+       }
+
+       return 0;
+}
+
+static int ocfs2_xattr_set_entry_index_block(struct inode *inode,
+                                            struct ocfs2_xattr_info *xi,
+                                            struct ocfs2_xattr_search *xs)
+{
+       struct ocfs2_xattr_header *xh;
+       struct ocfs2_xattr_entry *xe;
+       u16 count, header_size, xh_free_start;
+       int i, free, max_free, need, old;
+       size_t value_size = 0, name_len = strlen(xi->name);
+       size_t blocksize = inode->i_sb->s_blocksize;
+       int ret, allocation = 0;
+       u16 blk_per_bucket = ocfs2_blocks_per_xattr_bucket(inode->i_sb);
+
+       mlog_entry("Set xattr %s in xattr index block\n", xi->name);
+
+try_again:
+       xh = xs->header;
+       count = le16_to_cpu(xh->xh_count);
+       xh_free_start = le16_to_cpu(xh->xh_free_start);
+       header_size = sizeof(struct ocfs2_xattr_header) +
+                       count * sizeof(struct ocfs2_xattr_entry);
+       max_free = OCFS2_XATTR_BUCKET_SIZE -
+               le16_to_cpu(xh->xh_name_value_len) - header_size;
+
+       mlog_bug_on_msg(header_size > blocksize, "bucket %llu has header size "
+                       "of %u which exceed block size\n",
+                       (unsigned long long)xs->bucket.bhs[0]->b_blocknr,
+                       header_size);
+
+       if (xi->value && xi->value_len > OCFS2_XATTR_INLINE_SIZE)
+               value_size = OCFS2_XATTR_ROOT_SIZE;
+       else if (xi->value)
+               value_size = OCFS2_XATTR_SIZE(xi->value_len);
+
+       if (xs->not_found)
+               need = sizeof(struct ocfs2_xattr_entry) +
+                       OCFS2_XATTR_SIZE(name_len) + value_size;
+       else {
+               need = value_size + OCFS2_XATTR_SIZE(name_len);
+
+               /*
+                * We only replace the old value if the new length is smaller
+                * than the old one. Otherwise we will allocate new space in the
+                * bucket to store it.
+                */
+               xe = xs->here;
+               if (ocfs2_xattr_is_local(xe))
+                       old = OCFS2_XATTR_SIZE(le64_to_cpu(xe->xe_value_size));
+               else
+                       old = OCFS2_XATTR_SIZE(OCFS2_XATTR_ROOT_SIZE);
+
+               if (old >= value_size)
+                       need = 0;
+       }
+
+       free = xh_free_start - header_size;
+       /*
+        * We need to make sure the new name/value pair
+        * can exist in the same block.
+        */
+       if (xh_free_start % blocksize < need)
+               free -= xh_free_start % blocksize;
+
+       mlog(0, "xs->not_found = %d, in xattr bucket %llu: free = %d, "
+            "need = %d, max_free = %d, xh_free_start = %u, xh_name_value_len ="
+            " %u\n", xs->not_found,
+            (unsigned long long)xs->bucket.bhs[0]->b_blocknr,
+            free, need, max_free, le16_to_cpu(xh->xh_free_start),
+            le16_to_cpu(xh->xh_name_value_len));
+
+       if (free < need || count == ocfs2_xattr_max_xe_in_bucket(inode->i_sb)) {
+               if (need <= max_free &&
+                   count < ocfs2_xattr_max_xe_in_bucket(inode->i_sb)) {
+                       /*
+                        * We can create the space by defragment. Since only the
+                        * name/value will be moved, the xe shouldn't be changed
+                        * in xs.
+                        */
+                       ret = ocfs2_defrag_xattr_bucket(inode, &xs->bucket);
+                       if (ret) {
+                               mlog_errno(ret);
+                               goto out;
+                       }
+
+                       xh_free_start = le16_to_cpu(xh->xh_free_start);
+                       free = xh_free_start - header_size;
+                       if (xh_free_start % blocksize < need)
+                               free -= xh_free_start % blocksize;
+
+                       if (free >= need)
+                               goto xattr_set;
+
+                       mlog(0, "Can't get enough space for xattr insert by "
+                            "defragment. Need %u bytes, but we have %d, so "
+                            "allocate new bucket for it.\n", need, free);
+               }
+
+               /*
+                * We have to add new buckets or clusters and one
+                * allocation should leave us enough space for insert.
+                */
+               BUG_ON(allocation);
+
+               /*
+                * We do not allow for overlapping ranges between buckets. And
+                * the maximum number of collisions we will allow for then is
+                * one bucket's worth, so check it here whether we need to
+                * add a new bucket for the insert.
+                */
+               ret = ocfs2_check_xattr_bucket_collision(inode, &xs->bucket);
+               if (ret) {
+                       mlog_errno(ret);
+                       goto out;
+               }
+
+               ret = ocfs2_add_new_xattr_bucket(inode,
+                                                xs->xattr_bh,
+                                                xs->bucket.bhs[0]);
+               if (ret) {
+                       mlog_errno(ret);
+                       goto out;
+               }
+
+               for (i = 0; i < blk_per_bucket; i++)
+                       brelse(xs->bucket.bhs[i]);
+
+               memset(&xs->bucket, 0, sizeof(xs->bucket));
+
+               ret = ocfs2_xattr_index_block_find(inode, xs->xattr_bh,
+                                                  xi->name_index,
+                                                  xi->name, xs);
+               if (ret && ret != -ENODATA)
+                       goto out;
+               xs->not_found = ret;
+               allocation = 1;
+               goto try_again;
+       }
+
+xattr_set:
+       ret = ocfs2_xattr_set_in_bucket(inode, xi, xs);
+out:
+       mlog_exit(ret);
+       return ret;
+}
+
+static int ocfs2_delete_xattr_in_bucket(struct inode *inode,
+                                       struct ocfs2_xattr_bucket *bucket,
+                                       void *para)
+{
+       int ret = 0;
+       struct ocfs2_xattr_header *xh = bucket->xh;
+       u16 i;
+       struct ocfs2_xattr_entry *xe;
+
+       for (i = 0; i < le16_to_cpu(xh->xh_count); i++) {
+               xe = &xh->xh_entries[i];
+               if (ocfs2_xattr_is_local(xe))
+                       continue;
+
+               ret = ocfs2_xattr_bucket_value_truncate(inode,
+                                                       bucket->bhs[0],
+                                                       i, 0);
+               if (ret) {
+                       mlog_errno(ret);
+                       break;
+               }
+       }
+
+       return ret;
+}
+
+static int ocfs2_delete_xattr_index_block(struct inode *inode,
+                                         struct buffer_head *xb_bh)
+{
+       struct ocfs2_xattr_block *xb =
+                       (struct ocfs2_xattr_block *)xb_bh->b_data;
+       struct ocfs2_extent_list *el = &xb->xb_attrs.xb_root.xt_list;
+       int ret = 0;
+       u32 name_hash = UINT_MAX, e_cpos, num_clusters;
+       u64 p_blkno;
+
+       if (le16_to_cpu(el->l_next_free_rec) == 0)
+               return 0;
+
+       while (name_hash > 0) {
+               ret = ocfs2_xattr_get_rec(inode, name_hash, &p_blkno,
+                                         &e_cpos, &num_clusters, el);
+               if (ret) {
+                       mlog_errno(ret);
+                       goto out;
+               }
+
+               ret = ocfs2_iterate_xattr_buckets(inode, p_blkno, num_clusters,
+                                                 ocfs2_delete_xattr_in_bucket,
+                                                 NULL);
+               if (ret) {
+                       mlog_errno(ret);
+                       goto out;
+               }
+
+               ret = ocfs2_rm_xattr_cluster(inode, xb_bh,
+                                            p_blkno, e_cpos, num_clusters);
+               if (ret) {
+                       mlog_errno(ret);
+                       break;
+               }
+
+               if (e_cpos == 0)
+                       break;
+
+               name_hash = e_cpos - 1;
+       }
+
+out:
+       return ret;
+}
+
+/*
+ * 'trusted' attributes support
+ */
+
+#define XATTR_TRUSTED_PREFIX "trusted."
+
+static size_t ocfs2_xattr_trusted_list(struct inode *inode, char *list,
+                                      size_t list_size, const char *name,
+                                      size_t name_len)
+{
+       const size_t prefix_len = sizeof(XATTR_TRUSTED_PREFIX) - 1;
+       const size_t total_len = prefix_len + name_len + 1;
+
+       if (list && total_len <= list_size) {
+               memcpy(list, XATTR_TRUSTED_PREFIX, prefix_len);
+               memcpy(list + prefix_len, name, name_len);
+               list[prefix_len + name_len] = '\0';
+       }
+       return total_len;
+}
+
+static int ocfs2_xattr_trusted_get(struct inode *inode, const char *name,
+                                  void *buffer, size_t size)
+{
+       if (strcmp(name, "") == 0)
+               return -EINVAL;
+       return ocfs2_xattr_get(inode, OCFS2_XATTR_INDEX_TRUSTED, name,
+                              buffer, size);
+}
+
+static int ocfs2_xattr_trusted_set(struct inode *inode, const char *name,
+                                  const void *value, size_t size, int flags)
+{
+       if (strcmp(name, "") == 0)
+               return -EINVAL;
+
+       return ocfs2_xattr_set(inode, OCFS2_XATTR_INDEX_TRUSTED, name, value,
+                              size, flags);
+}
+
+struct xattr_handler ocfs2_xattr_trusted_handler = {
+       .prefix = XATTR_TRUSTED_PREFIX,
+       .list   = ocfs2_xattr_trusted_list,
+       .get    = ocfs2_xattr_trusted_get,
+       .set    = ocfs2_xattr_trusted_set,
+};
+
+
+/*
+ * 'user' attributes support
+ */
+
+#define XATTR_USER_PREFIX "user."
+
+static size_t ocfs2_xattr_user_list(struct inode *inode, char *list,
+                                   size_t list_size, const char *name,
+                                   size_t name_len)
+{
+       const size_t prefix_len = sizeof(XATTR_USER_PREFIX) - 1;
+       const size_t total_len = prefix_len + name_len + 1;
+       struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
+
+       if (osb->s_mount_opt & OCFS2_MOUNT_NOUSERXATTR)
+               return 0;
+
+       if (list && total_len <= list_size) {
+               memcpy(list, XATTR_USER_PREFIX, prefix_len);
+               memcpy(list + prefix_len, name, name_len);
+               list[prefix_len + name_len] = '\0';
+       }
+       return total_len;
+}
+
+static int ocfs2_xattr_user_get(struct inode *inode, const char *name,
+                               void *buffer, size_t size)
+{
+       struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
+
+       if (strcmp(name, "") == 0)
+               return -EINVAL;
+       if (osb->s_mount_opt & OCFS2_MOUNT_NOUSERXATTR)
+               return -EOPNOTSUPP;
+       return ocfs2_xattr_get(inode, OCFS2_XATTR_INDEX_USER, name,
+                              buffer, size);
+}
+
+static int ocfs2_xattr_user_set(struct inode *inode, const char *name,
+                               const void *value, size_t size, int flags)
+{
+       struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
+
+       if (strcmp(name, "") == 0)
+               return -EINVAL;
+       if (osb->s_mount_opt & OCFS2_MOUNT_NOUSERXATTR)
+               return -EOPNOTSUPP;
+
+       return ocfs2_xattr_set(inode, OCFS2_XATTR_INDEX_USER, name, value,
+                              size, flags);
+}
+
+struct xattr_handler ocfs2_xattr_user_handler = {
+       .prefix = XATTR_USER_PREFIX,
+       .list   = ocfs2_xattr_user_list,
+       .get    = ocfs2_xattr_user_get,
+       .set    = ocfs2_xattr_user_set,
+};
diff --git a/fs/ocfs2/xattr.h b/fs/ocfs2/xattr.h
new file mode 100644 (file)
index 0000000..c25c7c6
--- /dev/null
@@ -0,0 +1,68 @@
+/* -*- mode: c; c-basic-offset: 8; -*-
+ * vim: noexpandtab sw=8 ts=8 sts=0:
+ *
+ * xattr.h
+ *
+ * Function prototypes
+ *
+ * Copyright (C) 2008 Oracle.  All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 021110-1307, USA.
+ */
+
+#ifndef OCFS2_XATTR_H
+#define OCFS2_XATTR_H
+
+#include <linux/init.h>
+#include <linux/xattr.h>
+
+enum ocfs2_xattr_type {
+       OCFS2_XATTR_INDEX_USER = 1,
+       OCFS2_XATTR_INDEX_POSIX_ACL_ACCESS,
+       OCFS2_XATTR_INDEX_POSIX_ACL_DEFAULT,
+       OCFS2_XATTR_INDEX_TRUSTED,
+       OCFS2_XATTR_INDEX_SECURITY,
+       OCFS2_XATTR_MAX
+};
+
+extern struct xattr_handler ocfs2_xattr_user_handler;
+extern struct xattr_handler ocfs2_xattr_trusted_handler;
+
+extern ssize_t ocfs2_listxattr(struct dentry *, char *, size_t);
+extern int ocfs2_xattr_get(struct inode *, int, const char *, void *, size_t);
+extern int ocfs2_xattr_set(struct inode *, int, const char *, const void *,
+                          size_t, int);
+extern int ocfs2_xattr_remove(struct inode *inode, struct buffer_head *di_bh);
+extern struct xattr_handler *ocfs2_xattr_handlers[];
+
+static inline u16 ocfs2_xattr_buckets_per_cluster(struct ocfs2_super *osb)
+{
+       return (1 << osb->s_clustersize_bits) / OCFS2_XATTR_BUCKET_SIZE;
+}
+
+static inline u16 ocfs2_blocks_per_xattr_bucket(struct super_block *sb)
+{
+       return OCFS2_XATTR_BUCKET_SIZE / (1 << sb->s_blocksize_bits);
+}
+
+static inline u16 ocfs2_xattr_max_xe_in_bucket(struct super_block *sb)
+{
+       u16 len = sb->s_blocksize -
+                offsetof(struct ocfs2_xattr_header, xh_entries);
+
+       return len / sizeof(struct ocfs2_xattr_entry);
+}
+#endif /* OCFS2_XATTR_H */
index 66c1ab87656c24adfe3342f702b4243d3c5f02da..b675a49c182333eb6a38c321d288ca9ae6c4f46b 100644 (file)
@@ -683,6 +683,7 @@ static int cmdline_read_proc(char *page, char **start, off_t off,
        return proc_calc_metrics(page, start, off, count, eof, len);
 }
 
+#ifdef CONFIG_FILE_LOCKING
 static int locks_open(struct inode *inode, struct file *filp)
 {
        return seq_open(filp, &locks_seq_operations);
@@ -694,6 +695,7 @@ static const struct file_operations proc_locks_operations = {
        .llseek         = seq_lseek,
        .release        = seq_release,
 };
+#endif /* CONFIG_FILE_LOCKING */
 
 static int execdomains_read_proc(char *page, char **start, off_t off,
                                 int count, int *eof, void *data)
@@ -887,7 +889,9 @@ void __init proc_misc_init(void)
 #ifdef CONFIG_PRINTK
        proc_create("kmsg", S_IRUSR, NULL, &proc_kmsg_operations);
 #endif
+#ifdef CONFIG_FILE_LOCKING
        proc_create("locks", 0, NULL, &proc_locks_operations);
+#endif
        proc_create("devices", 0, NULL, &proc_devinfo_operations);
        proc_create("cpuinfo", 0, NULL, &proc_cpuinfo_operations);
 #ifdef CONFIG_BLOCK
index ecf007df77437cb14396bcd55ad24e9439677d48..1412b4ab202f1e753324e734d42dc92bbb635e7e 100644 (file)
@@ -39,7 +39,6 @@ extern int atari_dont_touch_floppy_select;
 #define MACH_IS_TT     ((atari_mch_cookie >> 16) == ATARI_MCH_TT)
 #define MACH_IS_FALCON ((atari_mch_cookie >> 16) == ATARI_MCH_FALCON)
 #define MACH_IS_MEDUSA (atari_mch_type == ATARI_MACH_MEDUSA)
-#define MACH_IS_HADES  (atari_mch_type == ATARI_MACH_HADES)
 #define MACH_IS_AB40   (atari_mch_type == ATARI_MACH_AB40)
 
 /* values for atari_switches */
index 91f7944333d49d6ce2f861036cddf0492ce76b9c..26f505488c1197dbe004cc6486d8c41c7285ab65 100644 (file)
@@ -74,6 +74,14 @@ extern void dma_sync_single_for_device(struct device *, dma_addr_t, size_t,
 extern void dma_sync_sg_for_device(struct device *, struct scatterlist *, int,
                                   enum dma_data_direction);
 
+static inline void dma_sync_single_range_for_device(struct device *dev,
+               dma_addr_t dma_handle, unsigned long offset, size_t size,
+               enum dma_data_direction direction)
+{
+       /* just sync everything for now */
+       dma_sync_single_for_device(dev, dma_handle, offset + size, direction);
+}
+
 static inline void dma_sync_single_for_cpu(struct device *dev, dma_addr_t handle,
                                           size_t size, enum dma_data_direction dir)
 {
@@ -84,6 +92,14 @@ static inline void dma_sync_sg_for_cpu(struct device *dev, struct scatterlist *s
 {
 }
 
+static inline void dma_sync_single_range_for_cpu(struct device *dev,
+               dma_addr_t dma_handle, unsigned long offset, size_t size,
+               enum dma_data_direction direction)
+{
+       /* just sync everything for now */
+       dma_sync_single_for_cpu(dev, dma_handle, offset + size, direction);
+}
+
 static inline int dma_mapping_error(struct device *dev, dma_addr_t handle)
 {
        return 0;
index d0c9e61e57b448e5aca2f4955467c72d2b3a7758..4240fbc946f86699b4211398c6ced340c9c4a0a8 100644 (file)
 extern int request_dma(unsigned int dmanr, const char * device_id);    /* reserve a DMA channel */
 extern void free_dma(unsigned int dmanr);      /* release it again */
 
-#ifdef CONFIG_PCI
-extern int isa_dma_bridge_buggy;
-#else
 #define isa_dma_bridge_buggy    (0)
-#endif
 
 #endif /* _M68K_DMA_H */
index f8f6b185d793b16d7dfe8781098524ea991ee762..5202f5a5b420acbeaf50868cd604768bbf6f9444 100644 (file)
@@ -31,7 +31,7 @@
  */
 
 /* the following macro is used when enabling interrupts */
-#if defined(MACH_ATARI_ONLY) && !defined(CONFIG_HADES)
+#if defined(MACH_ATARI_ONLY)
        /* block out HSYNC on the atari */
 #define ALLOWINT       (~0x400)
 #define        MAX_NOINT_IPL   3
index 657187f0c7c2f0ef834817e622c330727d2bcd82..9e673e3bd4344ae2947b752deb50e0a58db8bb59 100644 (file)
@@ -7,15 +7,12 @@
  *            - added skeleton for GG-II and Amiga PCMCIA
  * 2/3/01 RZ: - moved a few more defs into raw_io.h
  *
- * inX/outX/readX/writeX should not be used by any driver unless it does
- * ISA or PCI access. Other drivers should use function defined in raw_io.h
+ * inX/outX should not be used by any driver unless it does
+ * ISA access. Other drivers should use function defined in raw_io.h
  * or define its own macros on top of these.
  *
- *    inX(),outX()              are for PCI and ISA I/O
- *    readX(),writeX()          are for PCI memory
+ *    inX(),outX()              are for ISA I/O
  *    isa_readX(),isa_writeX()  are for ISA memory
- *
- * moved mem{cpy,set}_*io inside CONFIG_PCI
  */
 
 #ifndef _IO_H
@@ -256,10 +253,7 @@ static inline void isa_delay(void)
        (ISA_SEX ? raw_outsl(isa_itl(port), (u32 *)(buf), (nr)) :  \
                   raw_outsw_swapw(isa_itw(port), (u16 *)(buf), (nr)<<1))
 
-#endif  /* CONFIG_ISA */
-
 
-#if defined(CONFIG_ISA) && !defined(CONFIG_PCI)
 #define inb     isa_inb
 #define inb_p   isa_inb_p
 #define outb    isa_outb
@@ -282,55 +276,9 @@ static inline void isa_delay(void)
 #define readw   isa_readw
 #define writeb  isa_writeb
 #define writew  isa_writew
-#endif /* CONFIG_ISA */
-
-#if defined(CONFIG_PCI)
-
-#define readl(addr)      in_le32(addr)
-#define writel(val,addr) out_le32((addr),(val))
-
-/* those can be defined for both ISA and PCI - it won't work though */
-#define readb(addr)       in_8(addr)
-#define readw(addr)       in_le16(addr)
-#define writeb(val,addr)  out_8((addr),(val))
-#define writew(val,addr)  out_le16((addr),(val))
 
-#define readb_relaxed(addr) readb(addr)
-#define readw_relaxed(addr) readw(addr)
-#define readl_relaxed(addr) readl(addr)
+#else  /* CONFIG_ISA */
 
-#ifndef CONFIG_ISA
-#define inb(port)      in_8(port)
-#define outb(val,port) out_8((port),(val))
-#define inw(port)      in_le16(port)
-#define outw(val,port) out_le16((port),(val))
-#define inl(port)      in_le32(port)
-#define outl(val,port) out_le32((port),(val))
-
-#else
-/*
- * kernel with both ISA and PCI compiled in, those have
- * conflicting defs for in/out. Simply consider port < 1024
- * ISA and everything else PCI. read,write not defined
- * in this case
- */
-#define inb(port) ((port)<1024 ? isa_inb(port) : in_8(port))
-#define inb_p(port) ((port)<1024 ? isa_inb_p(port) : in_8(port))
-#define inw(port) ((port)<1024 ? isa_inw(port) : in_le16(port))
-#define inw_p(port) ((port)<1024 ? isa_inw_p(port) : in_le16(port))
-#define inl(port) ((port)<1024 ? isa_inl(port) : in_le32(port))
-#define inl_p(port) ((port)<1024 ? isa_inl_p(port) : in_le32(port))
-
-#define outb(val,port) ((port)<1024 ? isa_outb((val),(port)) : out_8((port),(val)))
-#define outb_p(val,port) ((port)<1024 ? isa_outb_p((val),(port)) : out_8((port),(val)))
-#define outw(val,port) ((port)<1024 ? isa_outw((val),(port)) : out_le16((port),(val)))
-#define outw_p(val,port) ((port)<1024 ? isa_outw_p((val),(port)) : out_le16((port),(val)))
-#define outl(val,port) ((port)<1024 ? isa_outl((val),(port)) : out_le32((port),(val)))
-#define outl_p(val,port) ((port)<1024 ? isa_outl_p((val),(port)) : out_le32((port),(val)))
-#endif
-#endif /* CONFIG_PCI */
-
-#if !defined(CONFIG_ISA) && !defined(CONFIG_PCI)
 /*
  * We need to define dummy functions for GENERIC_IOMAP support.
  */
@@ -357,11 +305,11 @@ static inline void isa_delay(void)
 #define writeb(val,addr) out_8((addr),(val))
 #define readw(addr)      in_le16(addr)
 #define writew(val,addr) out_le16((addr),(val))
-#endif
-#if !defined(CONFIG_PCI)
+
+#endif /* CONFIG_ISA */
+
 #define readl(addr)      in_le32(addr)
 #define writel(val,addr) out_le32((addr),(val))
-#endif
 
 #define mmiowb()
 
index 678cb0b52314dac16a1cfb9fc9dbd44b5eeeed16..4ad0aea48ab4e9d2b5c96a278d09c2f3eadb3ce5 100644 (file)
@@ -1,52 +1,7 @@
 #ifndef _ASM_M68K_PCI_H
 #define _ASM_M68K_PCI_H
 
-/*
- * asm-m68k/pci_m68k.h - m68k specific PCI declarations.
- *
- * Written by Wout Klaren.
- */
-
-#include <asm/scatterlist.h>
-
-struct pci_ops;
-
-/*
- * Structure with hardware dependent information and functions of the
- * PCI bus.
- */
-
-struct pci_bus_info
-{
-       /*
-        * Resources of the PCI bus.
-        */
-
-       struct resource mem_space;
-       struct resource io_space;
-
-       /*
-        * System dependent functions.
-        */
-
-       struct pci_ops *m68k_pci_ops;
-
-       void (*fixup)(int pci_modify);
-       void (*conf_device)(struct pci_dev *dev);
-};
-
-#define pcibios_assign_all_busses()    0
-#define pcibios_scan_all_fns(a, b)     0
-
-static inline void pcibios_set_master(struct pci_dev *dev)
-{
-       /* No special bus mastering setup handling */
-}
-
-static inline void pcibios_penalize_isa_irq(int irq, int active)
-{
-       /* We don't do dynamic PCI IRQ allocation */
-}
+#include <asm-generic/pci-dma-compat.h>
 
 /* The PCI address space does equal the physical memory
  * address space.  The networking and block device layers use
index dea32fbc7e51c1c5bc995a1aa69ab57f6b1cf6f5..22ab05c9c52b707a961e456430bd13f6153224f7 100644 (file)
@@ -40,15 +40,9 @@ static inline void *phys_to_virt(unsigned long address)
 
 /*
  * IO bus memory addresses are 1:1 with the physical address,
- * except on the PCI bus of the Hades.
  */
-#ifdef CONFIG_HADES
-#define virt_to_bus(a) (virt_to_phys(a) + (MACH_IS_HADES ? 0x80000000 : 0))
-#define bus_to_virt(a) (phys_to_virt((a) - (MACH_IS_HADES ? 0x80000000 : 0)))
-#else
 #define virt_to_bus virt_to_phys
 #define bus_to_virt phys_to_virt
-#endif
 
 #endif
 #endif
index e8d80d1de237bd502ce1412862f71d17bb201d90..bdf5dba4cfb0a85d0d8aee9c4a557b6c1d4f4a15 100644 (file)
@@ -39,7 +39,6 @@ typedef u64   pmdval_t;
 typedef u64    pudval_t;
 typedef u64    pgdval_t;
 typedef u64    pgprotval_t;
-typedef u64    phys_addr_t;
 
 typedef union {
        struct {
@@ -60,7 +59,6 @@ typedef unsigned long pmdval_t;
 typedef unsigned long  pudval_t;
 typedef unsigned long  pgdval_t;
 typedef unsigned long  pgprotval_t;
-typedef unsigned long  phys_addr_t;
 
 typedef union {
        pteval_t pte;
index 5e64acfed0a4882b1bf392e0541b03df8d53734f..49380b8c7e25fbafa26243860abe03d105b7a97a 100644 (file)
@@ -79,7 +79,6 @@ typedef unsigned long pmdval_t;
 typedef unsigned long  pudval_t;
 typedef unsigned long  pgdval_t;
 typedef unsigned long  pgprotval_t;
-typedef unsigned long  phys_addr_t;
 
 typedef struct page *pgtable_t;
 
index c50185dccec192bdef7e0d936863d5503cc304d5..d5eada0a48d9bea73e8dcadeaf2379dc69ee3639 100644 (file)
@@ -76,13 +76,13 @@ static inline unsigned long mfn_to_pfn(unsigned long mfn)
 static inline xmaddr_t phys_to_machine(xpaddr_t phys)
 {
        unsigned offset = phys.paddr & ~PAGE_MASK;
-       return XMADDR(PFN_PHYS((u64)pfn_to_mfn(PFN_DOWN(phys.paddr))) | offset);
+       return XMADDR(PFN_PHYS(pfn_to_mfn(PFN_DOWN(phys.paddr))) | offset);
 }
 
 static inline xpaddr_t machine_to_phys(xmaddr_t machine)
 {
        unsigned offset = machine.maddr & ~PAGE_MASK;
-       return XPADDR(PFN_PHYS((u64)mfn_to_pfn(PFN_DOWN(machine.maddr))) | offset);
+       return XPADDR(PFN_PHYS(mfn_to_pfn(PFN_DOWN(machine.maddr))) | offset);
 }
 
 /*
index b03f80a078bee4f8d822dd90fc73545def4adcce..d71f7c0f931b34b63bfacde2b06d19ffc5001616 100644 (file)
@@ -53,7 +53,6 @@ struct vc_data {
        unsigned short  vc_hi_font_mask;        /* [#] Attribute set for upper 256 chars of font or 0 if not supported */
        struct console_font vc_font;            /* Current VC font set */
        unsigned short  vc_video_erase_char;    /* Background erase character */
-       unsigned short  vc_scrl_erase_char;     /* Erase character for scroll */
        /* VT terminal data */
        unsigned int    vc_state;               /* Escape sequence parser state */
        unsigned int    vc_npar,vc_par[NPAR];   /* Parameters of current escape sequence */
index 44e3cb2f1966d86b4a422fa616b97f485a150c95..a6a625be13fce0c66ca8d034ec69a2ca956be356 100644 (file)
@@ -947,6 +947,14 @@ struct lock_manager_operations {
        int (*fl_change)(struct file_lock **, int);
 };
 
+struct lock_manager {
+       struct list_head list;
+};
+
+void locks_start_grace(struct lock_manager *);
+void locks_end_grace(struct lock_manager *);
+int locks_in_grace(void);
+
 /* that will die - we need it for nfs_lock_info */
 #include <linux/nfs_fs_i.h>
 
@@ -988,6 +996,13 @@ struct file_lock {
 
 #include <linux/fcntl.h>
 
+extern void send_sigio(struct fown_struct *fown, int fd, int band);
+
+/* fs/sync.c */
+extern int do_sync_mapping_range(struct address_space *mapping, loff_t offset,
+                       loff_t endbyte, unsigned int flags);
+
+#ifdef CONFIG_FILE_LOCKING
 extern int fcntl_getlk(struct file *, struct flock __user *);
 extern int fcntl_setlk(unsigned int, struct file *, unsigned int,
                        struct flock __user *);
@@ -998,14 +1013,9 @@ extern int fcntl_setlk64(unsigned int, struct file *, unsigned int,
                        struct flock64 __user *);
 #endif
 
-extern void send_sigio(struct fown_struct *fown, int fd, int band);
 extern int fcntl_setlease(unsigned int fd, struct file *filp, long arg);
 extern int fcntl_getlease(struct file *filp);
 
-/* fs/sync.c */
-extern int do_sync_mapping_range(struct address_space *mapping, loff_t offset,
-                       loff_t endbyte, unsigned int flags);
-
 /* fs/locks.c */
 extern void locks_init_lock(struct file_lock *);
 extern void locks_copy_lock(struct file_lock *, struct file_lock *);
@@ -1028,6 +1038,37 @@ extern int lease_modify(struct file_lock **, int);
 extern int lock_may_read(struct inode *, loff_t start, unsigned long count);
 extern int lock_may_write(struct inode *, loff_t start, unsigned long count);
 extern struct seq_operations locks_seq_operations;
+#else /* !CONFIG_FILE_LOCKING */
+#define fcntl_getlk(a, b) ({ -EINVAL; })
+#define fcntl_setlk(a, b, c, d) ({ -EACCES; })
+#if BITS_PER_LONG == 32
+#define fcntl_getlk64(a, b) ({ -EINVAL; })
+#define fcntl_setlk64(a, b, c, d) ({ -EACCES; })
+#endif
+#define fcntl_setlease(a, b, c) ({ 0; })
+#define fcntl_getlease(a) ({ 0; })
+#define locks_init_lock(a) ({ })
+#define __locks_copy_lock(a, b) ({ })
+#define locks_copy_lock(a, b) ({ })
+#define locks_remove_posix(a, b) ({ })
+#define locks_remove_flock(a) ({ })
+#define posix_test_lock(a, b) ({ 0; })
+#define posix_lock_file(a, b, c) ({ -ENOLCK; })
+#define posix_lock_file_wait(a, b) ({ -ENOLCK; })
+#define posix_unblock_lock(a, b) (-ENOENT)
+#define vfs_test_lock(a, b) ({ 0; })
+#define vfs_lock_file(a, b, c, d) (-ENOLCK)
+#define vfs_cancel_lock(a, b) ({ 0; })
+#define flock_lock_file_wait(a, b) ({ -ENOLCK; })
+#define __break_lease(a, b) ({ 0; })
+#define lease_get_mtime(a, b) ({ })
+#define generic_setlease(a, b, c) ({ -EINVAL; })
+#define vfs_setlease(a, b, c) ({ -EINVAL; })
+#define lease_modify(a, b) ({ -EINVAL; })
+#define lock_may_read(a, b, c) ({ 1; })
+#define lock_may_write(a, b, c) ({ 1; })
+#endif /* !CONFIG_FILE_LOCKING */
+
 
 struct fasync_struct {
        int     magic;
@@ -1575,9 +1616,12 @@ extern int vfs_statfs(struct dentry *, struct kstatfs *);
 /* /sys/fs */
 extern struct kobject *fs_kobj;
 
+extern int rw_verify_area(int, struct file *, loff_t *, size_t);
+
 #define FLOCK_VERIFY_READ  1
 #define FLOCK_VERIFY_WRITE 2
 
+#ifdef CONFIG_FILE_LOCKING
 extern int locks_mandatory_locked(struct inode *);
 extern int locks_mandatory_area(int, struct inode *, struct file *, loff_t, size_t);
 
@@ -1608,8 +1652,6 @@ static inline int locks_verify_locked(struct inode *inode)
        return 0;
 }
 
-extern int rw_verify_area(int, struct file *, loff_t *, size_t);
-
 static inline int locks_verify_truncate(struct inode *inode,
                                    struct file *filp,
                                    loff_t size)
@@ -1630,6 +1672,15 @@ static inline int break_lease(struct inode *inode, unsigned int mode)
                return __break_lease(inode, mode);
        return 0;
 }
+#else /* !CONFIG_FILE_LOCKING */
+#define locks_mandatory_locked(a) ({ 0; })
+#define locks_mandatory_area(a, b, c, d, e) ({ 0; })
+#define __mandatory_lock(a) ({ 0; })
+#define mandatory_lock(a) ({ 0; })
+#define locks_verify_locked(a) ({ 0; })
+#define locks_verify_truncate(a, b, c) ({ 0; })
+#define break_lease(a, b) ({ 0; })
+#endif /* CONFIG_FILE_LOCKING */
 
 /* fs/open.c */
 
index ac4e678a04edb497f9194b7eb4939c88bf8bec10..f13bca2dd53b806303577cb298562a3a426b79b4 100644 (file)
@@ -67,6 +67,7 @@
 #include <linux/types.h>
 #include <linux/slab.h>
 #include <linux/list.h>
+#include <linux/mod_devicetable.h> /* hid_device_id */
 #include <linux/timer.h>
 #include <linux/workqueue.h>
 #include <linux/input.h>
@@ -245,6 +246,19 @@ struct hid_item {
 #define HID_OUTPUT_REPORT      1
 #define HID_FEATURE_REPORT     2
 
+/*
+ * HID connect requests
+ */
+
+#define HID_CONNECT_HIDINPUT           0x01
+#define HID_CONNECT_HIDINPUT_FORCE     0x02
+#define HID_CONNECT_HIDRAW             0x04
+#define HID_CONNECT_HIDDEV             0x08
+#define HID_CONNECT_HIDDEV_FORCE       0x10
+#define HID_CONNECT_FF                 0x20
+#define HID_CONNECT_DEFAULT    (HID_CONNECT_HIDINPUT|HID_CONNECT_HIDRAW| \
+               HID_CONNECT_HIDDEV|HID_CONNECT_FF)
+
 /*
  * HID device quirks.
  */
@@ -256,48 +270,11 @@ struct hid_item {
 
 #define HID_QUIRK_INVERT                       0x00000001
 #define HID_QUIRK_NOTOUCH                      0x00000002
-#define HID_QUIRK_IGNORE                       0x00000004
 #define HID_QUIRK_NOGET                                0x00000008
-#define HID_QUIRK_HIDDEV                       0x00000010
 #define HID_QUIRK_BADPAD                       0x00000020
 #define HID_QUIRK_MULTI_INPUT                  0x00000040
-#define HID_QUIRK_2WHEEL_MOUSE_HACK_7          0x00000080
-#define HID_QUIRK_2WHEEL_MOUSE_HACK_5          0x00000100
-#define HID_QUIRK_2WHEEL_MOUSE_HACK_ON         0x00000200
-#define HID_QUIRK_MIGHTYMOUSE                  0x00000400
-#define HID_QUIRK_APPLE_HAS_FN                 0x00000800
-#define HID_QUIRK_APPLE_FN_ON                  0x00001000
-#define HID_QUIRK_INVERT_HWHEEL                        0x00002000
-#define HID_QUIRK_APPLE_ISO_KEYBOARD           0x00004000
-#define HID_QUIRK_BAD_RELATIVE_KEYS            0x00008000
 #define HID_QUIRK_SKIP_OUTPUT_REPORTS          0x00010000
-#define HID_QUIRK_IGNORE_MOUSE                 0x00020000
-#define HID_QUIRK_SONY_PS3_CONTROLLER          0x00040000
-#define HID_QUIRK_DUPLICATE_USAGES             0x00080000
-#define HID_QUIRK_RESET_LEDS                   0x00100000
-#define HID_QUIRK_HIDINPUT                     0x00200000
-#define HID_QUIRK_LOGITECH_IGNORE_DOUBLED_WHEEL        0x00400000
-#define HID_QUIRK_LOGITECH_EXPANDED_KEYMAP     0x00800000
-#define HID_QUIRK_IGNORE_HIDINPUT              0x01000000
-#define HID_QUIRK_2WHEEL_MOUSE_HACK_B8         0x02000000
-#define HID_QUIRK_HWHEEL_WHEEL_INVERT          0x04000000
-#define HID_QUIRK_MICROSOFT_KEYS               0x08000000
 #define HID_QUIRK_FULLSPEED_INTERVAL           0x10000000
-#define HID_QUIRK_APPLE_NUMLOCK_EMULATION      0x20000000
-
-/*
- * Separate quirks for runtime report descriptor fixup
- */
-
-#define HID_QUIRK_RDESC_CYMOTION               0x00000001
-#define HID_QUIRK_RDESC_LOGITECH               0x00000002
-#define HID_QUIRK_RDESC_SWAPPED_MIN_MAX                0x00000004
-#define HID_QUIRK_RDESC_PETALYNX               0x00000008
-#define HID_QUIRK_RDESC_MACBOOK_JIS            0x00000010
-#define HID_QUIRK_RDESC_BUTTON_CONSUMER                0x00000020
-#define HID_QUIRK_RDESC_SAMSUNG_REMOTE         0x00000040
-#define HID_QUIRK_RDESC_MICROSOFT_RECV_1028    0x00000080
-#define HID_QUIRK_RDESC_SUNPLUS_WDESKTOP       0x00000100
 
 /*
  * This is the global environment of the parser. This information is
@@ -411,12 +388,21 @@ struct hid_report_enum {
 struct hid_control_fifo {
        unsigned char dir;
        struct hid_report *report;
+       char *raw_report;
+};
+
+struct hid_output_fifo {
+       struct hid_report *report;
+       char *raw_report;
 };
 
 #define HID_CLAIMED_INPUT      1
 #define HID_CLAIMED_HIDDEV     2
 #define HID_CLAIMED_HIDRAW     4
 
+#define HID_STAT_ADDED         1
+#define HID_STAT_PARSED                2
+
 #define HID_CTRL_RUNNING       1
 #define HID_OUT_RUNNING                2
 #define HID_IN_RUNNING         3
@@ -431,22 +417,28 @@ struct hid_input {
        struct input_dev *input;
 };
 
+struct hid_driver;
+struct hid_ll_driver;
+
 struct hid_device {                                                    /* device report descriptor */
-        __u8 *rdesc;
+       __u8 *rdesc;
        unsigned rsize;
        struct hid_collection *collection;                              /* List of HID collections */
        unsigned collection_size;                                       /* Number of allocated hid_collections */
        unsigned maxcollection;                                         /* Number of parsed collections */
        unsigned maxapplication;                                        /* Number of applications */
-       unsigned short bus;                                             /* BUS ID */
-       unsigned short vendor;                                          /* Vendor ID */
-       unsigned short product;                                         /* Product ID */
-       unsigned version;                                               /* HID version */
+       __u16 bus;                                                      /* BUS ID */
+       __u32 vendor;                                                   /* Vendor ID */
+       __u32 product;                                                  /* Product ID */
+       __u32 version;                                                  /* HID version */
        unsigned country;                                               /* HID country */
        struct hid_report_enum report_enum[HID_REPORT_TYPES];
 
-       struct device *dev;                                             /* device */
+       struct device dev;                                              /* device */
+       struct hid_driver *driver;
+       struct hid_ll_driver *ll_driver;
 
+       unsigned int status;                                            /* see STAT flags above */
        unsigned claimed;                                               /* Claimed by hidinput, hiddev? */
        unsigned quirks;                                                /* Various quirks the device can pull on us */
 
@@ -462,26 +454,29 @@ struct hid_device {                                                       /* device report descriptor */
 
        void *driver_data;
 
-       __s32 delayed_value;                                            /* For A4 Tech mice hwheel quirk */
-
-       /* device-specific function pointers */
-       int (*hidinput_input_event) (struct input_dev *, unsigned int, unsigned int, int);
-       int (*hid_open) (struct hid_device *);
-       void (*hid_close) (struct hid_device *);
+       /* temporary hid_ff handling (until moved to the drivers) */
+       int (*ff_init)(struct hid_device *);
 
        /* hiddev event handler */
+       int (*hiddev_connect)(struct hid_device *, unsigned int);
        void (*hiddev_hid_event) (struct hid_device *, struct hid_field *field,
                                  struct hid_usage *, __s32);
        void (*hiddev_report_event) (struct hid_device *, struct hid_report *);
 
        /* handler for raw output data, used by hidraw */
        int (*hid_output_raw_report) (struct hid_device *, __u8 *, size_t);
-#ifdef CONFIG_USB_HIDINPUT_POWERBOOK
-       unsigned long apple_pressed_fn[BITS_TO_LONGS(KEY_CNT)];
-       unsigned long pb_pressed_numlock[BITS_TO_LONGS(KEY_CNT)];
-#endif
 };
 
+static inline void *hid_get_drvdata(struct hid_device *hdev)
+{
+       return dev_get_drvdata(&hdev->dev);
+}
+
+static inline void hid_set_drvdata(struct hid_device *hdev, void *data)
+{
+       dev_set_drvdata(&hdev->dev, data);
+}
+
 #define HID_GLOBAL_STACK_SIZE 4
 #define HID_COLLECTION_STACK_SIZE 4
 
@@ -510,6 +505,107 @@ struct hid_descriptor {
        struct hid_class_descriptor desc[1];
 } __attribute__ ((packed));
 
+#define HID_DEVICE(b, ven, prod) \
+       .bus = (b), \
+       .vendor = (ven), .product = (prod)
+
+#define HID_USB_DEVICE(ven, prod)      HID_DEVICE(BUS_USB, ven, prod)
+#define HID_BLUETOOTH_DEVICE(ven, prod)        HID_DEVICE(BUS_BLUETOOTH, ven, prod)
+
+#define HID_REPORT_ID(rep) \
+       .report_type = (rep)
+#define HID_USAGE_ID(uhid, utype, ucode) \
+       .usage_hid = (uhid), .usage_type = (utype), .usage_code = (ucode)
+/* we don't want to catch types and codes equal to 0 */
+#define HID_TERMINATOR         (HID_ANY_ID - 1)
+
+struct hid_report_id {
+       __u32 report_type;
+};
+struct hid_usage_id {
+       __u32 usage_hid;
+       __u32 usage_type;
+       __u32 usage_code;
+};
+
+/**
+ * struct hid_driver
+ * @name: driver name (e.g. "Footech_bar-wheel")
+ * @id_table: which devices is this driver for (must be non-NULL for probe
+ *           to be called)
+ * @probe: new device inserted
+ * @remove: device removed (NULL if not a hot-plug capable driver)
+ * @report_table: on which reports to call raw_event (NULL means all)
+ * @raw_event: if report in report_table, this hook is called (NULL means nop)
+ * @usage_table: on which events to call event (NULL means all)
+ * @event: if usage in usage_table, this hook is called (NULL means nop)
+ * @report_fixup: called before report descriptor parsing (NULL means nop)
+ * @input_mapping: invoked on input registering before mapping an usage
+ * @input_mapped: invoked on input registering after mapping an usage
+ *
+ * raw_event and event should return 0 on no action performed, 1 when no
+ * further processing should be done and negative on error
+ *
+ * input_mapping shall return a negative value to completely ignore this usage
+ * (e.g. doubled or invalid usage), zero to continue with parsing of this
+ * usage by generic code (no special handling needed) or positive to skip
+ * generic parsing (needed special handling which was done in the hook already)
+ * input_mapped shall return negative to inform the layer that this usage
+ * should not be considered for further processing or zero to notify that
+ * no processing was performed and should be done in a generic manner
+ * Both these functions may be NULL which means the same behavior as returning
+ * zero from them.
+ */
+struct hid_driver {
+       char *name;
+       const struct hid_device_id *id_table;
+
+       int (*probe)(struct hid_device *dev, const struct hid_device_id *id);
+       void (*remove)(struct hid_device *dev);
+
+       const struct hid_report_id *report_table;
+       int (*raw_event)(struct hid_device *hdev, struct hid_report *report,
+                       u8 *data, int size);
+       const struct hid_usage_id *usage_table;
+       int (*event)(struct hid_device *hdev, struct hid_field *field,
+                       struct hid_usage *usage, __s32 value);
+
+       void (*report_fixup)(struct hid_device *hdev, __u8 *buf,
+                       unsigned int size);
+
+       int (*input_mapping)(struct hid_device *hdev,
+                       struct hid_input *hidinput, struct hid_field *field,
+                       struct hid_usage *usage, unsigned long **bit, int *max);
+       int (*input_mapped)(struct hid_device *hdev,
+                       struct hid_input *hidinput, struct hid_field *field,
+                       struct hid_usage *usage, unsigned long **bit, int *max);
+/* private: */
+       struct device_driver driver;
+};
+
+/**
+ * hid_ll_driver - low level driver callbacks
+ * @start: called on probe to start the device
+ * @stop: called on remove
+ * @open: called by input layer on open
+ * @close: called by input layer on close
+ * @hidinput_input_event: event input event (e.g. ff or leds)
+ * @parse: this method is called only once to parse the device data,
+ *        shouldn't allocate anything to not leak memory
+ */
+struct hid_ll_driver {
+       int (*start)(struct hid_device *hdev);
+       void (*stop)(struct hid_device *hdev);
+
+       int (*open)(struct hid_device *hdev);
+       void (*close)(struct hid_device *hdev);
+
+       int (*hidinput_input_event) (struct input_dev *idev, unsigned int type,
+                       unsigned int code, int value);
+
+       int (*parse)(struct hid_device *hdev);
+};
+
 /* Applications from HID Usage Tables 4/8/99 Version 1.1 */
 /* We ignore a few input applications that are not widely used */
 #define IS_INPUT_APPLICATION(a) (((a >= 0x00010000) && (a <= 0x00010008)) || (a == 0x00010080) || (a == 0x000c0001) || (a == 0x000d0002))
@@ -520,43 +616,157 @@ struct hid_descriptor {
 extern int hid_debug;
 #endif
 
+extern int hid_add_device(struct hid_device *);
+extern void hid_destroy_device(struct hid_device *);
+
+extern int __must_check __hid_register_driver(struct hid_driver *,
+               struct module *, const char *mod_name);
+static inline int __must_check hid_register_driver(struct hid_driver *driver)
+{
+       return __hid_register_driver(driver, THIS_MODULE, KBUILD_MODNAME);
+}
+extern void hid_unregister_driver(struct hid_driver *);
+
 extern void hidinput_hid_event(struct hid_device *, struct hid_field *, struct hid_usage *, __s32);
 extern void hidinput_report_event(struct hid_device *hid, struct hid_report *report);
-extern int hidinput_connect(struct hid_device *);
+extern int hidinput_connect(struct hid_device *hid, unsigned int force);
 extern void hidinput_disconnect(struct hid_device *);
 
 int hid_set_field(struct hid_field *, unsigned, __s32);
 int hid_input_report(struct hid_device *, int type, u8 *, int, int);
 int hidinput_find_field(struct hid_device *hid, unsigned int type, unsigned int code, struct hid_field **field);
-int hidinput_mapping_quirks(struct hid_usage *, struct input_dev *, unsigned long **, int *);
-int hidinput_event_quirks(struct hid_device *, struct hid_field *, struct hid_usage *, __s32);
-int hidinput_apple_event(struct hid_device *, struct input_dev *, struct hid_usage *, __s32);
 void hid_output_report(struct hid_report *report, __u8 *data);
-void hid_free_device(struct hid_device *device);
-struct hid_device *hid_parse_report(__u8 *start, unsigned size);
+struct hid_device *hid_allocate_device(void);
+int hid_parse_report(struct hid_device *hid, __u8 *start, unsigned size);
+int hid_connect(struct hid_device *hid, unsigned int connect_mask);
+
+/**
+ * hid_map_usage - map usage input bits
+ *
+ * @hidinput: hidinput which we are interested in
+ * @usage: usage to fill in
+ * @bit: pointer to input->{}bit (out parameter)
+ * @max: maximal valid usage->code to consider later (out parameter)
+ * @type: input event type (EV_KEY, EV_REL, ...)
+ * @c: code which corresponds to this usage and type
+ */
+static inline void hid_map_usage(struct hid_input *hidinput,
+               struct hid_usage *usage, unsigned long **bit, int *max,
+               __u8 type, __u16 c)
+{
+       struct input_dev *input = hidinput->input;
+
+       usage->type = type;
+       usage->code = c;
+
+       switch (type) {
+       case EV_ABS:
+               *bit = input->absbit;
+               *max = ABS_MAX;
+               break;
+       case EV_REL:
+               *bit = input->relbit;
+               *max = REL_MAX;
+               break;
+       case EV_KEY:
+               *bit = input->keybit;
+               *max = KEY_MAX;
+               break;
+       case EV_LED:
+               *bit = input->ledbit;
+               *max = LED_MAX;
+               break;
+       }
+}
+
+/**
+ * hid_map_usage_clear - map usage input bits and clear the input bit
+ *
+ * The same as hid_map_usage, except the @c bit is also cleared in supported
+ * bits (@bit).
+ */
+static inline void hid_map_usage_clear(struct hid_input *hidinput,
+               struct hid_usage *usage, unsigned long **bit, int *max,
+               __u8 type, __u16 c)
+{
+       hid_map_usage(hidinput, usage, bit, max, type, c);
+       clear_bit(c, *bit);
+}
+
+/**
+ * hid_parse - parse HW reports
+ *
+ * @hdev: hid device
+ *
+ * Call this from probe after you set up the device (if needed). Your
+ * report_fixup will be called (if non-NULL) after reading raw report from
+ * device before passing it to hid layer for real parsing.
+ */
+static inline int __must_check hid_parse(struct hid_device *hdev)
+{
+       int ret;
+
+       if (hdev->status & HID_STAT_PARSED)
+               return 0;
+
+       ret = hdev->ll_driver->parse(hdev);
+       if (!ret)
+               hdev->status |= HID_STAT_PARSED;
+
+       return ret;
+}
+
+/**
+ * hid_hw_start - start underlaying HW
+ *
+ * @hdev: hid device
+ * @connect_mask: which outputs to connect, see HID_CONNECT_*
+ *
+ * Call this in probe function *after* hid_parse. This will setup HW buffers
+ * and start the device (if not deffered to device open). hid_hw_stop must be
+ * called if this was successfull.
+ */
+static inline int __must_check hid_hw_start(struct hid_device *hdev,
+               unsigned int connect_mask)
+{
+       int ret = hdev->ll_driver->start(hdev);
+       if (ret || !connect_mask)
+               return ret;
+       ret = hid_connect(hdev, connect_mask);
+       if (ret)
+               hdev->ll_driver->stop(hdev);
+       return ret;
+}
+
+/**
+ * hid_hw_stop - stop underlaying HW
+ *
+ * @hdev: hid device
+ *
+ * This is usually called from remove function or from probe when something
+ * failed and hid_hw_start was called already.
+ */
+static inline void hid_hw_stop(struct hid_device *hdev)
+{
+       hdev->ll_driver->stop(hdev);
+}
+
+void hid_report_raw_event(struct hid_device *hid, int type, u8 *data, int size,
+               int interrupt);
+
+extern int hid_generic_init(void);
+extern void hid_generic_exit(void);
 
 /* HID quirks API */
 u32 usbhid_lookup_quirk(const u16 idVendor, const u16 idProduct);
 int usbhid_quirks_init(char **quirks_param);
 void usbhid_quirks_exit(void);
-void usbhid_fixup_report_descriptor(const u16, const u16, char *, unsigned, char **);
-
-#ifdef CONFIG_HID_FF
-int hid_ff_init(struct hid_device *hid);
+void usbhid_set_leds(struct hid_device *hid);
 
-int hid_lgff_init(struct hid_device *hid);
-int hid_lg2ff_init(struct hid_device *hid);
-int hid_plff_init(struct hid_device *hid);
-int hid_tmff_init(struct hid_device *hid);
-int hid_zpff_init(struct hid_device *hid);
 #ifdef CONFIG_HID_PID
 int hid_pidff_init(struct hid_device *hid);
 #else
-static inline int hid_pidff_init(struct hid_device *hid) { return -ENODEV; }
-#endif
-
-#else
-static inline int hid_ff_init(struct hid_device *hid) { return -1; }
+#define hid_pidff_init NULL
 #endif
 
 #ifdef CONFIG_HID_DEBUG
@@ -572,10 +782,23 @@ dbg_hid(const char *fmt, ...)
        return 0;
 }
 #define dbg_hid_line dbg_hid
-#endif
+#endif /* HID_DEBUG */
 
 #define err_hid(format, arg...) printk(KERN_ERR "%s: " format "\n" , \
                __FILE__ , ## arg)
-#endif
+#endif /* HID_FF */
+
+#ifdef CONFIG_HID_COMPAT
+#define HID_COMPAT_LOAD_DRIVER(name)   \
+void hid_compat_##name(void) { }       \
+EXPORT_SYMBOL(hid_compat_##name)
+#else
+#define HID_COMPAT_LOAD_DRIVER(name)
+#endif /* HID_COMPAT */
+#define HID_COMPAT_CALL_DRIVER(name)   do {    \
+       extern void hid_compat_##name(void);    \
+       hid_compat_##name();                    \
+} while (0)
+
 #endif
 
index a416b904ba900c2bae4453547bb8f01f818fe251..c760ae0eb6a12b8842297388d2d86ec14f20291c 100644 (file)
@@ -182,26 +182,28 @@ struct hiddev_usage_ref_multi {
 /* To traverse the input report descriptor info for a HID device, perform the 
  * following:
  *
- *  rinfo.report_type = HID_REPORT_TYPE_INPUT;
- *  rinfo.report_id = HID_REPORT_ID_FIRST;
- *  ret = ioctl(fd, HIDIOCGREPORTINFO, &rinfo);
+ * rinfo.report_type = HID_REPORT_TYPE_INPUT;
+ * rinfo.report_id = HID_REPORT_ID_FIRST;
+ * ret = ioctl(fd, HIDIOCGREPORTINFO, &rinfo);
  *
- *  while (ret >= 0) {
- *      for (i = 0; i < rinfo.num_fields; i++) { 
- *         finfo.report_type = rinfo.report_type;
- *          finfo.report_id = rinfo.report_id;
- *          finfo.field_index = i;
- *          ioctl(fd, HIDIOCGFIELDINFO, &finfo);
- *          for (j = 0; j < finfo.maxusage; j++) {
- *              uref.field_index = i;
- *             uref.usage_index = j;
- *             ioctl(fd, HIDIOCGUCODE, &uref);
- *             ioctl(fd, HIDIOCGUSAGE, &uref);
- *          }
- *     }
- *     rinfo.report_id |= HID_REPORT_ID_NEXT;
- *     ret = ioctl(fd, HIDIOCGREPORTINFO, &rinfo);
- *  }
+ * while (ret >= 0) {
+ *     for (i = 0; i < rinfo.num_fields; i++) {
+ *             finfo.report_type = rinfo.report_type;
+ *             finfo.report_id = rinfo.report_id;
+ *             finfo.field_index = i;
+ *             ioctl(fd, HIDIOCGFIELDINFO, &finfo);
+ *             for (j = 0; j < finfo.maxusage; j++) {
+ *                     uref.report_type = rinfo.report_type;
+ *                     uref.report_id = rinfo.report_id;
+ *                     uref.field_index = i;
+ *                     uref.usage_index = j;
+ *                     ioctl(fd, HIDIOCGUCODE, &uref);
+ *                     ioctl(fd, HIDIOCGUSAGE, &uref);
+ *             }
+ *     }
+ *     rinfo.report_id |= HID_REPORT_ID_NEXT;
+ *     ret = ioctl(fd, HIDIOCGREPORTINFO, &rinfo);
+ * }
  */
 
 
@@ -217,7 +219,7 @@ struct hid_field;
 struct hid_report;
 
 #ifdef CONFIG_USB_HIDDEV
-int hiddev_connect(struct hid_device *);
+int hiddev_connect(struct hid_device *hid, unsigned int force);
 void hiddev_disconnect(struct hid_device *);
 void hiddev_hid_event(struct hid_device *hid, struct hid_field *field,
                      struct hid_usage *usage, __s32 value);
@@ -225,7 +227,9 @@ void hiddev_report_event(struct hid_device *hid, struct hid_report *report);
 int __init hiddev_init(void);
 void hiddev_exit(void);
 #else
-static inline int hiddev_connect(struct hid_device *hid) { return -1; }
+static inline int hiddev_connect(struct hid_device *hid,
+               unsigned int force)
+{ return -1; }
 static inline void hiddev_disconnect(struct hid_device *hid) { }
 static inline void hiddev_hid_event(struct hid_device *hid, struct hid_field *field,
                      struct hid_usage *usage, __s32 value) { }
index bf1a53b2682ebf6e4231a679d3a857d4a21598a6..7f3c735f422bed8ff1cdeacc9450d61834f8dd19 100644 (file)
@@ -9,7 +9,7 @@
  *
  * Author:     Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
  *             Donald Becker, <becker@super.org>
- *             Alan Cox, <alan@redhat.com>
+ *             Alan Cox, <alan@lxorguk.ukuu.org.uk>
  *             Steve Whitehouse, <gw7rrm@eeshack3.swan.ac.uk>
  *
  *             This program is free software; you can redistribute it and/or
index ae77daed6c2feb8e1b2b41a0b1b66e03d6cd9327..45de1046dbbf76db53146961e736dde080c56946 100644 (file)
@@ -12,7 +12,7 @@
  *             if_fddi.h is based on previous if_ether.h and if_tr.h work by
  *                     Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
  *                     Donald Becker, <becker@super.org>
- *                     Alan Cox, <alan@redhat.com>
+ *                     Alan Cox, <alan@lxorguk.ukuu.org.uk>
  *                     Steve Whitehouse, <gw7rrm@eeshack3.swan.ac.uk>
  *                     Peter De Schrijver, <stud11@cc4.kuleuven.ac.be>
  *
index 94d31ca7d71a0936c19e066f3c899184b64eaa66..f0f23516bb5982222fcaf0b4677782137e90a5b1 100644 (file)
@@ -9,7 +9,7 @@
  *
  * Author:     Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
  *             Donald Becker, <becker@super.org>
- *             Alan Cox, <alan@redhat.com>
+ *             Alan Cox, <alan@lxorguk.ukuu.org.uk>
  *             Steve Whitehouse, <gw7rrm@eeshack3.swan.ac.uk>
  *             Jes Sorensen, <Jes.Sorensen@cern.ch>
  *
index 7bb3c095c15b4fb33d0ffd227ac9d39f86b9d297..f734a0ba0698b2ddf7ccf58190205f0b0ac60f7c 100644 (file)
@@ -2,7 +2,7 @@
  *     Linux NET3:     Internet Group Management Protocol  [IGMP]
  *
  *     Authors:
- *             Alan Cox <Alan.Cox@linux.org>
+ *             Alan Cox <alan@lxorguk.ukuu.org.uk>
  *
  *     Extended to talk the BSD extended IGMP protocol of mrouted 3.6
  *
index 58ff4e74b2f362bcc2078e76e9c5d10bdfba1f83..54b3623434eca45d1f3758a787d4763aed069b2d 100644 (file)
@@ -252,6 +252,8 @@ enum
        HRTIMER_SOFTIRQ,
 #endif
        RCU_SOFTIRQ,    /* Preferable RCU should always be the last softirq */
+
+       NR_SOFTIRQS
 };
 
 /* softirq mask and active fields moved to irq_cpustat_t in
index ee9bcc6f32b62efab5c3e93a1b4fe488e7be4448..e38b6aa2d40c22ec1b04d932b417b805f5fa93ef 100644 (file)
@@ -169,6 +169,7 @@ extern struct resource * __devm_request_region(struct device *dev,
 
 extern void __devm_release_region(struct device *dev, struct resource *parent,
                                  resource_size_t start, resource_size_t n);
+extern int iomem_map_sanity_check(resource_size_t addr, unsigned long size);
 
 #endif /* __ASSEMBLY__ */
 #endif /* _LINUX_IOPORT_H */
index 75d81f157d2edbd7a8aec39ce3cb7cc3bd69a758..9687491c483f08b32ae8f9f1f2dd803e9d806fc4 100644 (file)
@@ -213,6 +213,9 @@ static inline bool printk_timed_ratelimit(unsigned long *caller_jiffies, \
                { return false; }
 #endif
 
+extern int printk_needs_cpu(int cpu);
+extern void printk_tick(void);
+
 extern void asmlinkage __attribute__((format(printf, 1, 2)))
        early_printk(const char *fmt, ...);
 
index 3d25bcd139d166b1173e4691ffa5670c2950cdb1..e5872dc994c0a0761d601229ced0d77676756397 100644 (file)
@@ -27,7 +27,6 @@ struct nlmsvc_binding {
                                                struct nfs_fh *,
                                                struct file **);
        void                    (*fclose)(struct file *);
-       unsigned long           (*get_grace_period)(void);
 };
 
 extern struct nlmsvc_binding * nlmsvc_ops;
@@ -53,15 +52,7 @@ extern void  nlmclnt_done(struct nlm_host *host);
 
 extern int     nlmclnt_proc(struct nlm_host *host, int cmd,
                                        struct file_lock *fl);
-extern int     lockd_up(int proto);
+extern int     lockd_up(void);
 extern void    lockd_down(void);
 
-unsigned long get_nfs_grace_period(void);
-
-#ifdef CONFIG_NFSD_V4
-unsigned long get_nfs4_grace_period(void);
-#else
-static inline unsigned long get_nfs4_grace_period(void) {return 0;}
-#endif
-
 #endif /* LINUX_LOCKD_BIND_H */
index dbb87ab282e8fd55c718939cdcfa34c4adf2439b..b56d5aa9b194c0d87296b303874d331b84246413 100644 (file)
@@ -12,6 +12,8 @@
 #ifdef __KERNEL__
 
 #include <linux/in.h>
+#include <linux/in6.h>
+#include <net/ipv6.h>
 #include <linux/fs.h>
 #include <linux/kref.h>
 #include <linux/utsname.h>
@@ -38,8 +40,9 @@
  */
 struct nlm_host {
        struct hlist_node       h_hash;         /* doubly linked list */
-       struct sockaddr_in      h_addr;         /* peer address */
-       struct sockaddr_in      h_saddr;        /* our address (optional) */
+       struct sockaddr_storage h_addr;         /* peer address */
+       size_t                  h_addrlen;
+       struct sockaddr_storage h_srcaddr;      /* our address (optional) */
        struct rpc_clnt *       h_rpcclnt;      /* RPC client to talk to peer */
        char *                  h_name;         /* remote hostname */
        u32                     h_version;      /* interface version */
@@ -61,17 +64,55 @@ struct nlm_host {
        struct list_head        h_granted;      /* Locks in GRANTED state */
        struct list_head        h_reclaim;      /* Locks in RECLAIM state */
        struct nsm_handle *     h_nsmhandle;    /* NSM status handle */
+
+       char                    h_addrbuf[48],  /* address eyecatchers */
+                               h_srcaddrbuf[48];
 };
 
 struct nsm_handle {
        struct list_head        sm_link;
        atomic_t                sm_count;
        char *                  sm_name;
-       struct sockaddr_in      sm_addr;
+       struct sockaddr_storage sm_addr;
+       size_t                  sm_addrlen;
        unsigned int            sm_monitored : 1,
                                sm_sticky : 1;  /* don't unmonitor */
+       char                    sm_addrbuf[48]; /* address eyecatcher */
 };
 
+/*
+ * Rigorous type checking on sockaddr type conversions
+ */
+static inline struct sockaddr_in *nlm_addr_in(const struct nlm_host *host)
+{
+       return (struct sockaddr_in *)&host->h_addr;
+}
+
+static inline struct sockaddr *nlm_addr(const struct nlm_host *host)
+{
+       return (struct sockaddr *)&host->h_addr;
+}
+
+static inline struct sockaddr_in *nlm_srcaddr_in(const struct nlm_host *host)
+{
+       return (struct sockaddr_in *)&host->h_srcaddr;
+}
+
+static inline struct sockaddr *nlm_srcaddr(const struct nlm_host *host)
+{
+       return (struct sockaddr *)&host->h_srcaddr;
+}
+
+static inline struct sockaddr_in *nsm_addr_in(const struct nsm_handle *handle)
+{
+       return (struct sockaddr_in *)&handle->sm_addr;
+}
+
+static inline struct sockaddr *nsm_addr(const struct nsm_handle *handle)
+{
+       return (struct sockaddr *)&handle->sm_addr;
+}
+
 /*
  * Map an fl_owner_t into a unique 32-bit "pid"
  */
@@ -166,7 +207,8 @@ int           nlm_async_reply(struct nlm_rqst *, u32, const struct rpc_call_ops *);
 struct nlm_wait * nlmclnt_prepare_block(struct nlm_host *host, struct file_lock *fl);
 void             nlmclnt_finish_block(struct nlm_wait *block);
 int              nlmclnt_block(struct nlm_wait *block, struct nlm_rqst *req, long timeout);
-__be32           nlmclnt_grant(const struct sockaddr_in *addr, const struct nlm_lock *);
+__be32           nlmclnt_grant(const struct sockaddr *addr,
+                               const struct nlm_lock *lock);
 void             nlmclnt_recovery(struct nlm_host *);
 int              nlmclnt_reclaim(struct nlm_host *, struct file_lock *);
 void             nlmclnt_next_cookie(struct nlm_cookie *);
@@ -174,12 +216,14 @@ void                nlmclnt_next_cookie(struct nlm_cookie *);
 /*
  * Host cache
  */
-struct nlm_host  *nlmclnt_lookup_host(const struct sockaddr_in *sin,
-                                       int proto, u32 version,
+struct nlm_host  *nlmclnt_lookup_host(const struct sockaddr *sap,
+                                       const size_t salen,
+                                       const unsigned short protocol,
+                                       const u32 version,
+                                       const char *hostname);
+struct nlm_host  *nlmsvc_lookup_host(const struct svc_rqst *rqstp,
                                        const char *hostname,
-                                       unsigned int hostname_len);
-struct nlm_host  *nlmsvc_lookup_host(struct svc_rqst *, const char *,
-                                       unsigned int);
+                                       const size_t hostname_len);
 struct rpc_clnt * nlm_bind_host(struct nlm_host *);
 void             nlm_rebind_host(struct nlm_host *);
 struct nlm_host * nlm_get_host(struct nlm_host *);
@@ -201,7 +245,7 @@ typedef int   (*nlm_host_match_fn_t)(void *cur, struct nlm_host *ref);
  */
 __be32           nlmsvc_lock(struct svc_rqst *, struct nlm_file *,
                              struct nlm_host *, struct nlm_lock *, int,
-                             struct nlm_cookie *);
+                             struct nlm_cookie *, int);
 __be32           nlmsvc_unlock(struct nlm_file *, struct nlm_lock *);
 __be32           nlmsvc_testlock(struct svc_rqst *, struct nlm_file *,
                        struct nlm_host *, struct nlm_lock *,
@@ -233,15 +277,82 @@ static inline struct inode *nlmsvc_file_inode(struct nlm_file *file)
        return file->f_file->f_path.dentry->d_inode;
 }
 
+static inline int __nlm_privileged_request4(const struct sockaddr *sap)
+{
+       const struct sockaddr_in *sin = (struct sockaddr_in *)sap;
+       return (sin->sin_addr.s_addr == htonl(INADDR_LOOPBACK)) &&
+                       (ntohs(sin->sin_port) < 1024);
+}
+
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+static inline int __nlm_privileged_request6(const struct sockaddr *sap)
+{
+       const struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sap;
+       return (ipv6_addr_type(&sin6->sin6_addr) & IPV6_ADDR_LOOPBACK) &&
+                       (ntohs(sin6->sin6_port) < 1024);
+}
+#else  /* defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) */
+static inline int __nlm_privileged_request6(const struct sockaddr *sap)
+{
+       return 0;
+}
+#endif /* defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) */
+
 /*
- * Compare two host addresses (needs modifying for ipv6)
+ * Ensure incoming requests are from local privileged callers.
+ *
+ * Return TRUE if sender is local and is connecting via a privileged port;
+ * otherwise return FALSE.
  */
-static inline int nlm_cmp_addr(const struct sockaddr_in *sin1,
-                              const struct sockaddr_in *sin2)
+static inline int nlm_privileged_requester(const struct svc_rqst *rqstp)
 {
+       const struct sockaddr *sap = svc_addr(rqstp);
+
+       switch (sap->sa_family) {
+       case AF_INET:
+               return __nlm_privileged_request4(sap);
+       case AF_INET6:
+               return __nlm_privileged_request6(sap);
+       default:
+               return 0;
+       }
+}
+
+static inline int __nlm_cmp_addr4(const struct sockaddr *sap1,
+                                 const struct sockaddr *sap2)
+{
+       const struct sockaddr_in *sin1 = (const struct sockaddr_in *)sap1;
+       const struct sockaddr_in *sin2 = (const struct sockaddr_in *)sap2;
        return sin1->sin_addr.s_addr == sin2->sin_addr.s_addr;
 }
 
+static inline int __nlm_cmp_addr6(const struct sockaddr *sap1,
+                                 const struct sockaddr *sap2)
+{
+       const struct sockaddr_in6 *sin1 = (const struct sockaddr_in6 *)sap1;
+       const struct sockaddr_in6 *sin2 = (const struct sockaddr_in6 *)sap2;
+       return ipv6_addr_equal(&sin1->sin6_addr, &sin2->sin6_addr);
+}
+
+/*
+ * Compare two host addresses
+ *
+ * Return TRUE if the addresses are the same; otherwise FALSE.
+ */
+static inline int nlm_cmp_addr(const struct sockaddr *sap1,
+                              const struct sockaddr *sap2)
+{
+       if (sap1->sa_family == sap2->sa_family) {
+               switch (sap1->sa_family) {
+               case AF_INET:
+                       return __nlm_cmp_addr4(sap1, sap2);
+               case AF_INET6:
+                       return __nlm_cmp_addr6(sap1, sap2);
+               }
+       }
+       return 0;
+}
+
 /*
  * Compare two NLM locks.
  * When the second lock is of type F_UNLCK, this acts like a wildcard.
index df18fa053bcd7d0a1e1f9665e57efa8929dafb87..d6b3a802c0469a8e65311058167b6e2d5bacd76c 100644 (file)
@@ -81,8 +81,6 @@ struct nlm_reboot {
        unsigned int    len;
        u32             state;
        __be32          addr;
-       __be32          vers;
-       __be32          proto;
 };
 
 /*
diff --git a/include/linux/mfd/wm8350/audio.h b/include/linux/mfd/wm8350/audio.h
new file mode 100644 (file)
index 0000000..217bb22
--- /dev/null
@@ -0,0 +1,598 @@
+/*
+ * audio.h  --  Audio Driver for Wolfson WM8350 PMIC
+ *
+ * Copyright 2007 Wolfson Microelectronics PLC
+ *
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ *
+ */
+
+#ifndef __LINUX_MFD_WM8350_AUDIO_H_
+#define __LINUX_MFD_WM8350_AUDIO_H_
+
+#include <linux/platform_device.h>
+
+#define WM8350_CLOCK_CONTROL_1                  0x28
+#define WM8350_CLOCK_CONTROL_2                  0x29
+#define WM8350_FLL_CONTROL_1                    0x2A
+#define WM8350_FLL_CONTROL_2                    0x2B
+#define WM8350_FLL_CONTROL_3                    0x2C
+#define WM8350_FLL_CONTROL_4                    0x2D
+#define WM8350_DAC_CONTROL                      0x30
+#define WM8350_DAC_DIGITAL_VOLUME_L             0x32
+#define WM8350_DAC_DIGITAL_VOLUME_R             0x33
+#define WM8350_DAC_LR_RATE                      0x35
+#define WM8350_DAC_CLOCK_CONTROL                0x36
+#define WM8350_DAC_MUTE                         0x3A
+#define WM8350_DAC_MUTE_VOLUME                  0x3B
+#define WM8350_DAC_SIDE                         0x3C
+#define WM8350_ADC_CONTROL                      0x40
+#define WM8350_ADC_DIGITAL_VOLUME_L             0x42
+#define WM8350_ADC_DIGITAL_VOLUME_R             0x43
+#define WM8350_ADC_DIVIDER                      0x44
+#define WM8350_ADC_LR_RATE                      0x46
+#define WM8350_INPUT_CONTROL                    0x48
+#define WM8350_IN3_INPUT_CONTROL                0x49
+#define WM8350_MIC_BIAS_CONTROL                 0x4A
+#define WM8350_OUTPUT_CONTROL                   0x4C
+#define WM8350_JACK_DETECT                      0x4D
+#define WM8350_ANTI_POP_CONTROL                 0x4E
+#define WM8350_LEFT_INPUT_VOLUME                0x50
+#define WM8350_RIGHT_INPUT_VOLUME               0x51
+#define WM8350_LEFT_MIXER_CONTROL               0x58
+#define WM8350_RIGHT_MIXER_CONTROL              0x59
+#define WM8350_OUT3_MIXER_CONTROL               0x5C
+#define WM8350_OUT4_MIXER_CONTROL               0x5D
+#define WM8350_OUTPUT_LEFT_MIXER_VOLUME         0x60
+#define WM8350_OUTPUT_RIGHT_MIXER_VOLUME        0x61
+#define WM8350_INPUT_MIXER_VOLUME_L             0x62
+#define WM8350_INPUT_MIXER_VOLUME_R             0x63
+#define WM8350_INPUT_MIXER_VOLUME               0x64
+#define WM8350_LOUT1_VOLUME                     0x68
+#define WM8350_ROUT1_VOLUME                     0x69
+#define WM8350_LOUT2_VOLUME                     0x6A
+#define WM8350_ROUT2_VOLUME                     0x6B
+#define WM8350_BEEP_VOLUME                      0x6F
+#define WM8350_AI_FORMATING                     0x70
+#define WM8350_ADC_DAC_COMP                     0x71
+#define WM8350_AI_ADC_CONTROL                   0x72
+#define WM8350_AI_DAC_CONTROL                   0x73
+#define WM8350_AIF_TEST                         0x74
+#define WM8350_JACK_PIN_STATUS                  0xE7
+
+/* Bit values for R08 (0x08) */
+#define WM8350_CODEC_ISEL_1_5                   0      /* x1.5 */
+#define WM8350_CODEC_ISEL_1_0                   1      /* x1.0 */
+#define WM8350_CODEC_ISEL_0_75                  2      /* x0.75 */
+#define WM8350_CODEC_ISEL_0_5                   3      /* x0.5 */
+
+#define WM8350_VMID_OFF                         0
+#define WM8350_VMID_500K                        1
+#define WM8350_VMID_100K                        2
+#define WM8350_VMID_10K                         3
+
+/*
+ * R40 (0x28) - Clock Control 1
+ */
+#define WM8350_TOCLK_RATE                       0x4000
+#define WM8350_MCLK_SEL                         0x0800
+#define WM8350_MCLK_DIV_MASK                    0x0100
+#define WM8350_BCLK_DIV_MASK                    0x00F0
+#define WM8350_OPCLK_DIV_MASK                   0x0007
+
+/*
+ * R41 (0x29) - Clock Control 2
+ */
+#define WM8350_LRC_ADC_SEL                      0x8000
+#define WM8350_MCLK_DIR                         0x0001
+
+/*
+ * R42 (0x2A) - FLL Control 1
+ */
+#define WM8350_FLL_DITHER_WIDTH_MASK            0x3000
+#define WM8350_FLL_DITHER_HP                    0x0800
+#define WM8350_FLL_OUTDIV_MASK                  0x0700
+#define WM8350_FLL_RSP_RATE_MASK                0x00F0
+#define WM8350_FLL_RATE_MASK                    0x0007
+
+/*
+ * R43 (0x2B) - FLL Control 2
+ */
+#define WM8350_FLL_RATIO_MASK                   0xF800
+#define WM8350_FLL_N_MASK                       0x03FF
+
+/*
+ * R44 (0x2C) - FLL Control 3
+ */
+#define WM8350_FLL_K_MASK                       0xFFFF
+
+/*
+ * R45 (0x2D) - FLL Control 4
+ */
+#define WM8350_FLL_FRAC                         0x0020
+#define WM8350_FLL_SLOW_LOCK_REF                0x0010
+#define WM8350_FLL_CLK_SRC_MASK                 0x0003
+
+/*
+ * R48 (0x30) - DAC Control
+ */
+#define WM8350_DAC_MONO                         0x2000
+#define WM8350_AIF_LRCLKRATE                    0x1000
+#define WM8350_DEEMP_MASK                       0x0030
+#define WM8350_DACL_DATINV                      0x0002
+#define WM8350_DACR_DATINV                      0x0001
+
+/*
+ * R50 (0x32) - DAC Digital Volume L
+ */
+#define WM8350_DAC_VU                           0x0100
+#define WM8350_DACL_VOL_MASK                    0x00FF
+
+/*
+ * R51 (0x33) - DAC Digital Volume R
+ */
+#define WM8350_DAC_VU                           0x0100
+#define WM8350_DACR_VOL_MASK                    0x00FF
+
+/*
+ * R53 (0x35) - DAC LR Rate
+ */
+#define WM8350_DACLRC_ENA                       0x0800
+#define WM8350_DACLRC_RATE_MASK                 0x07FF
+
+/*
+ * R54 (0x36) - DAC Clock Control
+ */
+#define WM8350_DACCLK_POL                       0x0010
+#define WM8350_DAC_CLKDIV_MASK                  0x0007
+
+/*
+ * R58 (0x3A) - DAC Mute
+ */
+#define WM8350_DAC_MUTE_ENA                     0x4000
+
+/*
+ * R59 (0x3B) - DAC Mute Volume
+ */
+#define WM8350_DAC_MUTEMODE                     0x4000
+#define WM8350_DAC_MUTERATE                     0x2000
+#define WM8350_DAC_SB_FILT                      0x1000
+
+/*
+ * R60 (0x3C) - DAC Side
+ */
+#define WM8350_ADC_TO_DACL_MASK                 0x3000
+#define WM8350_ADC_TO_DACR_MASK                 0x0C00
+
+/*
+ * R64 (0x40) - ADC Control
+ */
+#define WM8350_ADC_HPF_CUT_MASK                 0x0300
+#define WM8350_ADCL_DATINV                      0x0002
+#define WM8350_ADCR_DATINV                      0x0001
+
+/*
+ * R66 (0x42) - ADC Digital Volume L
+ */
+#define WM8350_ADC_VU                           0x0100
+#define WM8350_ADCL_VOL_MASK                    0x00FF
+
+/*
+ * R67 (0x43) - ADC Digital Volume R
+ */
+#define WM8350_ADC_VU                           0x0100
+#define WM8350_ADCR_VOL_MASK                    0x00FF
+
+/*
+ * R68 (0x44) - ADC Divider
+ */
+#define WM8350_ADCL_DAC_SVOL_MASK               0x0F00
+#define WM8350_ADCR_DAC_SVOL_MASK               0x00F0
+#define WM8350_ADCCLK_POL                       0x0008
+#define WM8350_ADC_CLKDIV_MASK                  0x0007
+
+/*
+ * R70 (0x46) - ADC LR Rate
+ */
+#define WM8350_ADCLRC_ENA                       0x0800
+#define WM8350_ADCLRC_RATE_MASK                 0x07FF
+
+/*
+ * R72 (0x48) - Input Control
+ */
+#define WM8350_IN2R_ENA                         0x0400
+#define WM8350_IN1RN_ENA                        0x0200
+#define WM8350_IN1RP_ENA                        0x0100
+#define WM8350_IN2L_ENA                         0x0004
+#define WM8350_IN1LN_ENA                        0x0002
+#define WM8350_IN1LP_ENA                        0x0001
+
+/*
+ * R73 (0x49) - IN3 Input Control
+ */
+#define WM8350_IN3R_SHORT                       0x4000
+#define WM8350_IN3L_SHORT                       0x0040
+
+/*
+ * R74 (0x4A) - Mic Bias Control
+ */
+#define WM8350_MICBSEL                          0x4000
+#define WM8350_MCDTHR_MASK                      0x001C
+#define WM8350_MCDSCTHR_MASK                    0x0003
+
+/*
+ * R76 (0x4C) - Output Control
+ */
+#define WM8350_OUT4_VROI                        0x0800
+#define WM8350_OUT3_VROI                        0x0400
+#define WM8350_OUT2_VROI                        0x0200
+#define WM8350_OUT1_VROI                        0x0100
+#define WM8350_OUT2_FB                          0x0004
+#define WM8350_OUT1_FB                          0x0001
+
+/*
+ * R77 (0x4D) - Jack Detect
+ */
+#define WM8350_JDL_ENA                          0x8000
+#define WM8350_JDR_ENA                          0x4000
+
+/*
+ * R78 (0x4E) - Anti Pop Control
+ */
+#define WM8350_ANTI_POP_MASK                    0x0300
+#define WM8350_DIS_OP_LN4_MASK                  0x00C0
+#define WM8350_DIS_OP_LN3_MASK                  0x0030
+#define WM8350_DIS_OP_OUT2_MASK                 0x000C
+#define WM8350_DIS_OP_OUT1_MASK                 0x0003
+
+/*
+ * R80 (0x50) - Left Input Volume
+ */
+#define WM8350_INL_MUTE                         0x4000
+#define WM8350_INL_ZC                           0x2000
+#define WM8350_IN_VU                            0x0100
+#define WM8350_INL_VOL_MASK                     0x00FC
+
+/*
+ * R81 (0x51) - Right Input Volume
+ */
+#define WM8350_INR_MUTE                         0x4000
+#define WM8350_INR_ZC                           0x2000
+#define WM8350_IN_VU                            0x0100
+#define WM8350_INR_VOL_MASK                     0x00FC
+
+/*
+ * R88 (0x58) - Left Mixer Control
+ */
+#define WM8350_DACR_TO_MIXOUTL                  0x1000
+#define WM8350_DACL_TO_MIXOUTL                  0x0800
+#define WM8350_IN3L_TO_MIXOUTL                  0x0004
+#define WM8350_INR_TO_MIXOUTL                   0x0002
+#define WM8350_INL_TO_MIXOUTL                   0x0001
+
+/*
+ * R89 (0x59) - Right Mixer Control
+ */
+#define WM8350_DACR_TO_MIXOUTR                  0x1000
+#define WM8350_DACL_TO_MIXOUTR                  0x0800
+#define WM8350_IN3R_TO_MIXOUTR                  0x0008
+#define WM8350_INR_TO_MIXOUTR                   0x0002
+#define WM8350_INL_TO_MIXOUTR                   0x0001
+
+/*
+ * R92 (0x5C) - OUT3 Mixer Control
+ */
+#define WM8350_DACL_TO_OUT3                     0x0800
+#define WM8350_MIXINL_TO_OUT3                   0x0100
+#define WM8350_OUT4_TO_OUT3                     0x0008
+#define WM8350_MIXOUTL_TO_OUT3                  0x0001
+
+/*
+ * R93 (0x5D) - OUT4 Mixer Control
+ */
+#define WM8350_DACR_TO_OUT4                     0x1000
+#define WM8350_DACL_TO_OUT4                     0x0800
+#define WM8350_OUT4_ATTN                        0x0400
+#define WM8350_MIXINR_TO_OUT4                   0x0200
+#define WM8350_OUT3_TO_OUT4                     0x0004
+#define WM8350_MIXOUTR_TO_OUT4                  0x0002
+#define WM8350_MIXOUTL_TO_OUT4                  0x0001
+
+/*
+ * R96 (0x60) - Output Left Mixer Volume
+ */
+#define WM8350_IN3L_MIXOUTL_VOL_MASK            0x0E00
+#define WM8350_IN3L_MIXOUTL_VOL_SHIFT                9
+#define WM8350_INR_MIXOUTL_VOL_MASK             0x00E0
+#define WM8350_INR_MIXOUTL_VOL_SHIFT                 5
+#define WM8350_INL_MIXOUTL_VOL_MASK             0x000E
+#define WM8350_INL_MIXOUTL_VOL_SHIFT                 1
+
+/* Bit values for R96 (0x60) */
+#define WM8350_IN3L_MIXOUTL_VOL_OFF                  0
+#define WM8350_IN3L_MIXOUTL_VOL_M12DB                1
+#define WM8350_IN3L_MIXOUTL_VOL_M9DB                 2
+#define WM8350_IN3L_MIXOUTL_VOL_M6DB                 3
+#define WM8350_IN3L_MIXOUTL_VOL_M3DB                 4
+#define WM8350_IN3L_MIXOUTL_VOL_0DB                  5
+#define WM8350_IN3L_MIXOUTL_VOL_3DB                  6
+#define WM8350_IN3L_MIXOUTL_VOL_6DB                  7
+
+#define WM8350_INR_MIXOUTL_VOL_OFF                   0
+#define WM8350_INR_MIXOUTL_VOL_M12DB                 1
+#define WM8350_INR_MIXOUTL_VOL_M9DB                  2
+#define WM8350_INR_MIXOUTL_VOL_M6DB                  3
+#define WM8350_INR_MIXOUTL_VOL_M3DB                  4
+#define WM8350_INR_MIXOUTL_VOL_0DB                   5
+#define WM8350_INR_MIXOUTL_VOL_3DB                   6
+#define WM8350_INR_MIXOUTL_VOL_6DB                   7
+
+#define WM8350_INL_MIXOUTL_VOL_OFF                   0
+#define WM8350_INL_MIXOUTL_VOL_M12DB                 1
+#define WM8350_INL_MIXOUTL_VOL_M9DB                  2
+#define WM8350_INL_MIXOUTL_VOL_M6DB                  3
+#define WM8350_INL_MIXOUTL_VOL_M3DB                  4
+#define WM8350_INL_MIXOUTL_VOL_0DB                   5
+#define WM8350_INL_MIXOUTL_VOL_3DB                   6
+#define WM8350_INL_MIXOUTL_VOL_6DB                   7
+
+/*
+ * R97 (0x61) - Output Right Mixer Volume
+ */
+#define WM8350_IN3R_MIXOUTR_VOL_MASK            0xE000
+#define WM8350_IN3R_MIXOUTR_VOL_SHIFT               13
+#define WM8350_INR_MIXOUTR_VOL_MASK             0x00E0
+#define WM8350_INR_MIXOUTR_VOL_SHIFT                 5
+#define WM8350_INL_MIXOUTR_VOL_MASK             0x000E
+#define WM8350_INL_MIXOUTR_VOL_SHIFT                 1
+
+/* Bit values for R96 (0x60) */
+#define WM8350_IN3R_MIXOUTR_VOL_OFF                  0
+#define WM8350_IN3R_MIXOUTR_VOL_M12DB                1
+#define WM8350_IN3R_MIXOUTR_VOL_M9DB                 2
+#define WM8350_IN3R_MIXOUTR_VOL_M6DB                 3
+#define WM8350_IN3R_MIXOUTR_VOL_M3DB                 4
+#define WM8350_IN3R_MIXOUTR_VOL_0DB                  5
+#define WM8350_IN3R_MIXOUTR_VOL_3DB                  6
+#define WM8350_IN3R_MIXOUTR_VOL_6DB                  7
+
+#define WM8350_INR_MIXOUTR_VOL_OFF                   0
+#define WM8350_INR_MIXOUTR_VOL_M12DB                 1
+#define WM8350_INR_MIXOUTR_VOL_M9DB                  2
+#define WM8350_INR_MIXOUTR_VOL_M6DB                  3
+#define WM8350_INR_MIXOUTR_VOL_M3DB                  4
+#define WM8350_INR_MIXOUTR_VOL_0DB                   5
+#define WM8350_INR_MIXOUTR_VOL_3DB                   6
+#define WM8350_INR_MIXOUTR_VOL_6DB                   7
+
+#define WM8350_INL_MIXOUTR_VOL_OFF                   0
+#define WM8350_INL_MIXOUTR_VOL_M12DB                 1
+#define WM8350_INL_MIXOUTR_VOL_M9DB                  2
+#define WM8350_INL_MIXOUTR_VOL_M6DB                  3
+#define WM8350_INL_MIXOUTR_VOL_M3DB                  4
+#define WM8350_INL_MIXOUTR_VOL_0DB                   5
+#define WM8350_INL_MIXOUTR_VOL_3DB                   6
+#define WM8350_INL_MIXOUTR_VOL_6DB                   7
+
+/*
+ * R98 (0x62) - Input Mixer Volume L
+ */
+#define WM8350_IN3L_MIXINL_VOL_MASK             0x0E00
+#define WM8350_IN2L_MIXINL_VOL_MASK             0x000E
+#define WM8350_INL_MIXINL_VOL                   0x0001
+
+/*
+ * R99 (0x63) - Input Mixer Volume R
+ */
+#define WM8350_IN3R_MIXINR_VOL_MASK             0xE000
+#define WM8350_IN2R_MIXINR_VOL_MASK             0x00E0
+#define WM8350_INR_MIXINR_VOL                   0x0001
+
+/*
+ * R100 (0x64) - Input Mixer Volume
+ */
+#define WM8350_OUT4_MIXIN_DST                   0x8000
+#define WM8350_OUT4_MIXIN_VOL_MASK              0x000E
+
+/*
+ * R104 (0x68) - LOUT1 Volume
+ */
+#define WM8350_OUT1L_MUTE                       0x4000
+#define WM8350_OUT1L_ZC                         0x2000
+#define WM8350_OUT1_VU                          0x0100
+#define WM8350_OUT1L_VOL_MASK                   0x00FC
+#define WM8350_OUT1L_VOL_SHIFT                       2
+
+/*
+ * R105 (0x69) - ROUT1 Volume
+ */
+#define WM8350_OUT1R_MUTE                       0x4000
+#define WM8350_OUT1R_ZC                         0x2000
+#define WM8350_OUT1_VU                          0x0100
+#define WM8350_OUT1R_VOL_MASK                   0x00FC
+#define WM8350_OUT1R_VOL_SHIFT                       2
+
+/*
+ * R106 (0x6A) - LOUT2 Volume
+ */
+#define WM8350_OUT2L_MUTE                       0x4000
+#define WM8350_OUT2L_ZC                         0x2000
+#define WM8350_OUT2_VU                          0x0100
+#define WM8350_OUT2L_VOL_MASK                   0x00FC
+
+/*
+ * R107 (0x6B) - ROUT2 Volume
+ */
+#define WM8350_OUT2R_MUTE                       0x4000
+#define WM8350_OUT2R_ZC                         0x2000
+#define WM8350_OUT2R_INV                        0x0400
+#define WM8350_OUT2R_INV_MUTE                   0x0200
+#define WM8350_OUT2_VU                          0x0100
+#define WM8350_OUT2R_VOL_MASK                   0x00FC
+
+/*
+ * R111 (0x6F) - BEEP Volume
+ */
+#define WM8350_IN3R_OUT2R_VOL_MASK              0x00E0
+
+/*
+ * R112 (0x70) - AI Formating
+ */
+#define WM8350_AIF_BCLK_INV                     0x8000
+#define WM8350_AIF_TRI                          0x2000
+#define WM8350_AIF_LRCLK_INV                    0x1000
+#define WM8350_AIF_WL_MASK                      0x0C00
+#define WM8350_AIF_FMT_MASK                     0x0300
+
+/*
+ * R113 (0x71) - ADC DAC COMP
+ */
+#define WM8350_DAC_COMP                         0x0080
+#define WM8350_DAC_COMPMODE                     0x0040
+#define WM8350_ADC_COMP                         0x0020
+#define WM8350_ADC_COMPMODE                     0x0010
+#define WM8350_LOOPBACK                         0x0001
+
+/*
+ * R114 (0x72) - AI ADC Control
+ */
+#define WM8350_AIFADC_PD                        0x0080
+#define WM8350_AIFADCL_SRC                      0x0040
+#define WM8350_AIFADCR_SRC                      0x0020
+#define WM8350_AIFADC_TDM_CHAN                  0x0010
+#define WM8350_AIFADC_TDM                       0x0008
+
+/*
+ * R115 (0x73) - AI DAC Control
+ */
+#define WM8350_BCLK_MSTR                        0x4000
+#define WM8350_AIFDAC_PD                        0x0080
+#define WM8350_DACL_SRC                         0x0040
+#define WM8350_DACR_SRC                         0x0020
+#define WM8350_AIFDAC_TDM_CHAN                  0x0010
+#define WM8350_AIFDAC_TDM                       0x0008
+#define WM8350_DAC_BOOST_MASK                   0x0003
+
+/*
+ * R116 (0x74) - AIF Test
+ */
+#define WM8350_CODEC_BYP                        0x4000
+#define WM8350_AIFADC_WR_TST                    0x2000
+#define WM8350_AIFADC_RD_TST                    0x1000
+#define WM8350_AIFDAC_WR_TST                    0x0800
+#define WM8350_AIFDAC_RD_TST                    0x0400
+#define WM8350_AIFADC_ASYN                      0x0020
+#define WM8350_AIFDAC_ASYN                      0x0010
+
+/*
+ * R231 (0xE7) - Jack Status
+ */
+#define WM8350_JACK_R_LVL                       0x0400
+
+/*
+ * WM8350 Platform setup
+ */
+#define WM8350_S_CURVE_NONE                    0x0
+#define WM8350_S_CURVE_FAST                    0x1
+#define WM8350_S_CURVE_MEDIUM                  0x2
+#define WM8350_S_CURVE_SLOW                    0x3
+
+#define WM8350_DISCHARGE_OFF                   0x0
+#define WM8350_DISCHARGE_FAST                  0x1
+#define WM8350_DISCHARGE_MEDIUM                        0x2
+#define WM8350_DISCHARGE_SLOW                  0x3
+
+#define WM8350_TIE_OFF_500R                    0x0
+#define WM8350_TIE_OFF_30K                     0x1
+
+/*
+ * Clock sources & directions
+ */
+#define WM8350_SYSCLK                          0
+
+#define WM8350_MCLK_SEL_PLL_MCLK               0
+#define WM8350_MCLK_SEL_PLL_DAC                        1
+#define WM8350_MCLK_SEL_PLL_ADC                        2
+#define WM8350_MCLK_SEL_PLL_32K                        3
+#define WM8350_MCLK_SEL_MCLK                   5
+
+#define WM8350_MCLK_DIR_OUT                    0
+#define WM8350_MCLK_DIR_IN                     1
+
+/* clock divider id's */
+#define WM8350_ADC_CLKDIV                      0
+#define WM8350_DAC_CLKDIV                      1
+#define WM8350_BCLK_CLKDIV                     2
+#define WM8350_OPCLK_CLKDIV                    3
+#define WM8350_TO_CLKDIV                       4
+#define WM8350_SYS_CLKDIV                      5
+#define WM8350_DACLR_CLKDIV                    6
+#define WM8350_ADCLR_CLKDIV                    7
+
+/* ADC clock dividers */
+#define WM8350_ADCDIV_1                                0x0
+#define WM8350_ADCDIV_1_5                      0x1
+#define WM8350_ADCDIV_2                                0x2
+#define WM8350_ADCDIV_3                                0x3
+#define WM8350_ADCDIV_4                                0x4
+#define WM8350_ADCDIV_5_5                      0x5
+#define WM8350_ADCDIV_6                                0x6
+
+/* ADC clock dividers */
+#define WM8350_DACDIV_1                                0x0
+#define WM8350_DACDIV_1_5                      0x1
+#define WM8350_DACDIV_2                                0x2
+#define WM8350_DACDIV_3                                0x3
+#define WM8350_DACDIV_4                                0x4
+#define WM8350_DACDIV_5_5                      0x5
+#define WM8350_DACDIV_6                                0x6
+
+/* BCLK clock dividers */
+#define WM8350_BCLK_DIV_1                      (0x0 << 4)
+#define WM8350_BCLK_DIV_1_5                    (0x1 << 4)
+#define WM8350_BCLK_DIV_2                      (0x2 << 4)
+#define WM8350_BCLK_DIV_3                      (0x3 << 4)
+#define WM8350_BCLK_DIV_4                      (0x4 << 4)
+#define WM8350_BCLK_DIV_5_5                    (0x5 << 4)
+#define WM8350_BCLK_DIV_6                      (0x6 << 4)
+#define WM8350_BCLK_DIV_8                      (0x7 << 4)
+#define WM8350_BCLK_DIV_11                     (0x8 << 4)
+#define WM8350_BCLK_DIV_12                     (0x9 << 4)
+#define WM8350_BCLK_DIV_16                     (0xa << 4)
+#define WM8350_BCLK_DIV_22                     (0xb << 4)
+#define WM8350_BCLK_DIV_24                     (0xc << 4)
+#define WM8350_BCLK_DIV_32                     (0xd << 4)
+#define WM8350_BCLK_DIV_44                     (0xe << 4)
+#define WM8350_BCLK_DIV_48                     (0xf << 4)
+
+/* Sys (MCLK) clock dividers */
+#define WM8350_MCLK_DIV_1                      (0x0 << 8)
+#define WM8350_MCLK_DIV_2                      (0x1 << 8)
+
+/* OP clock dividers */
+#define WM8350_OPCLK_DIV_1                     0x0
+#define WM8350_OPCLK_DIV_2                     0x1
+#define WM8350_OPCLK_DIV_3                     0x2
+#define WM8350_OPCLK_DIV_4                     0x3
+#define WM8350_OPCLK_DIV_5_5                   0x4
+#define WM8350_OPCLK_DIV_6                     0x5
+
+/* DAI ID */
+#define WM8350_HIFI_DAI                                0
+
+/*
+ * Audio interrupts.
+ */
+#define WM8350_IRQ_CODEC_JCK_DET_L             39
+#define WM8350_IRQ_CODEC_JCK_DET_R             40
+#define WM8350_IRQ_CODEC_MICSCD                        41
+#define WM8350_IRQ_CODEC_MICD                  42
+
+struct wm8350_codec {
+       struct platform_device *pdev;
+};
+
+#endif
diff --git a/include/linux/mfd/wm8350/comparator.h b/include/linux/mfd/wm8350/comparator.h
new file mode 100644 (file)
index 0000000..0537886
--- /dev/null
@@ -0,0 +1,167 @@
+/*
+ * comparator.h  --  Comparator Aux ADC for Wolfson WM8350 PMIC
+ *
+ * Copyright 2007 Wolfson Microelectronics PLC
+ *
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ */
+
+#ifndef __LINUX_MFD_WM8350_COMPARATOR_H_
+#define __LINUX_MFD_WM8350_COMPARATOR_H_
+
+/*
+ * Registers
+ */
+
+#define WM8350_DIGITISER_CONTROL_1              0x90
+#define WM8350_DIGITISER_CONTROL_2              0x91
+#define WM8350_AUX1_READBACK                    0x98
+#define WM8350_AUX2_READBACK                    0x99
+#define WM8350_AUX3_READBACK                    0x9A
+#define WM8350_AUX4_READBACK                    0x9B
+#define WM8350_CHIP_TEMP_READBACK               0x9F
+#define WM8350_GENERIC_COMPARATOR_CONTROL       0xA3
+#define WM8350_GENERIC_COMPARATOR_1             0xA4
+#define WM8350_GENERIC_COMPARATOR_2             0xA5
+#define WM8350_GENERIC_COMPARATOR_3             0xA6
+#define WM8350_GENERIC_COMPARATOR_4             0xA7
+
+/*
+ * R144 (0x90) - Digitiser Control (1)
+ */
+#define WM8350_AUXADC_CTC                       0x4000
+#define WM8350_AUXADC_POLL                      0x2000
+#define WM8350_AUXADC_HIB_MODE                  0x1000
+#define WM8350_AUXADC_SEL8                      0x0080
+#define WM8350_AUXADC_SEL7                      0x0040
+#define WM8350_AUXADC_SEL6                      0x0020
+#define WM8350_AUXADC_SEL5                      0x0010
+#define WM8350_AUXADC_SEL4                      0x0008
+#define WM8350_AUXADC_SEL3                      0x0004
+#define WM8350_AUXADC_SEL2                      0x0002
+#define WM8350_AUXADC_SEL1                      0x0001
+
+/*
+ * R145 (0x91) - Digitiser Control (2)
+ */
+#define WM8350_AUXADC_MASKMODE_MASK             0x3000
+#define WM8350_AUXADC_CRATE_MASK                0x0700
+#define WM8350_AUXADC_CAL                       0x0004
+#define WM8350_AUX_RBMODE                       0x0002
+#define WM8350_AUXADC_WAIT                      0x0001
+
+/*
+ * R152 (0x98) - AUX1 Readback
+ */
+#define WM8350_AUXADC_SCALE1_MASK               0x6000
+#define WM8350_AUXADC_REF1                      0x1000
+#define WM8350_AUXADC_DATA1_MASK                0x0FFF
+
+/*
+ * R153 (0x99) - AUX2 Readback
+ */
+#define WM8350_AUXADC_SCALE2_MASK               0x6000
+#define WM8350_AUXADC_REF2                      0x1000
+#define WM8350_AUXADC_DATA2_MASK                0x0FFF
+
+/*
+ * R154 (0x9A) - AUX3 Readback
+ */
+#define WM8350_AUXADC_SCALE3_MASK               0x6000
+#define WM8350_AUXADC_REF3                      0x1000
+#define WM8350_AUXADC_DATA3_MASK                0x0FFF
+
+/*
+ * R155 (0x9B) - AUX4 Readback
+ */
+#define WM8350_AUXADC_SCALE4_MASK               0x6000
+#define WM8350_AUXADC_REF4                      0x1000
+#define WM8350_AUXADC_DATA4_MASK                0x0FFF
+
+/*
+ * R156 (0x9C) - USB Voltage Readback
+ */
+#define WM8350_AUXADC_DATA_USB_MASK             0x0FFF
+
+/*
+ * R157 (0x9D) - LINE Voltage Readback
+ */
+#define WM8350_AUXADC_DATA_LINE_MASK            0x0FFF
+
+/*
+ * R158 (0x9E) - BATT Voltage Readback
+ */
+#define WM8350_AUXADC_DATA_BATT_MASK            0x0FFF
+
+/*
+ * R159 (0x9F) - Chip Temp Readback
+ */
+#define WM8350_AUXADC_DATA_CHIPTEMP_MASK        0x0FFF
+
+/*
+ * R163 (0xA3) - Generic Comparator Control
+ */
+#define WM8350_DCMP4_ENA                        0x0008
+#define WM8350_DCMP3_ENA                        0x0004
+#define WM8350_DCMP2_ENA                        0x0002
+#define WM8350_DCMP1_ENA                        0x0001
+
+/*
+ * R164 (0xA4) - Generic comparator 1
+ */
+#define WM8350_DCMP1_SRCSEL_MASK                0xE000
+#define WM8350_DCMP1_GT                         0x1000
+#define WM8350_DCMP1_THR_MASK                   0x0FFF
+
+/*
+ * R165 (0xA5) - Generic comparator 2
+ */
+#define WM8350_DCMP2_SRCSEL_MASK                0xE000
+#define WM8350_DCMP2_GT                         0x1000
+#define WM8350_DCMP2_THR_MASK                   0x0FFF
+
+/*
+ * R166 (0xA6) - Generic comparator 3
+ */
+#define WM8350_DCMP3_SRCSEL_MASK                0xE000
+#define WM8350_DCMP3_GT                         0x1000
+#define WM8350_DCMP3_THR_MASK                   0x0FFF
+
+/*
+ * R167 (0xA7) - Generic comparator 4
+ */
+#define WM8350_DCMP4_SRCSEL_MASK                0xE000
+#define WM8350_DCMP4_GT                         0x1000
+#define WM8350_DCMP4_THR_MASK                   0x0FFF
+
+/*
+ * Interrupts.
+ */
+#define WM8350_IRQ_AUXADC_DATARDY              16
+#define WM8350_IRQ_AUXADC_DCOMP4               17
+#define WM8350_IRQ_AUXADC_DCOMP3               18
+#define WM8350_IRQ_AUXADC_DCOMP2               19
+#define WM8350_IRQ_AUXADC_DCOMP1               20
+#define WM8350_IRQ_SYS_HYST_COMP_FAIL          21
+#define WM8350_IRQ_SYS_CHIP_GT115              22
+#define WM8350_IRQ_SYS_CHIP_GT140              23
+
+/*
+ * USB/2, LINE & BATT = ((VRTC * 2) / 4095)) * 10e6 uV
+ * Where VRTC = 2.7 V
+ */
+#define WM8350_AUX_COEFF                       1319
+
+#define WM8350_AUXADC_AUX1                     0
+#define WM8350_AUXADC_AUX2                     1
+#define WM8350_AUXADC_AUX3                     2
+#define WM8350_AUXADC_AUX4                     3
+#define WM8350_AUXADC_USB                      4
+#define WM8350_AUXADC_LINE                     5
+#define WM8350_AUXADC_BATT                     6
+#define WM8350_AUXADC_TEMP                     7
+
+#endif
diff --git a/include/linux/mfd/wm8350/core.h b/include/linux/mfd/wm8350/core.h
new file mode 100644 (file)
index 0000000..6ebf97f
--- /dev/null
@@ -0,0 +1,631 @@
+/*
+ * core.h  --  Core Driver for Wolfson WM8350 PMIC
+ *
+ * Copyright 2007 Wolfson Microelectronics PLC
+ *
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ *
+ */
+
+#ifndef __LINUX_MFD_WM8350_CORE_H_
+#define __LINUX_MFD_WM8350_CORE_H_
+
+#include <linux/kernel.h>
+#include <linux/mutex.h>
+#include <linux/workqueue.h>
+
+#include <linux/mfd/wm8350/audio.h>
+#include <linux/mfd/wm8350/gpio.h>
+#include <linux/mfd/wm8350/pmic.h>
+#include <linux/mfd/wm8350/rtc.h>
+#include <linux/mfd/wm8350/supply.h>
+#include <linux/mfd/wm8350/wdt.h>
+
+/*
+ * Register values.
+ */
+#define WM8350_RESET_ID                         0x00
+#define WM8350_ID                               0x01
+#define WM8350_SYSTEM_CONTROL_1                 0x03
+#define WM8350_SYSTEM_CONTROL_2                 0x04
+#define WM8350_SYSTEM_HIBERNATE                 0x05
+#define WM8350_INTERFACE_CONTROL                0x06
+#define WM8350_POWER_MGMT_1                     0x08
+#define WM8350_POWER_MGMT_2                     0x09
+#define WM8350_POWER_MGMT_3                     0x0A
+#define WM8350_POWER_MGMT_4                     0x0B
+#define WM8350_POWER_MGMT_5                     0x0C
+#define WM8350_POWER_MGMT_6                     0x0D
+#define WM8350_POWER_MGMT_7                     0x0E
+
+#define WM8350_SYSTEM_INTERRUPTS                0x18
+#define WM8350_INT_STATUS_1                     0x19
+#define WM8350_INT_STATUS_2                     0x1A
+#define WM8350_POWER_UP_INT_STATUS              0x1B
+#define WM8350_UNDER_VOLTAGE_INT_STATUS         0x1C
+#define WM8350_OVER_CURRENT_INT_STATUS          0x1D
+#define WM8350_GPIO_INT_STATUS                  0x1E
+#define WM8350_COMPARATOR_INT_STATUS            0x1F
+#define WM8350_SYSTEM_INTERRUPTS_MASK           0x20
+#define WM8350_INT_STATUS_1_MASK                0x21
+#define WM8350_INT_STATUS_2_MASK                0x22
+#define WM8350_POWER_UP_INT_STATUS_MASK         0x23
+#define WM8350_UNDER_VOLTAGE_INT_STATUS_MASK    0x24
+#define WM8350_OVER_CURRENT_INT_STATUS_MASK     0x25
+#define WM8350_GPIO_INT_STATUS_MASK             0x26
+#define WM8350_COMPARATOR_INT_STATUS_MASK       0x27
+
+#define WM8350_MAX_REGISTER                     0xFF
+
+/*
+ * Field Definitions.
+ */
+
+/*
+ * R0 (0x00) - Reset/ID
+ */
+#define WM8350_SW_RESET_CHIP_ID_MASK            0xFFFF
+
+/*
+ * R1 (0x01) - ID
+ */
+#define WM8350_CHIP_REV_MASK                    0x7000
+#define WM8350_CONF_STS_MASK                    0x0C00
+#define WM8350_CUST_ID_MASK                     0x00FF
+
+/*
+ * R3 (0x03) - System Control 1
+ */
+#define WM8350_CHIP_ON                          0x8000
+#define WM8350_POWERCYCLE                       0x2000
+#define WM8350_VCC_FAULT_OV                     0x1000
+#define WM8350_REG_RSTB_TIME_MASK               0x0C00
+#define WM8350_BG_SLEEP                         0x0200
+#define WM8350_MEM_VALID                        0x0020
+#define WM8350_CHIP_SET_UP                      0x0010
+#define WM8350_ON_DEB_T                         0x0008
+#define WM8350_ON_POL                           0x0002
+#define WM8350_IRQ_POL                          0x0001
+
+/*
+ * R4 (0x04) - System Control 2
+ */
+#define WM8350_USB_SUSPEND_8MA                  0x8000
+#define WM8350_USB_SUSPEND                      0x4000
+#define WM8350_USB_MSTR                         0x2000
+#define WM8350_USB_MSTR_SRC                     0x1000
+#define WM8350_USB_500MA                        0x0800
+#define WM8350_USB_NOLIM                        0x0400
+
+/*
+ * R5 (0x05) - System Hibernate
+ */
+#define WM8350_HIBERNATE                        0x8000
+#define WM8350_WDOG_HIB_MODE                    0x0080
+#define WM8350_REG_HIB_STARTUP_SEQ              0x0040
+#define WM8350_REG_RESET_HIB_MODE               0x0020
+#define WM8350_RST_HIB_MODE                     0x0010
+#define WM8350_IRQ_HIB_MODE                     0x0008
+#define WM8350_MEMRST_HIB_MODE                  0x0004
+#define WM8350_PCCOMP_HIB_MODE                  0x0002
+#define WM8350_TEMPMON_HIB_MODE                 0x0001
+
+/*
+ * R6 (0x06) - Interface Control
+ */
+#define WM8350_USE_DEV_PINS                     0x8000
+#define WM8350_USE_DEV_PINS_MASK                0x8000
+#define WM8350_USE_DEV_PINS_SHIFT                   15
+#define WM8350_DEV_ADDR_MASK                    0x6000
+#define WM8350_DEV_ADDR_SHIFT                       13
+#define WM8350_CONFIG_DONE                      0x1000
+#define WM8350_CONFIG_DONE_MASK                 0x1000
+#define WM8350_CONFIG_DONE_SHIFT                    12
+#define WM8350_RECONFIG_AT_ON                   0x0800
+#define WM8350_RECONFIG_AT_ON_MASK              0x0800
+#define WM8350_RECONFIG_AT_ON_SHIFT                 11
+#define WM8350_AUTOINC                          0x0200
+#define WM8350_AUTOINC_MASK                     0x0200
+#define WM8350_AUTOINC_SHIFT                         9
+#define WM8350_ARA                              0x0100
+#define WM8350_ARA_MASK                         0x0100
+#define WM8350_ARA_SHIFT                             8
+#define WM8350_SPI_CFG                          0x0008
+#define WM8350_SPI_CFG_MASK                     0x0008
+#define WM8350_SPI_CFG_SHIFT                         3
+#define WM8350_SPI_4WIRE                        0x0004
+#define WM8350_SPI_4WIRE_MASK                   0x0004
+#define WM8350_SPI_4WIRE_SHIFT                       2
+#define WM8350_SPI_3WIRE                        0x0002
+#define WM8350_SPI_3WIRE_MASK                   0x0002
+#define WM8350_SPI_3WIRE_SHIFT                       1
+
+/* Bit values for R06 (0x06) */
+#define WM8350_USE_DEV_PINS_PRIMARY                  0
+#define WM8350_USE_DEV_PINS_DEV                      1
+
+#define WM8350_DEV_ADDR_34                           0
+#define WM8350_DEV_ADDR_36                           1
+#define WM8350_DEV_ADDR_3C                           2
+#define WM8350_DEV_ADDR_3E                           3
+
+#define WM8350_CONFIG_DONE_OFF                       0
+#define WM8350_CONFIG_DONE_DONE                      1
+
+#define WM8350_RECONFIG_AT_ON_OFF                    0
+#define WM8350_RECONFIG_AT_ON_ON                     1
+
+#define WM8350_AUTOINC_OFF                           0
+#define WM8350_AUTOINC_ON                            1
+
+#define WM8350_ARA_OFF                               0
+#define WM8350_ARA_ON                                1
+
+#define WM8350_SPI_CFG_CMOS                          0
+#define WM8350_SPI_CFG_OD                            1
+
+#define WM8350_SPI_4WIRE_3WIRE                       0
+#define WM8350_SPI_4WIRE_4WIRE                       1
+
+#define WM8350_SPI_3WIRE_I2C                         0
+#define WM8350_SPI_3WIRE_SPI                         1
+
+/*
+ * R8 (0x08) - Power mgmt (1)
+ */
+#define WM8350_CODEC_ISEL_MASK                  0xC000
+#define WM8350_VBUFEN                           0x2000
+#define WM8350_OUTPUT_DRAIN_EN                  0x0400
+#define WM8350_MIC_DET_ENA                      0x0100
+#define WM8350_BIASEN                           0x0020
+#define WM8350_MICBEN                           0x0010
+#define WM8350_VMIDEN                           0x0004
+#define WM8350_VMID_MASK                        0x0003
+#define WM8350_VMID_SHIFT                            0
+
+/*
+ * R9 (0x09) - Power mgmt (2)
+ */
+#define WM8350_IN3R_ENA                         0x0800
+#define WM8350_IN3L_ENA                         0x0400
+#define WM8350_INR_ENA                          0x0200
+#define WM8350_INL_ENA                          0x0100
+#define WM8350_MIXINR_ENA                       0x0080
+#define WM8350_MIXINL_ENA                       0x0040
+#define WM8350_OUT4_ENA                         0x0020
+#define WM8350_OUT3_ENA                         0x0010
+#define WM8350_MIXOUTR_ENA                      0x0002
+#define WM8350_MIXOUTL_ENA                      0x0001
+
+/*
+ * R10 (0x0A) - Power mgmt (3)
+ */
+#define WM8350_IN3R_TO_OUT2R                    0x0080
+#define WM8350_OUT2R_ENA                        0x0008
+#define WM8350_OUT2L_ENA                        0x0004
+#define WM8350_OUT1R_ENA                        0x0002
+#define WM8350_OUT1L_ENA                        0x0001
+
+/*
+ * R11 (0x0B) - Power mgmt (4)
+ */
+#define WM8350_SYSCLK_ENA                       0x4000
+#define WM8350_ADC_HPF_ENA                      0x2000
+#define WM8350_FLL_ENA                          0x0800
+#define WM8350_FLL_OSC_ENA                      0x0400
+#define WM8350_TOCLK_ENA                        0x0100
+#define WM8350_DACR_ENA                         0x0020
+#define WM8350_DACL_ENA                         0x0010
+#define WM8350_ADCR_ENA                         0x0008
+#define WM8350_ADCL_ENA                         0x0004
+
+/*
+ * R12 (0x0C) - Power mgmt (5)
+ */
+#define WM8350_CODEC_ENA                        0x1000
+#define WM8350_RTC_TICK_ENA                     0x0800
+#define WM8350_OSC32K_ENA                       0x0400
+#define WM8350_CHG_ENA                          0x0200
+#define WM8350_ACC_DET_ENA                      0x0100
+#define WM8350_AUXADC_ENA                       0x0080
+#define WM8350_DCMP4_ENA                        0x0008
+#define WM8350_DCMP3_ENA                        0x0004
+#define WM8350_DCMP2_ENA                        0x0002
+#define WM8350_DCMP1_ENA                        0x0001
+
+/*
+ * R13 (0x0D) - Power mgmt (6)
+ */
+#define WM8350_LS_ENA                           0x8000
+#define WM8350_LDO4_ENA                         0x0800
+#define WM8350_LDO3_ENA                         0x0400
+#define WM8350_LDO2_ENA                         0x0200
+#define WM8350_LDO1_ENA                         0x0100
+#define WM8350_DC6_ENA                          0x0020
+#define WM8350_DC5_ENA                          0x0010
+#define WM8350_DC4_ENA                          0x0008
+#define WM8350_DC3_ENA                          0x0004
+#define WM8350_DC2_ENA                          0x0002
+#define WM8350_DC1_ENA                          0x0001
+
+/*
+ * R14 (0x0E) - Power mgmt (7)
+ */
+#define WM8350_CS2_ENA                          0x0002
+#define WM8350_CS1_ENA                          0x0001
+
+/*
+ * R24 (0x18) - System Interrupts
+ */
+#define WM8350_OC_INT                           0x2000
+#define WM8350_UV_INT                           0x1000
+#define WM8350_PUTO_INT                         0x0800
+#define WM8350_CS_INT                           0x0200
+#define WM8350_EXT_INT                          0x0100
+#define WM8350_CODEC_INT                        0x0080
+#define WM8350_GP_INT                           0x0040
+#define WM8350_AUXADC_INT                       0x0020
+#define WM8350_RTC_INT                          0x0010
+#define WM8350_SYS_INT                          0x0008
+#define WM8350_CHG_INT                          0x0004
+#define WM8350_USB_INT                          0x0002
+#define WM8350_WKUP_INT                         0x0001
+
+/*
+ * R25 (0x19) - Interrupt Status 1
+ */
+#define WM8350_CHG_BAT_HOT_EINT                 0x8000
+#define WM8350_CHG_BAT_COLD_EINT                0x4000
+#define WM8350_CHG_BAT_FAIL_EINT                0x2000
+#define WM8350_CHG_TO_EINT                      0x1000
+#define WM8350_CHG_END_EINT                     0x0800
+#define WM8350_CHG_START_EINT                   0x0400
+#define WM8350_CHG_FAST_RDY_EINT                0x0200
+#define WM8350_RTC_PER_EINT                     0x0080
+#define WM8350_RTC_SEC_EINT                     0x0040
+#define WM8350_RTC_ALM_EINT                     0x0020
+#define WM8350_CHG_VBATT_LT_3P9_EINT            0x0004
+#define WM8350_CHG_VBATT_LT_3P1_EINT            0x0002
+#define WM8350_CHG_VBATT_LT_2P85_EINT           0x0001
+
+/*
+ * R26 (0x1A) - Interrupt Status 2
+ */
+#define WM8350_CS1_EINT                         0x2000
+#define WM8350_CS2_EINT                         0x1000
+#define WM8350_USB_LIMIT_EINT                   0x0400
+#define WM8350_AUXADC_DATARDY_EINT              0x0100
+#define WM8350_AUXADC_DCOMP4_EINT               0x0080
+#define WM8350_AUXADC_DCOMP3_EINT               0x0040
+#define WM8350_AUXADC_DCOMP2_EINT               0x0020
+#define WM8350_AUXADC_DCOMP1_EINT               0x0010
+#define WM8350_SYS_HYST_COMP_FAIL_EINT          0x0008
+#define WM8350_SYS_CHIP_GT115_EINT              0x0004
+#define WM8350_SYS_CHIP_GT140_EINT              0x0002
+#define WM8350_SYS_WDOG_TO_EINT                 0x0001
+
+/*
+ * R27 (0x1B) - Power Up Interrupt Status
+ */
+#define WM8350_PUTO_LDO4_EINT                   0x0800
+#define WM8350_PUTO_LDO3_EINT                   0x0400
+#define WM8350_PUTO_LDO2_EINT                   0x0200
+#define WM8350_PUTO_LDO1_EINT                   0x0100
+#define WM8350_PUTO_DC6_EINT                    0x0020
+#define WM8350_PUTO_DC5_EINT                    0x0010
+#define WM8350_PUTO_DC4_EINT                    0x0008
+#define WM8350_PUTO_DC3_EINT                    0x0004
+#define WM8350_PUTO_DC2_EINT                    0x0002
+#define WM8350_PUTO_DC1_EINT                    0x0001
+
+/*
+ * R28 (0x1C) - Under Voltage Interrupt status
+ */
+#define WM8350_UV_LDO4_EINT                     0x0800
+#define WM8350_UV_LDO3_EINT                     0x0400
+#define WM8350_UV_LDO2_EINT                     0x0200
+#define WM8350_UV_LDO1_EINT                     0x0100
+#define WM8350_UV_DC6_EINT                      0x0020
+#define WM8350_UV_DC5_EINT                      0x0010
+#define WM8350_UV_DC4_EINT                      0x0008
+#define WM8350_UV_DC3_EINT                      0x0004
+#define WM8350_UV_DC2_EINT                      0x0002
+#define WM8350_UV_DC1_EINT                      0x0001
+
+/*
+ * R29 (0x1D) - Over Current Interrupt status
+ */
+#define WM8350_OC_LS_EINT                       0x8000
+
+/*
+ * R30 (0x1E) - GPIO Interrupt Status
+ */
+#define WM8350_GP12_EINT                        0x1000
+#define WM8350_GP11_EINT                        0x0800
+#define WM8350_GP10_EINT                        0x0400
+#define WM8350_GP9_EINT                         0x0200
+#define WM8350_GP8_EINT                         0x0100
+#define WM8350_GP7_EINT                         0x0080
+#define WM8350_GP6_EINT                         0x0040
+#define WM8350_GP5_EINT                         0x0020
+#define WM8350_GP4_EINT                         0x0010
+#define WM8350_GP3_EINT                         0x0008
+#define WM8350_GP2_EINT                         0x0004
+#define WM8350_GP1_EINT                         0x0002
+#define WM8350_GP0_EINT                         0x0001
+
+/*
+ * R31 (0x1F) - Comparator Interrupt Status
+ */
+#define WM8350_EXT_USB_FB_EINT                  0x8000
+#define WM8350_EXT_WALL_FB_EINT                 0x4000
+#define WM8350_EXT_BAT_FB_EINT                  0x2000
+#define WM8350_CODEC_JCK_DET_L_EINT             0x0800
+#define WM8350_CODEC_JCK_DET_R_EINT             0x0400
+#define WM8350_CODEC_MICSCD_EINT                0x0200
+#define WM8350_CODEC_MICD_EINT                  0x0100
+#define WM8350_WKUP_OFF_STATE_EINT              0x0040
+#define WM8350_WKUP_HIB_STATE_EINT              0x0020
+#define WM8350_WKUP_CONV_FAULT_EINT             0x0010
+#define WM8350_WKUP_WDOG_RST_EINT               0x0008
+#define WM8350_WKUP_GP_PWR_ON_EINT              0x0004
+#define WM8350_WKUP_ONKEY_EINT                  0x0002
+#define WM8350_WKUP_GP_WAKEUP_EINT              0x0001
+
+/*
+ * R32 (0x20) - System Interrupts Mask
+ */
+#define WM8350_IM_OC_INT                        0x2000
+#define WM8350_IM_UV_INT                        0x1000
+#define WM8350_IM_PUTO_INT                      0x0800
+#define WM8350_IM_SPARE_INT                     0x0400
+#define WM8350_IM_CS_INT                        0x0200
+#define WM8350_IM_EXT_INT                       0x0100
+#define WM8350_IM_CODEC_INT                     0x0080
+#define WM8350_IM_GP_INT                        0x0040
+#define WM8350_IM_AUXADC_INT                    0x0020
+#define WM8350_IM_RTC_INT                       0x0010
+#define WM8350_IM_SYS_INT                       0x0008
+#define WM8350_IM_CHG_INT                       0x0004
+#define WM8350_IM_USB_INT                       0x0002
+#define WM8350_IM_WKUP_INT                      0x0001
+
+/*
+ * R33 (0x21) - Interrupt Status 1 Mask
+ */
+#define WM8350_IM_CHG_BAT_HOT_EINT              0x8000
+#define WM8350_IM_CHG_BAT_COLD_EINT             0x4000
+#define WM8350_IM_CHG_BAT_FAIL_EINT             0x2000
+#define WM8350_IM_CHG_TO_EINT                   0x1000
+#define WM8350_IM_CHG_END_EINT                  0x0800
+#define WM8350_IM_CHG_START_EINT                0x0400
+#define WM8350_IM_CHG_FAST_RDY_EINT             0x0200
+#define WM8350_IM_RTC_PER_EINT                  0x0080
+#define WM8350_IM_RTC_SEC_EINT                  0x0040
+#define WM8350_IM_RTC_ALM_EINT                  0x0020
+#define WM8350_IM_CHG_VBATT_LT_3P9_EINT         0x0004
+#define WM8350_IM_CHG_VBATT_LT_3P1_EINT         0x0002
+#define WM8350_IM_CHG_VBATT_LT_2P85_EINT        0x0001
+
+/*
+ * R34 (0x22) - Interrupt Status 2 Mask
+ */
+#define WM8350_IM_SPARE2_EINT                   0x8000
+#define WM8350_IM_SPARE1_EINT                   0x4000
+#define WM8350_IM_CS1_EINT                      0x2000
+#define WM8350_IM_CS2_EINT                      0x1000
+#define WM8350_IM_USB_LIMIT_EINT                0x0400
+#define WM8350_IM_AUXADC_DATARDY_EINT           0x0100
+#define WM8350_IM_AUXADC_DCOMP4_EINT            0x0080
+#define WM8350_IM_AUXADC_DCOMP3_EINT            0x0040
+#define WM8350_IM_AUXADC_DCOMP2_EINT            0x0020
+#define WM8350_IM_AUXADC_DCOMP1_EINT            0x0010
+#define WM8350_IM_SYS_HYST_COMP_FAIL_EINT       0x0008
+#define WM8350_IM_SYS_CHIP_GT115_EINT           0x0004
+#define WM8350_IM_SYS_CHIP_GT140_EINT           0x0002
+#define WM8350_IM_SYS_WDOG_TO_EINT              0x0001
+
+/*
+ * R35 (0x23) - Power Up Interrupt Status Mask
+ */
+#define WM8350_IM_PUTO_LDO4_EINT                0x0800
+#define WM8350_IM_PUTO_LDO3_EINT                0x0400
+#define WM8350_IM_PUTO_LDO2_EINT                0x0200
+#define WM8350_IM_PUTO_LDO1_EINT                0x0100
+#define WM8350_IM_PUTO_DC6_EINT                 0x0020
+#define WM8350_IM_PUTO_DC5_EINT                 0x0010
+#define WM8350_IM_PUTO_DC4_EINT                 0x0008
+#define WM8350_IM_PUTO_DC3_EINT                 0x0004
+#define WM8350_IM_PUTO_DC2_EINT                 0x0002
+#define WM8350_IM_PUTO_DC1_EINT                 0x0001
+
+/*
+ * R36 (0x24) - Under Voltage Interrupt status Mask
+ */
+#define WM8350_IM_UV_LDO4_EINT                  0x0800
+#define WM8350_IM_UV_LDO3_EINT                  0x0400
+#define WM8350_IM_UV_LDO2_EINT                  0x0200
+#define WM8350_IM_UV_LDO1_EINT                  0x0100
+#define WM8350_IM_UV_DC6_EINT                   0x0020
+#define WM8350_IM_UV_DC5_EINT                   0x0010
+#define WM8350_IM_UV_DC4_EINT                   0x0008
+#define WM8350_IM_UV_DC3_EINT                   0x0004
+#define WM8350_IM_UV_DC2_EINT                   0x0002
+#define WM8350_IM_UV_DC1_EINT                   0x0001
+
+/*
+ * R37 (0x25) - Over Current Interrupt status Mask
+ */
+#define WM8350_IM_OC_LS_EINT                    0x8000
+
+/*
+ * R38 (0x26) - GPIO Interrupt Status Mask
+ */
+#define WM8350_IM_GP12_EINT                     0x1000
+#define WM8350_IM_GP11_EINT                     0x0800
+#define WM8350_IM_GP10_EINT                     0x0400
+#define WM8350_IM_GP9_EINT                      0x0200
+#define WM8350_IM_GP8_EINT                      0x0100
+#define WM8350_IM_GP7_EINT                      0x0080
+#define WM8350_IM_GP6_EINT                      0x0040
+#define WM8350_IM_GP5_EINT                      0x0020
+#define WM8350_IM_GP4_EINT                      0x0010
+#define WM8350_IM_GP3_EINT                      0x0008
+#define WM8350_IM_GP2_EINT                      0x0004
+#define WM8350_IM_GP1_EINT                      0x0002
+#define WM8350_IM_GP0_EINT                      0x0001
+
+/*
+ * R39 (0x27) - Comparator Interrupt Status Mask
+ */
+#define WM8350_IM_EXT_USB_FB_EINT               0x8000
+#define WM8350_IM_EXT_WALL_FB_EINT              0x4000
+#define WM8350_IM_EXT_BAT_FB_EINT               0x2000
+#define WM8350_IM_CODEC_JCK_DET_L_EINT          0x0800
+#define WM8350_IM_CODEC_JCK_DET_R_EINT          0x0400
+#define WM8350_IM_CODEC_MICSCD_EINT             0x0200
+#define WM8350_IM_CODEC_MICD_EINT               0x0100
+#define WM8350_IM_WKUP_OFF_STATE_EINT           0x0040
+#define WM8350_IM_WKUP_HIB_STATE_EINT           0x0020
+#define WM8350_IM_WKUP_CONV_FAULT_EINT          0x0010
+#define WM8350_IM_WKUP_WDOG_RST_EINT            0x0008
+#define WM8350_IM_WKUP_GP_PWR_ON_EINT           0x0004
+#define WM8350_IM_WKUP_ONKEY_EINT               0x0002
+#define WM8350_IM_WKUP_GP_WAKEUP_EINT           0x0001
+
+/*
+ * R220 (0xDC) - RAM BIST 1
+ */
+#define WM8350_READ_STATUS                      0x0800
+#define WM8350_TSTRAM_CLK                       0x0100
+#define WM8350_TSTRAM_CLK_ENA                   0x0080
+#define WM8350_STARTSEQ                         0x0040
+#define WM8350_READ_SRC                         0x0020
+#define WM8350_COUNT_DIR                        0x0010
+#define WM8350_TSTRAM_MODE_MASK                 0x000E
+#define WM8350_TSTRAM_ENA                       0x0001
+
+/*
+ * R225 (0xE1) - DCDC/LDO status
+ */
+#define WM8350_LS_STS                           0x8000
+#define WM8350_LDO4_STS                         0x0800
+#define WM8350_LDO3_STS                         0x0400
+#define WM8350_LDO2_STS                         0x0200
+#define WM8350_LDO1_STS                         0x0100
+#define WM8350_DC6_STS                          0x0020
+#define WM8350_DC5_STS                          0x0010
+#define WM8350_DC4_STS                          0x0008
+#define WM8350_DC3_STS                          0x0004
+#define WM8350_DC2_STS                          0x0002
+#define WM8350_DC1_STS                          0x0001
+
+/* WM8350 wake up conditions */
+#define WM8350_IRQ_WKUP_OFF_STATE              43
+#define WM8350_IRQ_WKUP_HIB_STATE              44
+#define WM8350_IRQ_WKUP_CONV_FAULT             45
+#define WM8350_IRQ_WKUP_WDOG_RST               46
+#define WM8350_IRQ_WKUP_GP_PWR_ON              47
+#define WM8350_IRQ_WKUP_ONKEY                  48
+#define WM8350_IRQ_WKUP_GP_WAKEUP              49
+
+/* wm8350 chip revisions */
+#define WM8350_REV_E                           0x4
+#define WM8350_REV_F                           0x5
+#define WM8350_REV_G                           0x6
+
+#define WM8350_NUM_IRQ                         63
+
+struct wm8350_reg_access {
+       u16 readable;           /* Mask of readable bits */
+       u16 writable;           /* Mask of writable bits */
+       u16 vol;                /* Mask of volatile bits */
+};
+extern const struct wm8350_reg_access wm8350_reg_io_map[];
+extern const u16 wm8350_mode0_defaults[];
+extern const u16 wm8350_mode1_defaults[];
+extern const u16 wm8350_mode2_defaults[];
+extern const u16 wm8350_mode3_defaults[];
+
+struct wm8350;
+
+struct wm8350_irq {
+       void (*handler) (struct wm8350 *, int, void *);
+       void *data;
+};
+
+struct wm8350 {
+       int rev;                /* chip revision */
+
+       struct device *dev;
+
+       /* device IO */
+       union {
+               struct i2c_client *i2c_client;
+               struct spi_device *spi_device;
+       };
+       int (*read_dev)(struct wm8350 *wm8350, char reg, int size, void *dest);
+       int (*write_dev)(struct wm8350 *wm8350, char reg, int size,
+                        void *src);
+       u16 *reg_cache;
+
+       /* Interrupt handling */
+       struct work_struct irq_work;
+       struct mutex irq_mutex; /* IRQ table mutex */
+       struct wm8350_irq irq[WM8350_NUM_IRQ];
+       int chip_irq;
+
+       /* Client devices */
+       struct wm8350_codec codec;
+       struct wm8350_gpio gpio;
+       struct wm8350_pmic pmic;
+       struct wm8350_power power;
+       struct wm8350_rtc rtc;
+       struct wm8350_wdt wdt;
+};
+
+/**
+ * Data to be supplied by the platform to initialise the WM8350.
+ *
+ * @init: Function called during driver initialisation.  Should be
+ *        used by the platform to configure GPIO functions and similar.
+ */
+struct wm8350_platform_data {
+       int (*init)(struct wm8350 *wm8350);
+};
+
+
+/*
+ * WM8350 device initialisation and exit.
+ */
+int wm8350_device_init(struct wm8350 *wm8350, int irq,
+                      struct wm8350_platform_data *pdata);
+void wm8350_device_exit(struct wm8350 *wm8350);
+
+/*
+ * WM8350 device IO
+ */
+int wm8350_clear_bits(struct wm8350 *wm8350, u16 reg, u16 mask);
+int wm8350_set_bits(struct wm8350 *wm8350, u16 reg, u16 mask);
+u16 wm8350_reg_read(struct wm8350 *wm8350, int reg);
+int wm8350_reg_write(struct wm8350 *wm8350, int reg, u16 val);
+int wm8350_reg_lock(struct wm8350 *wm8350);
+int wm8350_reg_unlock(struct wm8350 *wm8350);
+int wm8350_block_read(struct wm8350 *wm8350, int reg, int size, u16 *dest);
+int wm8350_block_write(struct wm8350 *wm8350, int reg, int size, u16 *src);
+
+/*
+ * WM8350 internal interrupts
+ */
+int wm8350_register_irq(struct wm8350 *wm8350, int irq,
+                       void (*handler) (struct wm8350 *, int, void *),
+                       void *data);
+int wm8350_free_irq(struct wm8350 *wm8350, int irq);
+int wm8350_mask_irq(struct wm8350 *wm8350, int irq);
+int wm8350_unmask_irq(struct wm8350 *wm8350, int irq);
+
+
+#endif
diff --git a/include/linux/mfd/wm8350/gpio.h b/include/linux/mfd/wm8350/gpio.h
new file mode 100644 (file)
index 0000000..ed91e8f
--- /dev/null
@@ -0,0 +1,342 @@
+/*
+ * gpio.h  --  GPIO Driver for Wolfson WM8350 PMIC
+ *
+ * Copyright 2007 Wolfson Microelectronics PLC
+ *
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ *
+ */
+
+#ifndef __LINUX_MFD_WM8350_GPIO_H_
+#define __LINUX_MFD_WM8350_GPIO_H_
+
+#include <linux/platform_device.h>
+
+/*
+ * GPIO Registers.
+ */
+#define WM8350_GPIO_DEBOUNCE                    0x80
+#define WM8350_GPIO_PIN_PULL_UP_CONTROL         0x81
+#define WM8350_GPIO_PULL_DOWN_CONTROL           0x82
+#define WM8350_GPIO_INT_MODE                    0x83
+#define WM8350_GPIO_CONTROL                     0x85
+#define WM8350_GPIO_CONFIGURATION_I_O           0x86
+#define WM8350_GPIO_PIN_POLARITY_TYPE           0x87
+#define WM8350_GPIO_FUNCTION_SELECT_1           0x8C
+#define WM8350_GPIO_FUNCTION_SELECT_2           0x8D
+#define WM8350_GPIO_FUNCTION_SELECT_3           0x8E
+#define WM8350_GPIO_FUNCTION_SELECT_4           0x8F
+
+/*
+ * GPIO Functions
+ */
+#define WM8350_GPIO0_GPIO_IN                   0x0
+#define WM8350_GPIO0_GPIO_OUT                  0x0
+#define WM8350_GPIO0_PWR_ON_IN                 0x1
+#define WM8350_GPIO0_PWR_ON_OUT                        0x1
+#define WM8350_GPIO0_LDO_EN_IN                 0x2
+#define WM8350_GPIO0_VRTC_OUT                  0x2
+#define WM8350_GPIO0_LPWR1_IN                  0x3
+#define WM8350_GPIO0_POR_B_OUT                 0x3
+
+#define WM8350_GPIO1_GPIO_IN                   0x0
+#define WM8350_GPIO1_GPIO_OUT                  0x0
+#define WM8350_GPIO1_PWR_ON_IN                 0x1
+#define WM8350_GPIO1_DO_CONF_OUT               0x1
+#define WM8350_GPIO1_LDO_EN_IN                 0x2
+#define WM8350_GPIO1_RESET_OUT                 0x2
+#define WM8350_GPIO1_LPWR2_IN                  0x3
+#define WM8350_GPIO1_MEMRST_OUT                        0x3
+
+#define WM8350_GPIO2_GPIO_IN                   0x0
+#define WM8350_GPIO2_GPIO_OUT                  0x0
+#define WM8350_GPIO2_PWR_ON_IN                 0x1
+#define WM8350_GPIO2_PWR_ON_OUT                        0x1
+#define WM8350_GPIO2_WAKE_UP_IN                        0x2
+#define WM8350_GPIO2_VRTC_OUT                  0x2
+#define WM8350_GPIO2_32KHZ_IN                  0x3
+#define WM8350_GPIO2_32KHZ_OUT                 0x3
+
+#define WM8350_GPIO3_GPIO_IN                   0x0
+#define WM8350_GPIO3_GPIO_OUT                  0x0
+#define WM8350_GPIO3_PWR_ON_IN                 0x1
+#define WM8350_GPIO3_P_CLK_OUT                 0x1
+#define WM8350_GPIO3_LDO_EN_IN                 0x2
+#define WM8350_GPIO3_VRTC_OUT                  0x2
+#define WM8350_GPIO3_PWR_OFF_IN                        0x3
+#define WM8350_GPIO3_32KHZ_OUT                 0x3
+
+#define WM8350_GPIO4_GPIO_IN                   0x0
+#define WM8350_GPIO4_GPIO_OUT                  0x0
+#define WM8350_GPIO4_MR_IN                     0x1
+#define WM8350_GPIO4_MEM_RST_OUT               0x1
+#define WM8350_GPIO4_FLASH_IN                  0x2
+#define WM8350_GPIO4_ADA_OUT                   0x2
+#define WM8350_GPIO4_HIBERNATE_IN              0x3
+#define WM8350_GPIO4_FLASH_OUT                 0x3
+#define WM8350_GPIO4_MICDET_OUT                        0x4
+#define WM8350_GPIO4_MICSHT_OUT                        0x5
+
+#define WM8350_GPIO5_GPIO_IN                   0x0
+#define WM8350_GPIO5_GPIO_OUT                  0x0
+#define WM8350_GPIO5_LPWR1_IN                  0x1
+#define WM8350_GPIO5_P_CLK_OUT                 0x1
+#define WM8350_GPIO5_ADCLRCLK_IN               0x2
+#define WM8350_GPIO5_ADCLRCLK_OUT              0x2
+#define WM8350_GPIO5_HIBERNATE_IN              0x3
+#define WM8350_GPIO5_32KHZ_OUT                 0x3
+#define WM8350_GPIO5_MICDET_OUT                        0x4
+#define WM8350_GPIO5_MICSHT_OUT                        0x5
+#define WM8350_GPIO5_ADA_OUT                   0x6
+#define WM8350_GPIO5_OPCLK_OUT                 0x7
+
+#define WM8350_GPIO6_GPIO_IN                   0x0
+#define WM8350_GPIO6_GPIO_OUT                  0x0
+#define WM8350_GPIO6_LPWR2_IN                  0x1
+#define WM8350_GPIO6_MEMRST_OUT                        0x1
+#define WM8350_GPIO6_FLASH_IN                  0x2
+#define WM8350_GPIO6_ADA_OUT                   0x2
+#define WM8350_GPIO6_HIBERNATE_IN              0x3
+#define WM8350_GPIO6_RTC_OUT                   0x3
+#define WM8350_GPIO6_MICDET_OUT                        0x4
+#define WM8350_GPIO6_MICSHT_OUT                        0x5
+#define WM8350_GPIO6_ADCLRCLKB_OUT             0x6
+#define WM8350_GPIO6_SDOUT_OUT                 0x7
+
+#define WM8350_GPIO7_GPIO_IN                   0x0
+#define WM8350_GPIO7_GPIO_OUT                  0x0
+#define WM8350_GPIO7_LPWR3_IN                  0x1
+#define WM8350_GPIO7_P_CLK_OUT                 0x1
+#define WM8350_GPIO7_MASK_IN                   0x2
+#define WM8350_GPIO7_VCC_FAULT_OUT             0x2
+#define WM8350_GPIO7_HIBERNATE_IN              0x3
+#define WM8350_GPIO7_BATT_FAULT_OUT            0x3
+#define WM8350_GPIO7_MICDET_OUT                        0x4
+#define WM8350_GPIO7_MICSHT_OUT                        0x5
+#define WM8350_GPIO7_ADA_OUT                   0x6
+#define WM8350_GPIO7_CSB_IN                    0x7
+
+#define WM8350_GPIO8_GPIO_IN                   0x0
+#define WM8350_GPIO8_GPIO_OUT                  0x0
+#define WM8350_GPIO8_MR_IN                     0x1
+#define WM8350_GPIO8_VCC_FAULT_OUT             0x1
+#define WM8350_GPIO8_ADCBCLK_IN                        0x2
+#define WM8350_GPIO8_ADCBCLK_OUT               0x2
+#define WM8350_GPIO8_PWR_OFF_IN                        0x3
+#define WM8350_GPIO8_BATT_FAULT_OUT            0x3
+#define WM8350_GPIO8_ALTSCL_IN                 0xf
+
+#define WM8350_GPIO9_GPIO_IN                   0x0
+#define WM8350_GPIO9_GPIO_OUT                  0x0
+#define WM8350_GPIO9_HEARTBEAT_IN              0x1
+#define WM8350_GPIO9_VCC_FAULT_OUT             0x1
+#define WM8350_GPIO9_MASK_IN                   0x2
+#define WM8350_GPIO9_LINE_GT_BATT_OUT          0x2
+#define WM8350_GPIO9_PWR_OFF_IN                        0x3
+#define WM8350_GPIO9_BATT_FAULT_OUT            0x3
+#define WM8350_GPIO9_ALTSDA_OUT                        0xf
+
+#define WM8350_GPIO10_GPIO_IN                  0x0
+#define WM8350_GPIO10_GPIO_OUT                 0x0
+#define WM8350_GPIO10_ISINKC_OUT               0x1
+#define WM8350_GPIO10_PWR_OFF_IN               0x2
+#define WM8350_GPIO10_LINE_GT_BATT_OUT         0x2
+#define WM8350_GPIO10_CHD_IND_IN               0x3
+
+#define WM8350_GPIO11_GPIO_IN                  0x0
+#define WM8350_GPIO11_GPIO_OUT                 0x0
+#define WM8350_GPIO11_ISINKD_OUT               0x1
+#define WM8350_GPIO11_WAKEUP_IN                        0x2
+#define WM8350_GPIO11_LINE_GT_BATT_OUT         0x2
+#define WM8350_GPIO11_CHD_IND_IN               0x3
+
+#define WM8350_GPIO12_GPIO_IN                  0x0
+#define WM8350_GPIO12_GPIO_OUT                 0x0
+#define WM8350_GPIO12_ISINKE_OUT               0x1
+#define WM8350_GPIO12_LINE_GT_BATT_OUT         0x2
+#define WM8350_GPIO12_LINE_EN_OUT              0x3
+#define WM8350_GPIO12_32KHZ_OUT                        0x4
+
+#define WM8350_GPIO_DIR_IN                     0
+#define WM8350_GPIO_DIR_OUT                    1
+#define WM8350_GPIO_ACTIVE_LOW                 0
+#define WM8350_GPIO_ACTIVE_HIGH                        1
+#define WM8350_GPIO_PULL_NONE                  0
+#define WM8350_GPIO_PULL_UP                    1
+#define WM8350_GPIO_PULL_DOWN                  2
+#define WM8350_GPIO_INVERT_OFF                 0
+#define WM8350_GPIO_INVERT_ON                  1
+#define WM8350_GPIO_DEBOUNCE_OFF               0
+#define WM8350_GPIO_DEBOUNCE_ON                        1
+
+/*
+ * R128 (0x80) - GPIO Debounce
+ */
+#define WM8350_GP12_DB                          0x1000
+#define WM8350_GP11_DB                          0x0800
+#define WM8350_GP10_DB                          0x0400
+#define WM8350_GP9_DB                           0x0200
+#define WM8350_GP8_DB                           0x0100
+#define WM8350_GP7_DB                           0x0080
+#define WM8350_GP6_DB                           0x0040
+#define WM8350_GP5_DB                           0x0020
+#define WM8350_GP4_DB                           0x0010
+#define WM8350_GP3_DB                           0x0008
+#define WM8350_GP2_DB                           0x0004
+#define WM8350_GP1_DB                           0x0002
+#define WM8350_GP0_DB                           0x0001
+
+/*
+ * R129 (0x81) - GPIO Pin pull up Control
+ */
+#define WM8350_GP12_PU                          0x1000
+#define WM8350_GP11_PU                          0x0800
+#define WM8350_GP10_PU                          0x0400
+#define WM8350_GP9_PU                           0x0200
+#define WM8350_GP8_PU                           0x0100
+#define WM8350_GP7_PU                           0x0080
+#define WM8350_GP6_PU                           0x0040
+#define WM8350_GP5_PU                           0x0020
+#define WM8350_GP4_PU                           0x0010
+#define WM8350_GP3_PU                           0x0008
+#define WM8350_GP2_PU                           0x0004
+#define WM8350_GP1_PU                           0x0002
+#define WM8350_GP0_PU                           0x0001
+
+/*
+ * R130 (0x82) - GPIO Pull down Control
+ */
+#define WM8350_GP12_PD                          0x1000
+#define WM8350_GP11_PD                          0x0800
+#define WM8350_GP10_PD                          0x0400
+#define WM8350_GP9_PD                           0x0200
+#define WM8350_GP8_PD                           0x0100
+#define WM8350_GP7_PD                           0x0080
+#define WM8350_GP6_PD                           0x0040
+#define WM8350_GP5_PD                           0x0020
+#define WM8350_GP4_PD                           0x0010
+#define WM8350_GP3_PD                           0x0008
+#define WM8350_GP2_PD                           0x0004
+#define WM8350_GP1_PD                           0x0002
+#define WM8350_GP0_PD                           0x0001
+
+/*
+ * R131 (0x83) - GPIO Interrupt Mode
+ */
+#define WM8350_GP12_INTMODE                     0x1000
+#define WM8350_GP11_INTMODE                     0x0800
+#define WM8350_GP10_INTMODE                     0x0400
+#define WM8350_GP9_INTMODE                      0x0200
+#define WM8350_GP8_INTMODE                      0x0100
+#define WM8350_GP7_INTMODE                      0x0080
+#define WM8350_GP6_INTMODE                      0x0040
+#define WM8350_GP5_INTMODE                      0x0020
+#define WM8350_GP4_INTMODE                      0x0010
+#define WM8350_GP3_INTMODE                      0x0008
+#define WM8350_GP2_INTMODE                      0x0004
+#define WM8350_GP1_INTMODE                      0x0002
+#define WM8350_GP0_INTMODE                      0x0001
+
+/*
+ * R133 (0x85) - GPIO Control
+ */
+#define WM8350_GP_DBTIME_MASK                   0x00C0
+
+/*
+ * R134 (0x86) - GPIO Configuration (i/o)
+ */
+#define WM8350_GP12_DIR                         0x1000
+#define WM8350_GP11_DIR                         0x0800
+#define WM8350_GP10_DIR                         0x0400
+#define WM8350_GP9_DIR                          0x0200
+#define WM8350_GP8_DIR                          0x0100
+#define WM8350_GP7_DIR                          0x0080
+#define WM8350_GP6_DIR                          0x0040
+#define WM8350_GP5_DIR                          0x0020
+#define WM8350_GP4_DIR                          0x0010
+#define WM8350_GP3_DIR                          0x0008
+#define WM8350_GP2_DIR                          0x0004
+#define WM8350_GP1_DIR                          0x0002
+#define WM8350_GP0_DIR                          0x0001
+
+/*
+ * R135 (0x87) - GPIO Pin Polarity / Type
+ */
+#define WM8350_GP12_CFG                         0x1000
+#define WM8350_GP11_CFG                         0x0800
+#define WM8350_GP10_CFG                         0x0400
+#define WM8350_GP9_CFG                          0x0200
+#define WM8350_GP8_CFG                          0x0100
+#define WM8350_GP7_CFG                          0x0080
+#define WM8350_GP6_CFG                          0x0040
+#define WM8350_GP5_CFG                          0x0020
+#define WM8350_GP4_CFG                          0x0010
+#define WM8350_GP3_CFG                          0x0008
+#define WM8350_GP2_CFG                          0x0004
+#define WM8350_GP1_CFG                          0x0002
+#define WM8350_GP0_CFG                          0x0001
+
+/*
+ * R140 (0x8C) - GPIO Function Select 1
+ */
+#define WM8350_GP3_FN_MASK                      0xF000
+#define WM8350_GP2_FN_MASK                      0x0F00
+#define WM8350_GP1_FN_MASK                      0x00F0
+#define WM8350_GP0_FN_MASK                      0x000F
+
+/*
+ * R141 (0x8D) - GPIO Function Select 2
+ */
+#define WM8350_GP7_FN_MASK                      0xF000
+#define WM8350_GP6_FN_MASK                      0x0F00
+#define WM8350_GP5_FN_MASK                      0x00F0
+#define WM8350_GP4_FN_MASK                      0x000F
+
+/*
+ * R142 (0x8E) - GPIO Function Select 3
+ */
+#define WM8350_GP11_FN_MASK                     0xF000
+#define WM8350_GP10_FN_MASK                     0x0F00
+#define WM8350_GP9_FN_MASK                      0x00F0
+#define WM8350_GP8_FN_MASK                      0x000F
+
+/*
+ * R143 (0x8F) - GPIO Function Select 4
+ */
+#define WM8350_GP12_FN_MASK                     0x000F
+
+/*
+ * R230 (0xE6) - GPIO Pin Status
+ */
+#define WM8350_GP12_LVL                         0x1000
+#define WM8350_GP11_LVL                         0x0800
+#define WM8350_GP10_LVL                         0x0400
+#define WM8350_GP9_LVL                          0x0200
+#define WM8350_GP8_LVL                          0x0100
+#define WM8350_GP7_LVL                          0x0080
+#define WM8350_GP6_LVL                          0x0040
+#define WM8350_GP5_LVL                          0x0020
+#define WM8350_GP4_LVL                          0x0010
+#define WM8350_GP3_LVL                          0x0008
+#define WM8350_GP2_LVL                          0x0004
+#define WM8350_GP1_LVL                          0x0002
+#define WM8350_GP0_LVL                          0x0001
+
+struct wm8350;
+
+int wm8350_gpio_config(struct wm8350 *wm8350, int gpio, int dir, int func,
+                      int pol, int pull, int invert, int debounce);
+
+struct wm8350_gpio {
+       struct platform_device *pdev;
+};
+
+/*
+ * GPIO Interrupts
+ */
+#define WM8350_IRQ_GPIO(x)                      (50 + x)
+
+#endif
diff --git a/include/linux/mfd/wm8350/pmic.h b/include/linux/mfd/wm8350/pmic.h
new file mode 100644 (file)
index 0000000..69b69e0
--- /dev/null
@@ -0,0 +1,741 @@
+/*
+ * pmic.h  --  Power Managment Driver for Wolfson WM8350 PMIC
+ *
+ * Copyright 2007 Wolfson Microelectronics PLC
+ *
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ *
+ */
+
+#ifndef __LINUX_MFD_WM8350_PMIC_H
+#define __LINUX_MFD_WM8350_PMIC_H
+
+/*
+ * Register values.
+ */
+
+#define WM8350_CURRENT_SINK_DRIVER_A            0xAC
+#define WM8350_CSA_FLASH_CONTROL                0xAD
+#define WM8350_CURRENT_SINK_DRIVER_B            0xAE
+#define WM8350_CSB_FLASH_CONTROL                0xAF
+#define WM8350_DCDC_LDO_REQUESTED               0xB0
+#define WM8350_DCDC_ACTIVE_OPTIONS              0xB1
+#define WM8350_DCDC_SLEEP_OPTIONS               0xB2
+#define WM8350_POWER_CHECK_COMPARATOR           0xB3
+#define WM8350_DCDC1_CONTROL                    0xB4
+#define WM8350_DCDC1_TIMEOUTS                   0xB5
+#define WM8350_DCDC1_LOW_POWER                  0xB6
+#define WM8350_DCDC2_CONTROL                    0xB7
+#define WM8350_DCDC2_TIMEOUTS                   0xB8
+#define WM8350_DCDC3_CONTROL                    0xBA
+#define WM8350_DCDC3_TIMEOUTS                   0xBB
+#define WM8350_DCDC3_LOW_POWER                  0xBC
+#define WM8350_DCDC4_CONTROL                    0xBD
+#define WM8350_DCDC4_TIMEOUTS                   0xBE
+#define WM8350_DCDC4_LOW_POWER                  0xBF
+#define WM8350_DCDC5_CONTROL                    0xC0
+#define WM8350_DCDC5_TIMEOUTS                   0xC1
+#define WM8350_DCDC6_CONTROL                    0xC3
+#define WM8350_DCDC6_TIMEOUTS                   0xC4
+#define WM8350_DCDC6_LOW_POWER                  0xC5
+#define WM8350_LIMIT_SWITCH_CONTROL             0xC7
+#define WM8350_LDO1_CONTROL                     0xC8
+#define WM8350_LDO1_TIMEOUTS                    0xC9
+#define WM8350_LDO1_LOW_POWER                   0xCA
+#define WM8350_LDO2_CONTROL                     0xCB
+#define WM8350_LDO2_TIMEOUTS                    0xCC
+#define WM8350_LDO2_LOW_POWER                   0xCD
+#define WM8350_LDO3_CONTROL                     0xCE
+#define WM8350_LDO3_TIMEOUTS                    0xCF
+#define WM8350_LDO3_LOW_POWER                   0xD0
+#define WM8350_LDO4_CONTROL                     0xD1
+#define WM8350_LDO4_TIMEOUTS                    0xD2
+#define WM8350_LDO4_LOW_POWER                   0xD3
+#define WM8350_VCC_FAULT_MASKS                  0xD7
+#define WM8350_MAIN_BANDGAP_CONTROL             0xD8
+#define WM8350_OSC_CONTROL                      0xD9
+#define WM8350_RTC_TICK_CONTROL                 0xDA
+#define WM8350_SECURITY                         0xDB
+#define WM8350_RAM_BIST_1                       0xDC
+#define WM8350_DCDC_LDO_STATUS                  0xE1
+#define WM8350_GPIO_PIN_STATUS                  0xE6
+
+#define WM8350_DCDC1_FORCE_PWM                  0xF8
+#define WM8350_DCDC3_FORCE_PWM                  0xFA
+#define WM8350_DCDC4_FORCE_PWM                  0xFB
+#define WM8350_DCDC6_FORCE_PWM                  0xFD
+
+/*
+ * R172 (0xAC) - Current Sink Driver A
+ */
+#define WM8350_CS1_HIB_MODE                     0x1000
+#define WM8350_CS1_HIB_MODE_MASK                0x1000
+#define WM8350_CS1_HIB_MODE_SHIFT                   12
+#define WM8350_CS1_ISEL_MASK                    0x003F
+#define WM8350_CS1_ISEL_SHIFT                        0
+
+/* Bit values for R172 (0xAC) */
+#define WM8350_CS1_HIB_MODE_DISABLE                  0
+#define WM8350_CS1_HIB_MODE_LEAVE                    1
+
+#define WM8350_CS1_ISEL_220M                      0x3F
+
+/*
+ * R173 (0xAD) - CSA Flash control
+ */
+#define WM8350_CS1_FLASH_MODE                   0x8000
+#define WM8350_CS1_TRIGSRC                      0x4000
+#define WM8350_CS1_DRIVE                        0x2000
+#define WM8350_CS1_FLASH_DUR_MASK               0x0300
+#define WM8350_CS1_OFF_RAMP_MASK                0x0030
+#define WM8350_CS1_ON_RAMP_MASK                 0x0003
+
+/*
+ * R174 (0xAE) - Current Sink Driver B
+ */
+#define WM8350_CS2_HIB_MODE                     0x1000
+#define WM8350_CS2_ISEL_MASK                    0x003F
+
+/*
+ * R175 (0xAF) - CSB Flash control
+ */
+#define WM8350_CS2_FLASH_MODE                   0x8000
+#define WM8350_CS2_TRIGSRC                      0x4000
+#define WM8350_CS2_DRIVE                        0x2000
+#define WM8350_CS2_FLASH_DUR_MASK               0x0300
+#define WM8350_CS2_OFF_RAMP_MASK                0x0030
+#define WM8350_CS2_ON_RAMP_MASK                 0x0003
+
+/*
+ * R176 (0xB0) - DCDC/LDO requested
+ */
+#define WM8350_LS_ENA                           0x8000
+#define WM8350_LDO4_ENA                         0x0800
+#define WM8350_LDO3_ENA                         0x0400
+#define WM8350_LDO2_ENA                         0x0200
+#define WM8350_LDO1_ENA                         0x0100
+#define WM8350_DC6_ENA                          0x0020
+#define WM8350_DC5_ENA                          0x0010
+#define WM8350_DC4_ENA                          0x0008
+#define WM8350_DC3_ENA                          0x0004
+#define WM8350_DC2_ENA                          0x0002
+#define WM8350_DC1_ENA                          0x0001
+
+/*
+ * R177 (0xB1) - DCDC Active options
+ */
+#define WM8350_PUTO_MASK                        0x3000
+#define WM8350_PWRUP_DELAY_MASK                 0x0300
+#define WM8350_DC6_ACTIVE                       0x0020
+#define WM8350_DC4_ACTIVE                       0x0008
+#define WM8350_DC3_ACTIVE                       0x0004
+#define WM8350_DC1_ACTIVE                       0x0001
+
+/*
+ * R178 (0xB2) - DCDC Sleep options
+ */
+#define WM8350_DC6_SLEEP                        0x0020
+#define WM8350_DC4_SLEEP                        0x0008
+#define WM8350_DC3_SLEEP                        0x0004
+#define WM8350_DC1_SLEEP                        0x0001
+
+/*
+ * R179 (0xB3) - Power-check comparator
+ */
+#define WM8350_PCCMP_ERRACT                     0x4000
+#define WM8350_PCCMP_RAIL                       0x0100
+#define WM8350_PCCMP_OFF_THR_MASK               0x0070
+#define WM8350_PCCMP_ON_THR_MASK                0x0007
+
+/*
+ * R180 (0xB4) - DCDC1 Control
+ */
+#define WM8350_DC1_OPFLT                        0x0400
+#define WM8350_DC1_VSEL_MASK                    0x007F
+#define WM8350_DC1_VSEL_SHIFT                        0
+
+/*
+ * R181 (0xB5) - DCDC1 Timeouts
+ */
+#define WM8350_DC1_ERRACT_MASK                  0xC000
+#define WM8350_DC1_ERRACT_SHIFT                     14
+#define WM8350_DC1_ENSLOT_MASK                  0x3C00
+#define WM8350_DC1_ENSLOT_SHIFT                     10
+#define WM8350_DC1_SDSLOT_MASK                  0x03C0
+#define WM8350_DC1_UVTO_MASK                    0x0030
+#define WM8350_DC1_SDSLOT_SHIFT                      6
+
+/* Bit values for R181 (0xB5) */
+#define WM8350_DC1_ERRACT_NONE                       0
+#define WM8350_DC1_ERRACT_SHUTDOWN_CONV              1
+#define WM8350_DC1_ERRACT_SHUTDOWN_SYS               2
+
+/*
+ * R182 (0xB6) - DCDC1 Low Power
+ */
+#define WM8350_DC1_HIB_MODE_MASK                0x7000
+#define WM8350_DC1_HIB_TRIG_MASK                0x0300
+#define WM8350_DC1_VIMG_MASK                    0x007F
+
+/*
+ * R183 (0xB7) - DCDC2 Control
+ */
+#define WM8350_DC2_MODE                         0x4000
+#define WM8350_DC2_MODE_MASK                    0x4000
+#define WM8350_DC2_MODE_SHIFT                       14
+#define WM8350_DC2_HIB_MODE                     0x1000
+#define WM8350_DC2_HIB_MODE_MASK                0x1000
+#define WM8350_DC2_HIB_MODE_SHIFT                   12
+#define WM8350_DC2_HIB_TRIG_MASK                0x0300
+#define WM8350_DC2_HIB_TRIG_SHIFT                    8
+#define WM8350_DC2_ILIM                         0x0040
+#define WM8350_DC2_ILIM_MASK                    0x0040
+#define WM8350_DC2_ILIM_SHIFT                        6
+#define WM8350_DC2_RMP_MASK                     0x0018
+#define WM8350_DC2_RMP_SHIFT                         3
+#define WM8350_DC2_FBSRC_MASK                   0x0003
+#define WM8350_DC2_FBSRC_SHIFT                       0
+
+/* Bit values for R183 (0xB7) */
+#define WM8350_DC2_MODE_BOOST                        0
+#define WM8350_DC2_MODE_SWITCH                       1
+
+#define WM8350_DC2_HIB_MODE_ACTIVE                   1
+#define WM8350_DC2_HIB_MODE_DISABLE                  0
+
+#define WM8350_DC2_HIB_TRIG_NONE                     0
+#define WM8350_DC2_HIB_TRIG_LPWR1                    1
+#define WM8350_DC2_HIB_TRIG_LPWR2                    2
+#define WM8350_DC2_HIB_TRIG_LPWR3                    3
+
+#define WM8350_DC2_ILIM_HIGH                         0
+#define WM8350_DC2_ILIM_LOW                          1
+
+#define WM8350_DC2_RMP_30V                           0
+#define WM8350_DC2_RMP_20V                           1
+#define WM8350_DC2_RMP_10V                           2
+#define WM8350_DC2_RMP_5V                            3
+
+#define WM8350_DC2_FBSRC_FB2                         0
+#define WM8350_DC2_FBSRC_ISINKA                      1
+#define WM8350_DC2_FBSRC_ISINKB                      2
+#define WM8350_DC2_FBSRC_USB                         3
+
+/*
+ * R184 (0xB8) - DCDC2 Timeouts
+ */
+#define WM8350_DC2_ERRACT_MASK                  0xC000
+#define WM8350_DC2_ERRACT_SHIFT                     14
+#define WM8350_DC2_ENSLOT_MASK                  0x3C00
+#define WM8350_DC2_ENSLOT_SHIFT                     10
+#define WM8350_DC2_SDSLOT_MASK                  0x03C0
+#define WM8350_DC2_UVTO_MASK                    0x0030
+
+/* Bit values for R184 (0xB8) */
+#define WM8350_DC2_ERRACT_NONE                       0
+#define WM8350_DC2_ERRACT_SHUTDOWN_CONV              1
+#define WM8350_DC2_ERRACT_SHUTDOWN_SYS               2
+
+/*
+ * R186 (0xBA) - DCDC3 Control
+ */
+#define WM8350_DC3_OPFLT                        0x0400
+#define WM8350_DC3_VSEL_MASK                    0x007F
+#define WM8350_DC3_VSEL_SHIFT                        0
+
+/*
+ * R187 (0xBB) - DCDC3 Timeouts
+ */
+#define WM8350_DC3_ERRACT_MASK                  0xC000
+#define WM8350_DC3_ERRACT_SHIFT                     14
+#define WM8350_DC3_ENSLOT_MASK                  0x3C00
+#define WM8350_DC3_ENSLOT_SHIFT                     10
+#define WM8350_DC3_SDSLOT_MASK                  0x03C0
+#define WM8350_DC3_UVTO_MASK                    0x0030
+#define WM8350_DC3_SDSLOT_SHIFT                      6
+
+/* Bit values for R187 (0xBB) */
+#define WM8350_DC3_ERRACT_NONE                       0
+#define WM8350_DC3_ERRACT_SHUTDOWN_CONV              1
+#define WM8350_DC3_ERRACT_SHUTDOWN_SYS               2
+/*
+ * R188 (0xBC) - DCDC3 Low Power
+ */
+#define WM8350_DC3_HIB_MODE_MASK                0x7000
+#define WM8350_DC3_HIB_TRIG_MASK                0x0300
+#define WM8350_DC3_VIMG_MASK                    0x007F
+
+/*
+ * R189 (0xBD) - DCDC4 Control
+ */
+#define WM8350_DC4_OPFLT                        0x0400
+#define WM8350_DC4_VSEL_MASK                    0x007F
+#define WM8350_DC4_VSEL_SHIFT                        0
+
+/*
+ * R190 (0xBE) - DCDC4 Timeouts
+ */
+#define WM8350_DC4_ERRACT_MASK                  0xC000
+#define WM8350_DC4_ERRACT_SHIFT                     14
+#define WM8350_DC4_ENSLOT_MASK                  0x3C00
+#define WM8350_DC4_ENSLOT_SHIFT                     10
+#define WM8350_DC4_SDSLOT_MASK                  0x03C0
+#define WM8350_DC4_UVTO_MASK                    0x0030
+#define WM8350_DC4_SDSLOT_SHIFT                      6
+
+/* Bit values for R190 (0xBE) */
+#define WM8350_DC4_ERRACT_NONE                       0
+#define WM8350_DC4_ERRACT_SHUTDOWN_CONV              1
+#define WM8350_DC4_ERRACT_SHUTDOWN_SYS               2
+
+/*
+ * R191 (0xBF) - DCDC4 Low Power
+ */
+#define WM8350_DC4_HIB_MODE_MASK                0x7000
+#define WM8350_DC4_HIB_TRIG_MASK                0x0300
+#define WM8350_DC4_VIMG_MASK                    0x007F
+
+/*
+ * R192 (0xC0) - DCDC5 Control
+ */
+#define WM8350_DC5_MODE                         0x4000
+#define WM8350_DC5_MODE_MASK                    0x4000
+#define WM8350_DC5_MODE_SHIFT                       14
+#define WM8350_DC5_HIB_MODE                     0x1000
+#define WM8350_DC5_HIB_MODE_MASK                0x1000
+#define WM8350_DC5_HIB_MODE_SHIFT                   12
+#define WM8350_DC5_HIB_TRIG_MASK                0x0300
+#define WM8350_DC5_HIB_TRIG_SHIFT                    8
+#define WM8350_DC5_ILIM                         0x0040
+#define WM8350_DC5_ILIM_MASK                    0x0040
+#define WM8350_DC5_ILIM_SHIFT                        6
+#define WM8350_DC5_RMP_MASK                     0x0018
+#define WM8350_DC5_RMP_SHIFT                         3
+#define WM8350_DC5_FBSRC_MASK                   0x0003
+#define WM8350_DC5_FBSRC_SHIFT                       0
+
+/* Bit values for R192 (0xC0) */
+#define WM8350_DC5_MODE_BOOST                        0
+#define WM8350_DC5_MODE_SWITCH                       1
+
+#define WM8350_DC5_HIB_MODE_ACTIVE                   1
+#define WM8350_DC5_HIB_MODE_DISABLE                  0
+
+#define WM8350_DC5_HIB_TRIG_NONE                     0
+#define WM8350_DC5_HIB_TRIG_LPWR1                    1
+#define WM8350_DC5_HIB_TRIG_LPWR2                    2
+#define WM8350_DC5_HIB_TRIG_LPWR3                    3
+
+#define WM8350_DC5_ILIM_HIGH                         0
+#define WM8350_DC5_ILIM_LOW                          1
+
+#define WM8350_DC5_RMP_30V                           0
+#define WM8350_DC5_RMP_20V                           1
+#define WM8350_DC5_RMP_10V                           2
+#define WM8350_DC5_RMP_5V                            3
+
+#define WM8350_DC5_FBSRC_FB2                         0
+#define WM8350_DC5_FBSRC_ISINKA                      1
+#define WM8350_DC5_FBSRC_ISINKB                      2
+#define WM8350_DC5_FBSRC_USB                         3
+
+/*
+ * R193 (0xC1) - DCDC5 Timeouts
+ */
+#define WM8350_DC5_ERRACT_MASK                  0xC000
+#define WM8350_DC5_ERRACT_SHIFT                     14
+#define WM8350_DC5_ENSLOT_MASK                  0x3C00
+#define WM8350_DC5_ENSLOT_SHIFT                     10
+#define WM8350_DC5_SDSLOT_MASK                  0x03C0
+#define WM8350_DC5_UVTO_MASK                    0x0030
+#define WM8350_DC5_SDSLOT_SHIFT                      6
+
+/* Bit values for R193 (0xC1) */
+#define WM8350_DC5_ERRACT_NONE                       0
+#define WM8350_DC5_ERRACT_SHUTDOWN_CONV              1
+#define WM8350_DC5_ERRACT_SHUTDOWN_SYS               2
+
+/*
+ * R195 (0xC3) - DCDC6 Control
+ */
+#define WM8350_DC6_OPFLT                        0x0400
+#define WM8350_DC6_VSEL_MASK                    0x007F
+#define WM8350_DC6_VSEL_SHIFT                        0
+
+/*
+ * R196 (0xC4) - DCDC6 Timeouts
+ */
+#define WM8350_DC6_ERRACT_MASK                  0xC000
+#define WM8350_DC6_ERRACT_SHIFT                     14
+#define WM8350_DC6_ENSLOT_MASK                  0x3C00
+#define WM8350_DC6_ENSLOT_SHIFT                     10
+#define WM8350_DC6_SDSLOT_MASK                  0x03C0
+#define WM8350_DC6_UVTO_MASK                    0x0030
+#define WM8350_DC6_SDSLOT_SHIFT                      6
+
+/* Bit values for R196 (0xC4) */
+#define WM8350_DC6_ERRACT_NONE                       0
+#define WM8350_DC6_ERRACT_SHUTDOWN_CONV              1
+#define WM8350_DC6_ERRACT_SHUTDOWN_SYS               2
+
+/*
+ * R197 (0xC5) - DCDC6 Low Power
+ */
+#define WM8350_DC6_HIB_MODE_MASK                0x7000
+#define WM8350_DC6_HIB_TRIG_MASK                0x0300
+#define WM8350_DC6_VIMG_MASK                    0x007F
+
+/*
+ * R199 (0xC7) - Limit Switch Control
+ */
+#define WM8350_LS_ERRACT_MASK                   0xC000
+#define WM8350_LS_ERRACT_SHIFT                      14
+#define WM8350_LS_ENSLOT_MASK                   0x3C00
+#define WM8350_LS_ENSLOT_SHIFT                      10
+#define WM8350_LS_SDSLOT_MASK                   0x03C0
+#define WM8350_LS_SDSLOT_SHIFT                       6
+#define WM8350_LS_HIB_MODE                      0x0010
+#define WM8350_LS_HIB_MODE_MASK                 0x0010
+#define WM8350_LS_HIB_MODE_SHIFT                     4
+#define WM8350_LS_HIB_PROT                      0x0002
+#define WM8350_LS_HIB_PROT_MASK                 0x0002
+#define WM8350_LS_HIB_PROT_SHIFT                     1
+#define WM8350_LS_PROT                          0x0001
+#define WM8350_LS_PROT_MASK                     0x0001
+#define WM8350_LS_PROT_SHIFT                         0
+
+/* Bit values for R199 (0xC7) */
+#define WM8350_LS_ERRACT_NONE                       0
+#define WM8350_LS_ERRACT_SHUTDOWN_CONV              1
+#define WM8350_LS_ERRACT_SHUTDOWN_SYS               2
+
+/*
+ * R200 (0xC8) - LDO1 Control
+ */
+#define WM8350_LDO1_SWI                         0x4000
+#define WM8350_LDO1_OPFLT                       0x0400
+#define WM8350_LDO1_VSEL_MASK                   0x001F
+#define WM8350_LDO1_VSEL_SHIFT                       0
+
+/*
+ * R201 (0xC9) - LDO1 Timeouts
+ */
+#define WM8350_LDO1_ERRACT_MASK                 0xC000
+#define WM8350_LDO1_ERRACT_SHIFT                    14
+#define WM8350_LDO1_ENSLOT_MASK                 0x3C00
+#define WM8350_LDO1_ENSLOT_SHIFT                    10
+#define WM8350_LDO1_SDSLOT_MASK                 0x03C0
+#define WM8350_LDO1_UVTO_MASK                   0x0030
+#define WM8350_LDO1_SDSLOT_SHIFT                     6
+
+/* Bit values for R201 (0xC9) */
+#define WM8350_LDO1_ERRACT_NONE                       0
+#define WM8350_LDO1_ERRACT_SHUTDOWN_CONV              1
+#define WM8350_LDO1_ERRACT_SHUTDOWN_SYS               2
+
+/*
+ * R202 (0xCA) - LDO1 Low Power
+ */
+#define WM8350_LDO1_HIB_MODE_MASK               0x3000
+#define WM8350_LDO1_HIB_TRIG_MASK               0x0300
+#define WM8350_LDO1_VIMG_MASK                   0x001F
+#define WM8350_LDO1_HIB_MODE_DIS               (0x1 << 12)
+
+
+/*
+ * R203 (0xCB) - LDO2 Control
+ */
+#define WM8350_LDO2_SWI                         0x4000
+#define WM8350_LDO2_OPFLT                       0x0400
+#define WM8350_LDO2_VSEL_MASK                   0x001F
+#define WM8350_LDO2_VSEL_SHIFT                       0
+
+/*
+ * R204 (0xCC) - LDO2 Timeouts
+ */
+#define WM8350_LDO2_ERRACT_MASK                 0xC000
+#define WM8350_LDO2_ERRACT_SHIFT                    14
+#define WM8350_LDO2_ENSLOT_MASK                 0x3C00
+#define WM8350_LDO2_ENSLOT_SHIFT                    10
+#define WM8350_LDO2_SDSLOT_MASK                 0x03C0
+#define WM8350_LDO2_SDSLOT_SHIFT                     6
+
+/* Bit values for R204 (0xCC) */
+#define WM8350_LDO2_ERRACT_NONE                       0
+#define WM8350_LDO2_ERRACT_SHUTDOWN_CONV              1
+#define WM8350_LDO2_ERRACT_SHUTDOWN_SYS               2
+
+/*
+ * R205 (0xCD) - LDO2 Low Power
+ */
+#define WM8350_LDO2_HIB_MODE_MASK               0x3000
+#define WM8350_LDO2_HIB_TRIG_MASK               0x0300
+#define WM8350_LDO2_VIMG_MASK                   0x001F
+
+/*
+ * R206 (0xCE) - LDO3 Control
+ */
+#define WM8350_LDO3_SWI                         0x4000
+#define WM8350_LDO3_OPFLT                       0x0400
+#define WM8350_LDO3_VSEL_MASK                   0x001F
+#define WM8350_LDO3_VSEL_SHIFT                       0
+
+/*
+ * R207 (0xCF) - LDO3 Timeouts
+ */
+#define WM8350_LDO3_ERRACT_MASK                 0xC000
+#define WM8350_LDO3_ERRACT_SHIFT                    14
+#define WM8350_LDO3_ENSLOT_MASK                 0x3C00
+#define WM8350_LDO3_ENSLOT_SHIFT                    10
+#define WM8350_LDO3_SDSLOT_MASK                 0x03C0
+#define WM8350_LDO3_UVTO_MASK                   0x0030
+#define WM8350_LDO3_SDSLOT_SHIFT                     6
+
+/* Bit values for R207 (0xCF) */
+#define WM8350_LDO3_ERRACT_NONE                       0
+#define WM8350_LDO3_ERRACT_SHUTDOWN_CONV              1
+#define WM8350_LDO3_ERRACT_SHUTDOWN_SYS               2
+
+/*
+ * R208 (0xD0) - LDO3 Low Power
+ */
+#define WM8350_LDO3_HIB_MODE_MASK               0x3000
+#define WM8350_LDO3_HIB_TRIG_MASK               0x0300
+#define WM8350_LDO3_VIMG_MASK                   0x001F
+
+/*
+ * R209 (0xD1) - LDO4 Control
+ */
+#define WM8350_LDO4_SWI                         0x4000
+#define WM8350_LDO4_OPFLT                       0x0400
+#define WM8350_LDO4_VSEL_MASK                   0x001F
+#define WM8350_LDO4_VSEL_SHIFT                       0
+
+/*
+ * R210 (0xD2) - LDO4 Timeouts
+ */
+#define WM8350_LDO4_ERRACT_MASK                 0xC000
+#define WM8350_LDO4_ERRACT_SHIFT                    14
+#define WM8350_LDO4_ENSLOT_MASK                 0x3C00
+#define WM8350_LDO4_ENSLOT_SHIFT                    10
+#define WM8350_LDO4_SDSLOT_MASK                 0x03C0
+#define WM8350_LDO4_UVTO_MASK                   0x0030
+#define WM8350_LDO4_SDSLOT_SHIFT                     6
+
+/* Bit values for R210 (0xD2) */
+#define WM8350_LDO4_ERRACT_NONE                       0
+#define WM8350_LDO4_ERRACT_SHUTDOWN_CONV              1
+#define WM8350_LDO4_ERRACT_SHUTDOWN_SYS               2
+
+/*
+ * R211 (0xD3) - LDO4 Low Power
+ */
+#define WM8350_LDO4_HIB_MODE_MASK               0x3000
+#define WM8350_LDO4_HIB_TRIG_MASK               0x0300
+#define WM8350_LDO4_VIMG_MASK                   0x001F
+
+/*
+ * R215 (0xD7) - VCC_FAULT Masks
+ */
+#define WM8350_LS_FAULT                         0x8000
+#define WM8350_LDO4_FAULT                       0x0800
+#define WM8350_LDO3_FAULT                       0x0400
+#define WM8350_LDO2_FAULT                       0x0200
+#define WM8350_LDO1_FAULT                       0x0100
+#define WM8350_DC6_FAULT                        0x0020
+#define WM8350_DC5_FAULT                        0x0010
+#define WM8350_DC4_FAULT                        0x0008
+#define WM8350_DC3_FAULT                        0x0004
+#define WM8350_DC2_FAULT                        0x0002
+#define WM8350_DC1_FAULT                        0x0001
+
+/*
+ * R216 (0xD8) - Main Bandgap Control
+ */
+#define WM8350_MBG_LOAD_FUSES                   0x8000
+#define WM8350_MBG_FUSE_WPREP                   0x4000
+#define WM8350_MBG_FUSE_WRITE                   0x2000
+#define WM8350_MBG_FUSE_TRIM_MASK               0x1F00
+#define WM8350_MBG_TRIM_SRC                     0x0020
+#define WM8350_MBG_USER_TRIM_MASK               0x001F
+
+/*
+ * R217 (0xD9) - OSC Control
+ */
+#define WM8350_OSC_LOAD_FUSES                   0x8000
+#define WM8350_OSC_FUSE_WPREP                   0x4000
+#define WM8350_OSC_FUSE_WRITE                   0x2000
+#define WM8350_OSC_FUSE_TRIM_MASK               0x0F00
+#define WM8350_OSC_TRIM_SRC                     0x0020
+#define WM8350_OSC_USER_TRIM_MASK               0x000F
+
+/*
+ * R248 (0xF8) - DCDC1 Force PWM
+ */
+#define WM8350_DCDC1_FORCE_PWM_ENA              0x0010
+
+/*
+ * R250 (0xFA) - DCDC3 Force PWM
+ */
+#define WM8350_DCDC3_FORCE_PWM_ENA              0x0010
+
+/*
+ * R251 (0xFB) - DCDC4 Force PWM
+ */
+#define WM8350_DCDC4_FORCE_PWM_ENA              0x0010
+
+/*
+ * R253 (0xFD) - DCDC1 Force PWM
+ */
+#define WM8350_DCDC6_FORCE_PWM_ENA              0x0010
+
+/*
+ * DCDC's
+ */
+#define WM8350_DCDC_1                          0
+#define WM8350_DCDC_2                          1
+#define WM8350_DCDC_3                          2
+#define WM8350_DCDC_4                          3
+#define WM8350_DCDC_5                          4
+#define WM8350_DCDC_6                          5
+
+/* DCDC modes */
+#define WM8350_DCDC_ACTIVE_STANDBY             0
+#define WM8350_DCDC_ACTIVE_PULSE               1
+#define WM8350_DCDC_SLEEP_NORMAL               0
+#define WM8350_DCDC_SLEEP_LOW                  1
+
+/* DCDC Low power (Hibernate) mode */
+#define WM8350_DCDC_HIB_MODE_CUR               (0 << 12)
+#define WM8350_DCDC_HIB_MODE_IMAGE             (1 << 12)
+#define WM8350_DCDC_HIB_MODE_STANDBY           (2 << 12)
+#define WM8350_DCDC_HIB_MODE_LDO               (4 << 12)
+#define WM8350_DCDC_HIB_MODE_LDO_IM            (5 << 12)
+#define WM8350_DCDC_HIB_MODE_DIS               (7 << 12)
+#define WM8350_DCDC_HIB_MODE_MASK              (7 << 12)
+
+/* DCDC Low Power (Hibernate) signal */
+#define WM8350_DCDC_HIB_SIG_REG                        (0 << 8)
+#define WM8350_DCDC_HIB_SIG_LPWR1              (1 << 8)
+#define WM8350_DCDC_HIB_SIG_LPWR2              (2 << 8)
+#define WM8350_DCDC_HIB_SIG_LPWR3              (3 << 8)
+
+/* LDO Low power (Hibernate) mode */
+#define WM8350_LDO_HIB_MODE_IMAGE              (0 << 0)
+#define WM8350_LDO_HIB_MODE_DIS                        (1 << 0)
+
+/* LDO Low Power (Hibernate) signal */
+#define WM8350_LDO_HIB_SIG_REG                 (0 << 8)
+#define WM8350_LDO_HIB_SIG_LPWR1               (1 << 8)
+#define WM8350_LDO_HIB_SIG_LPWR2               (2 << 8)
+#define WM8350_LDO_HIB_SIG_LPWR3               (3 << 8)
+
+/*
+ * LDOs
+ */
+#define WM8350_LDO_1                           6
+#define WM8350_LDO_2                           7
+#define WM8350_LDO_3                           8
+#define WM8350_LDO_4                           9
+
+/*
+ * ISINKs
+ */
+#define WM8350_ISINK_A                         10
+#define WM8350_ISINK_B                         11
+
+#define WM8350_ISINK_MODE_BOOST                        0
+#define WM8350_ISINK_MODE_SWITCH               1
+#define WM8350_ISINK_ILIM_NORMAL               0
+#define WM8350_ISINK_ILIM_LOW                  1
+
+#define WM8350_ISINK_FLASH_DISABLE             0
+#define WM8350_ISINK_FLASH_ENABLE              1
+#define WM8350_ISINK_FLASH_TRIG_BIT            0
+#define WM8350_ISINK_FLASH_TRIG_GPIO           1
+#define WM8350_ISINK_FLASH_MODE_EN             (1 << 13)
+#define WM8350_ISINK_FLASH_MODE_DIS            (0 << 13)
+#define WM8350_ISINK_FLASH_DUR_32MS            (0 << 8)
+#define WM8350_ISINK_FLASH_DUR_64MS            (1 << 8)
+#define WM8350_ISINK_FLASH_DUR_96MS            (2 << 8)
+#define WM8350_ISINK_FLASH_DUR_1024MS          (3 << 8)
+#define WM8350_ISINK_FLASH_ON_INSTANT          (0 << 4)
+#define WM8350_ISINK_FLASH_ON_0_25S            (1 << 4)
+#define WM8350_ISINK_FLASH_ON_0_50S            (2 << 4)
+#define WM8350_ISINK_FLASH_ON_1_00S            (3 << 4)
+#define WM8350_ISINK_FLASH_ON_1_95S            (1 << 4)
+#define WM8350_ISINK_FLASH_ON_3_91S            (2 << 4)
+#define WM8350_ISINK_FLASH_ON_7_80S            (3 << 4)
+#define WM8350_ISINK_FLASH_OFF_INSTANT         (0 << 0)
+#define WM8350_ISINK_FLASH_OFF_0_25S           (1 << 0)
+#define WM8350_ISINK_FLASH_OFF_0_50S           (2 << 0)
+#define WM8350_ISINK_FLASH_OFF_1_00S           (3 << 0)
+#define WM8350_ISINK_FLASH_OFF_1_95S           (1 << 0)
+#define WM8350_ISINK_FLASH_OFF_3_91S           (2 << 0)
+#define WM8350_ISINK_FLASH_OFF_7_80S           (3 << 0)
+
+/*
+ * Regulator Interrupts.
+ */
+#define WM8350_IRQ_CS1                         13
+#define WM8350_IRQ_CS2                         14
+#define WM8350_IRQ_UV_LDO4                     25
+#define WM8350_IRQ_UV_LDO3                     26
+#define WM8350_IRQ_UV_LDO2                     27
+#define WM8350_IRQ_UV_LDO1                     28
+#define WM8350_IRQ_UV_DC6                      29
+#define WM8350_IRQ_UV_DC5                      30
+#define WM8350_IRQ_UV_DC4                      31
+#define WM8350_IRQ_UV_DC3                      32
+#define WM8350_IRQ_UV_DC2                      33
+#define WM8350_IRQ_UV_DC1                      34
+#define WM8350_IRQ_OC_LS                       35
+
+#define NUM_WM8350_REGULATORS                  12
+
+struct wm8350;
+struct platform_device;
+struct regulator_init_data;
+
+struct wm8350_pmic {
+       /* ISINK to DCDC mapping */
+       int isink_A_dcdc;
+       int isink_B_dcdc;
+
+       /* hibernate configs */
+       u16 dcdc1_hib_mode;
+       u16 dcdc3_hib_mode;
+       u16 dcdc4_hib_mode;
+       u16 dcdc6_hib_mode;
+
+       /* regulator devices */
+       struct platform_device *pdev[NUM_WM8350_REGULATORS];
+};
+
+int wm8350_register_regulator(struct wm8350 *wm8350, int reg,
+                             struct regulator_init_data *initdata);
+
+/*
+ * Additional DCDC control not supported via regulator API
+ */
+int wm8350_dcdc_set_slot(struct wm8350 *wm8350, int dcdc, u16 start,
+                        u16 stop, u16 fault);
+int wm8350_dcdc25_set_mode(struct wm8350 *wm8350, int dcdc, u16 mode,
+                          u16 ilim, u16 ramp, u16 feedback);
+
+/*
+ * Additional LDO control not supported via regulator API
+ */
+int wm8350_ldo_set_slot(struct wm8350 *wm8350, int ldo, u16 start, u16 stop);
+
+/*
+ * Additional ISINK control not supported via regulator API
+ */
+int wm8350_isink_set_flash(struct wm8350 *wm8350, int isink, u16 mode,
+                          u16 trigger, u16 duration, u16 on_ramp,
+                          u16 off_ramp, u16 drive);
+
+#endif
diff --git a/include/linux/mfd/wm8350/rtc.h b/include/linux/mfd/wm8350/rtc.h
new file mode 100644 (file)
index 0000000..dfda69e
--- /dev/null
@@ -0,0 +1,266 @@
+/*
+ * rtc.h  --  RTC driver for Wolfson WM8350 PMIC
+ *
+ * Copyright 2007 Wolfson Microelectronics PLC
+ *
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ */
+
+#ifndef __LINUX_MFD_WM8350_RTC_H
+#define __LINUX_MFD_WM8350_RTC_H
+
+#include <linux/platform_device.h>
+
+/*
+ * Register values.
+ */
+#define WM8350_RTC_SECONDS_MINUTES              0x10
+#define WM8350_RTC_HOURS_DAY                    0x11
+#define WM8350_RTC_DATE_MONTH                   0x12
+#define WM8350_RTC_YEAR                         0x13
+#define WM8350_ALARM_SECONDS_MINUTES            0x14
+#define WM8350_ALARM_HOURS_DAY                  0x15
+#define WM8350_ALARM_DATE_MONTH                 0x16
+#define WM8350_RTC_TIME_CONTROL                 0x17
+
+/*
+ * R16 (0x10) - RTC Seconds/Minutes
+ */
+#define WM8350_RTC_MINS_MASK                    0x7F00
+#define WM8350_RTC_MINS_SHIFT                        8
+#define WM8350_RTC_SECS_MASK                    0x007F
+#define WM8350_RTC_SECS_SHIFT                        0
+
+/*
+ * R17 (0x11) - RTC Hours/Day
+ */
+#define WM8350_RTC_DAY_MASK                     0x0700
+#define WM8350_RTC_DAY_SHIFT                         8
+#define WM8350_RTC_HPM_MASK                     0x0020
+#define WM8350_RTC_HPM_SHIFT                         5
+#define WM8350_RTC_HRS_MASK                     0x001F
+#define WM8350_RTC_HRS_SHIFT                         0
+
+/* Bit values for R21 (0x15) */
+#define WM8350_RTC_DAY_SUN                           1
+#define WM8350_RTC_DAY_MON                           2
+#define WM8350_RTC_DAY_TUE                           3
+#define WM8350_RTC_DAY_WED                           4
+#define WM8350_RTC_DAY_THU                           5
+#define WM8350_RTC_DAY_FRI                           6
+#define WM8350_RTC_DAY_SAT                           7
+
+#define WM8350_RTC_HPM_AM                            0
+#define WM8350_RTC_HPM_PM                            1
+
+/*
+ * R18 (0x12) - RTC Date/Month
+ */
+#define WM8350_RTC_MTH_MASK                     0x1F00
+#define WM8350_RTC_MTH_SHIFT                         8
+#define WM8350_RTC_DATE_MASK                    0x003F
+#define WM8350_RTC_DATE_SHIFT                        0
+
+/* Bit values for R22 (0x16) */
+#define WM8350_RTC_MTH_JAN                           1
+#define WM8350_RTC_MTH_FEB                           2
+#define WM8350_RTC_MTH_MAR                           3
+#define WM8350_RTC_MTH_APR                           4
+#define WM8350_RTC_MTH_MAY                           5
+#define WM8350_RTC_MTH_JUN                           6
+#define WM8350_RTC_MTH_JUL                           7
+#define WM8350_RTC_MTH_AUG                           8
+#define WM8350_RTC_MTH_SEP                           9
+#define WM8350_RTC_MTH_OCT                          10
+#define WM8350_RTC_MTH_NOV                          11
+#define WM8350_RTC_MTH_DEC                          12
+#define WM8350_RTC_MTH_JAN_BCD                    0x01
+#define WM8350_RTC_MTH_FEB_BCD                    0x02
+#define WM8350_RTC_MTH_MAR_BCD                    0x03
+#define WM8350_RTC_MTH_APR_BCD                    0x04
+#define WM8350_RTC_MTH_MAY_BCD                    0x05
+#define WM8350_RTC_MTH_JUN_BCD                    0x06
+#define WM8350_RTC_MTH_JUL_BCD                    0x07
+#define WM8350_RTC_MTH_AUG_BCD                    0x08
+#define WM8350_RTC_MTH_SEP_BCD                    0x09
+#define WM8350_RTC_MTH_OCT_BCD                    0x10
+#define WM8350_RTC_MTH_NOV_BCD                    0x11
+#define WM8350_RTC_MTH_DEC_BCD                    0x12
+
+/*
+ * R19 (0x13) - RTC Year
+ */
+#define WM8350_RTC_YHUNDREDS_MASK               0x3F00
+#define WM8350_RTC_YHUNDREDS_SHIFT                   8
+#define WM8350_RTC_YUNITS_MASK                  0x00FF
+#define WM8350_RTC_YUNITS_SHIFT                      0
+
+/*
+ * R20 (0x14) - Alarm Seconds/Minutes
+ */
+#define WM8350_RTC_ALMMINS_MASK                 0x7F00
+#define WM8350_RTC_ALMMINS_SHIFT                     8
+#define WM8350_RTC_ALMSECS_MASK                 0x007F
+#define WM8350_RTC_ALMSECS_SHIFT                     0
+
+/* Bit values for R20 (0x14) */
+#define WM8350_RTC_ALMMINS_DONT_CARE                -1
+#define WM8350_RTC_ALMSECS_DONT_CARE                -1
+
+/*
+ * R21 (0x15) - Alarm Hours/Day
+ */
+#define WM8350_RTC_ALMDAY_MASK                  0x0F00
+#define WM8350_RTC_ALMDAY_SHIFT                      8
+#define WM8350_RTC_ALMHPM_MASK                  0x0020
+#define WM8350_RTC_ALMHPM_SHIFT                      5
+#define WM8350_RTC_ALMHRS_MASK                  0x001F
+#define WM8350_RTC_ALMHRS_SHIFT                      0
+
+/* Bit values for R21 (0x15) */
+#define WM8350_RTC_ALMDAY_DONT_CARE                 -1
+#define WM8350_RTC_ALMDAY_SUN                        1
+#define WM8350_RTC_ALMDAY_MON                        2
+#define WM8350_RTC_ALMDAY_TUE                        3
+#define WM8350_RTC_ALMDAY_WED                        4
+#define WM8350_RTC_ALMDAY_THU                        5
+#define WM8350_RTC_ALMDAY_FRI                        6
+#define WM8350_RTC_ALMDAY_SAT                        7
+
+#define WM8350_RTC_ALMHPM_AM                         0
+#define WM8350_RTC_ALMHPM_PM                         1
+
+#define WM8350_RTC_ALMHRS_DONT_CARE                 -1
+
+/*
+ * R22 (0x16) - Alarm Date/Month
+ */
+#define WM8350_RTC_ALMMTH_MASK                  0x1F00
+#define WM8350_RTC_ALMMTH_SHIFT                      8
+#define WM8350_RTC_ALMDATE_MASK                 0x003F
+#define WM8350_RTC_ALMDATE_SHIFT                     0
+
+/* Bit values for R22 (0x16) */
+#define WM8350_RTC_ALMDATE_DONT_CARE                -1
+
+#define WM8350_RTC_ALMMTH_DONT_CARE                 -1
+#define WM8350_RTC_ALMMTH_JAN                        1
+#define WM8350_RTC_ALMMTH_FEB                        2
+#define WM8350_RTC_ALMMTH_MAR                        3
+#define WM8350_RTC_ALMMTH_APR                        4
+#define WM8350_RTC_ALMMTH_MAY                        5
+#define WM8350_RTC_ALMMTH_JUN                        6
+#define WM8350_RTC_ALMMTH_JUL                        7
+#define WM8350_RTC_ALMMTH_AUG                        8
+#define WM8350_RTC_ALMMTH_SEP                        9
+#define WM8350_RTC_ALMMTH_OCT                       10
+#define WM8350_RTC_ALMMTH_NOV                       11
+#define WM8350_RTC_ALMMTH_DEC                       12
+#define WM8350_RTC_ALMMTH_JAN_BCD                 0x01
+#define WM8350_RTC_ALMMTH_FEB_BCD                 0x02
+#define WM8350_RTC_ALMMTH_MAR_BCD                 0x03
+#define WM8350_RTC_ALMMTH_APR_BCD                 0x04
+#define WM8350_RTC_ALMMTH_MAY_BCD                 0x05
+#define WM8350_RTC_ALMMTH_JUN_BCD                 0x06
+#define WM8350_RTC_ALMMTH_JUL_BCD                 0x07
+#define WM8350_RTC_ALMMTH_AUG_BCD                 0x08
+#define WM8350_RTC_ALMMTH_SEP_BCD                 0x09
+#define WM8350_RTC_ALMMTH_OCT_BCD                 0x10
+#define WM8350_RTC_ALMMTH_NOV_BCD                 0x11
+#define WM8350_RTC_ALMMTH_DEC_BCD                 0x12
+
+/*
+ * R23 (0x17) - RTC Time Control
+ */
+#define WM8350_RTC_BCD                          0x8000
+#define WM8350_RTC_BCD_MASK                     0x8000
+#define WM8350_RTC_BCD_SHIFT                        15
+#define WM8350_RTC_12HR                         0x4000
+#define WM8350_RTC_12HR_MASK                    0x4000
+#define WM8350_RTC_12HR_SHIFT                       14
+#define WM8350_RTC_DST                          0x2000
+#define WM8350_RTC_DST_MASK                     0x2000
+#define WM8350_RTC_DST_SHIFT                        13
+#define WM8350_RTC_SET                          0x0800
+#define WM8350_RTC_SET_MASK                     0x0800
+#define WM8350_RTC_SET_SHIFT                        11
+#define WM8350_RTC_STS                          0x0400
+#define WM8350_RTC_STS_MASK                     0x0400
+#define WM8350_RTC_STS_SHIFT                        10
+#define WM8350_RTC_ALMSET                       0x0200
+#define WM8350_RTC_ALMSET_MASK                  0x0200
+#define WM8350_RTC_ALMSET_SHIFT                      9
+#define WM8350_RTC_ALMSTS                       0x0100
+#define WM8350_RTC_ALMSTS_MASK                  0x0100
+#define WM8350_RTC_ALMSTS_SHIFT                      8
+#define WM8350_RTC_PINT                         0x0070
+#define WM8350_RTC_PINT_MASK                    0x0070
+#define WM8350_RTC_PINT_SHIFT                        4
+#define WM8350_RTC_DSW                          0x000F
+#define WM8350_RTC_DSW_MASK                     0x000F
+#define WM8350_RTC_DSW_SHIFT                         0
+
+/* Bit values for R23 (0x17) */
+#define WM8350_RTC_BCD_BINARY                        0
+#define WM8350_RTC_BCD_BCD                           1
+
+#define WM8350_RTC_12HR_24HR                         0
+#define WM8350_RTC_12HR_12HR                         1
+
+#define WM8350_RTC_DST_DISABLED                      0
+#define WM8350_RTC_DST_ENABLED                       1
+
+#define WM8350_RTC_SET_RUN                           0
+#define WM8350_RTC_SET_SET                           1
+
+#define WM8350_RTC_STS_RUNNING                       0
+#define WM8350_RTC_STS_STOPPED                       1
+
+#define WM8350_RTC_ALMSET_RUN                        0
+#define WM8350_RTC_ALMSET_SET                        1
+
+#define WM8350_RTC_ALMSTS_RUNNING                    0
+#define WM8350_RTC_ALMSTS_STOPPED                    1
+
+#define WM8350_RTC_PINT_DISABLED                     0
+#define WM8350_RTC_PINT_SECS                         1
+#define WM8350_RTC_PINT_MINS                         2
+#define WM8350_RTC_PINT_HRS                          3
+#define WM8350_RTC_PINT_DAYS                         4
+#define WM8350_RTC_PINT_MTHS                         5
+
+#define WM8350_RTC_DSW_DISABLED                      0
+#define WM8350_RTC_DSW_1HZ                           1
+#define WM8350_RTC_DSW_2HZ                           2
+#define WM8350_RTC_DSW_4HZ                           3
+#define WM8350_RTC_DSW_8HZ                           4
+#define WM8350_RTC_DSW_16HZ                          5
+#define WM8350_RTC_DSW_32HZ                          6
+#define WM8350_RTC_DSW_64HZ                          7
+#define WM8350_RTC_DSW_128HZ                         8
+#define WM8350_RTC_DSW_256HZ                         9
+#define WM8350_RTC_DSW_512HZ                        10
+#define WM8350_RTC_DSW_1024HZ                       11
+
+/*
+ * R218 (0xDA) - RTC Tick Control
+ */
+#define WM8350_RTC_TICKSTS                      0x4000
+#define WM8350_RTC_CLKSRC                       0x2000
+#define WM8350_RTC_TRIM_MASK                    0x03FF
+
+/*
+ * RTC Interrupts.
+ */
+#define WM8350_IRQ_RTC_PER                     7
+#define WM8350_IRQ_RTC_SEC                     8
+#define WM8350_IRQ_RTC_ALM                     9
+
+struct wm8350_rtc {
+       struct platform_device *pdev;
+};
+
+#endif
diff --git a/include/linux/mfd/wm8350/supply.h b/include/linux/mfd/wm8350/supply.h
new file mode 100644 (file)
index 0000000..1c8f3cd
--- /dev/null
@@ -0,0 +1,111 @@
+/*
+ * supply.h  --  Power Supply Driver for Wolfson WM8350 PMIC
+ *
+ * Copyright 2007 Wolfson Microelectronics PLC
+ *
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ *
+ */
+
+#ifndef __LINUX_MFD_WM8350_SUPPLY_H_
+#define __LINUX_MFD_WM8350_SUPPLY_H_
+
+#include <linux/platform_device.h>
+
+/*
+ * Charger registers
+ */
+#define WM8350_BATTERY_CHARGER_CONTROL_1        0xA8
+#define WM8350_BATTERY_CHARGER_CONTROL_2        0xA9
+#define WM8350_BATTERY_CHARGER_CONTROL_3        0xAA
+
+/*
+ * R168 (0xA8) - Battery Charger Control 1
+ */
+#define WM8350_CHG_ENA_R168                     0x8000
+#define WM8350_CHG_THR                          0x2000
+#define WM8350_CHG_EOC_SEL_MASK                 0x1C00
+#define WM8350_CHG_TRICKLE_TEMP_CHOKE           0x0200
+#define WM8350_CHG_TRICKLE_USB_CHOKE            0x0100
+#define WM8350_CHG_RECOVER_T                    0x0080
+#define WM8350_CHG_END_ACT                      0x0040
+#define WM8350_CHG_FAST                         0x0020
+#define WM8350_CHG_FAST_USB_THROTTLE            0x0010
+#define WM8350_CHG_NTC_MON                      0x0008
+#define WM8350_CHG_BATT_HOT_MON                 0x0004
+#define WM8350_CHG_BATT_COLD_MON                0x0002
+#define WM8350_CHG_CHIP_TEMP_MON                0x0001
+
+/*
+ * R169 (0xA9) - Battery Charger Control 2
+ */
+#define WM8350_CHG_ACTIVE                       0x8000
+#define WM8350_CHG_PAUSE                        0x4000
+#define WM8350_CHG_STS_MASK                     0x3000
+#define WM8350_CHG_TIME_MASK                    0x0F00
+#define WM8350_CHG_MASK_WALL_FB                 0x0080
+#define WM8350_CHG_TRICKLE_SEL                  0x0040
+#define WM8350_CHG_VSEL_MASK                    0x0030
+#define WM8350_CHG_ISEL_MASK                    0x000F
+#define WM8350_CHG_STS_OFF                      0x0000
+#define WM8350_CHG_STS_TRICKLE                  0x1000
+#define WM8350_CHG_STS_FAST                     0x2000
+
+/*
+ * R170 (0xAA) - Battery Charger Control 3
+ */
+#define WM8350_CHG_THROTTLE_T_MASK              0x0060
+#define WM8350_CHG_SMART                        0x0010
+#define WM8350_CHG_TIMER_ADJT_MASK              0x000F
+
+/*
+ * Charger Interrupts
+ */
+#define WM8350_IRQ_CHG_BAT_HOT                 0
+#define WM8350_IRQ_CHG_BAT_COLD                        1
+#define WM8350_IRQ_CHG_BAT_FAIL                        2
+#define WM8350_IRQ_CHG_TO                      3
+#define WM8350_IRQ_CHG_END                     4
+#define WM8350_IRQ_CHG_START                   5
+#define WM8350_IRQ_CHG_FAST_RDY                        6
+#define WM8350_IRQ_CHG_VBATT_LT_3P9            10
+#define WM8350_IRQ_CHG_VBATT_LT_3P1            11
+#define WM8350_IRQ_CHG_VBATT_LT_2P85           12
+
+/*
+ * Charger Policy
+ */
+#define WM8350_CHG_TRICKLE_50mA                        (0 << 6)
+#define WM8350_CHG_TRICKLE_100mA               (1 << 6)
+#define WM8350_CHG_4_05V                       (0 << 4)
+#define WM8350_CHG_4_10V                       (1 << 4)
+#define WM8350_CHG_4_15V                       (2 << 4)
+#define WM8350_CHG_4_20V                       (3 << 4)
+#define WM8350_CHG_FAST_LIMIT_mA(x)            ((x / 50) & 0xf)
+#define WM8350_CHG_EOC_mA(x)                   (((x - 10) & 0x7) << 10)
+#define WM8350_CHG_TRICKLE_3_1V                        (0 << 13)
+#define WM8350_CHG_TRICKLE_3_9V                        (1 << 13)
+
+/*
+ * Supply Registers.
+ */
+#define WM8350_USB_VOLTAGE_READBACK             0x9C
+#define WM8350_LINE_VOLTAGE_READBACK            0x9D
+#define WM8350_BATT_VOLTAGE_READBACK            0x9E
+
+/*
+ * Supply Interrupts.
+ */
+#define WM8350_IRQ_USB_LIMIT                   15
+#define WM8350_IRQ_EXT_USB_FB                  36
+#define WM8350_IRQ_EXT_WALL_FB                 37
+#define WM8350_IRQ_EXT_BAT_FB                  38
+
+struct wm8350_power {
+       struct platform_device *pdev;
+};
+
+#endif
diff --git a/include/linux/mfd/wm8350/wdt.h b/include/linux/mfd/wm8350/wdt.h
new file mode 100644 (file)
index 0000000..f6135b5
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * wdt.h  --  Watchdog Driver for Wolfson WM8350 PMIC
+ *
+ * Copyright 2007, 2008 Wolfson Microelectronics PLC
+ *
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ */
+
+#ifndef __LINUX_MFD_WM8350_WDT_H_
+#define __LINUX_MFD_WM8350_WDT_H_
+
+#include <linux/platform_device.h>
+
+#define WM8350_WDOG_HIB_MODE                    0x0080
+#define WM8350_WDOG_DEBUG                       0x0040
+#define WM8350_WDOG_MODE_MASK                   0x0030
+#define WM8350_WDOG_TO_MASK                     0x0007
+
+#define WM8350_IRQ_SYS_WDOG_TO                 24
+
+struct wm8350_wdt {
+       struct platform_device *pdev;
+};
+
+#endif
diff --git a/include/linux/mfd/wm8400-audio.h b/include/linux/mfd/wm8400-audio.h
new file mode 100644 (file)
index 0000000..b6640e0
--- /dev/null
@@ -0,0 +1,1186 @@
+/*
+ * wm8400 private definitions for audio
+ *
+ * Copyright 2008 Wolfson Microelectronics plc
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef __LINUX_MFD_WM8400_AUDIO_H
+#define __LINUX_MFD_WM8400_AUDIO_H
+
+#include <linux/mfd/wm8400-audio.h>
+
+/*
+ * R2 (0x02) - Power Management (1)
+ */
+#define WM8400_CODEC_ENA                        0x8000  /* CODEC_ENA */
+#define WM8400_CODEC_ENA_MASK                   0x8000  /* CODEC_ENA */
+#define WM8400_CODEC_ENA_SHIFT                      15  /* CODEC_ENA */
+#define WM8400_CODEC_ENA_WIDTH                       1  /* CODEC_ENA */
+#define WM8400_SYSCLK_ENA                       0x4000  /* SYSCLK_ENA */
+#define WM8400_SYSCLK_ENA_MASK                  0x4000  /* SYSCLK_ENA */
+#define WM8400_SYSCLK_ENA_SHIFT                     14  /* SYSCLK_ENA */
+#define WM8400_SYSCLK_ENA_WIDTH                      1  /* SYSCLK_ENA */
+#define WM8400_SPK_MIX_ENA                      0x2000  /* SPK_MIX_ENA */
+#define WM8400_SPK_MIX_ENA_MASK                 0x2000  /* SPK_MIX_ENA */
+#define WM8400_SPK_MIX_ENA_SHIFT                    13  /* SPK_MIX_ENA */
+#define WM8400_SPK_MIX_ENA_WIDTH                     1  /* SPK_MIX_ENA */
+#define WM8400_SPK_ENA                          0x1000  /* SPK_ENA */
+#define WM8400_SPK_ENA_MASK                     0x1000  /* SPK_ENA */
+#define WM8400_SPK_ENA_SHIFT                        12  /* SPK_ENA */
+#define WM8400_SPK_ENA_WIDTH                         1  /* SPK_ENA */
+#define WM8400_OUT3_ENA                         0x0800  /* OUT3_ENA */
+#define WM8400_OUT3_ENA_MASK                    0x0800  /* OUT3_ENA */
+#define WM8400_OUT3_ENA_SHIFT                       11  /* OUT3_ENA */
+#define WM8400_OUT3_ENA_WIDTH                        1  /* OUT3_ENA */
+#define WM8400_OUT4_ENA                         0x0400  /* OUT4_ENA */
+#define WM8400_OUT4_ENA_MASK                    0x0400  /* OUT4_ENA */
+#define WM8400_OUT4_ENA_SHIFT                       10  /* OUT4_ENA */
+#define WM8400_OUT4_ENA_WIDTH                        1  /* OUT4_ENA */
+#define WM8400_LOUT_ENA                         0x0200  /* LOUT_ENA */
+#define WM8400_LOUT_ENA_MASK                    0x0200  /* LOUT_ENA */
+#define WM8400_LOUT_ENA_SHIFT                        9  /* LOUT_ENA */
+#define WM8400_LOUT_ENA_WIDTH                        1  /* LOUT_ENA */
+#define WM8400_ROUT_ENA                         0x0100  /* ROUT_ENA */
+#define WM8400_ROUT_ENA_MASK                    0x0100  /* ROUT_ENA */
+#define WM8400_ROUT_ENA_SHIFT                        8  /* ROUT_ENA */
+#define WM8400_ROUT_ENA_WIDTH                        1  /* ROUT_ENA */
+#define WM8400_MIC1BIAS_ENA                     0x0010  /* MIC1BIAS_ENA */
+#define WM8400_MIC1BIAS_ENA_MASK                0x0010  /* MIC1BIAS_ENA */
+#define WM8400_MIC1BIAS_ENA_SHIFT                    4  /* MIC1BIAS_ENA */
+#define WM8400_MIC1BIAS_ENA_WIDTH                    1  /* MIC1BIAS_ENA */
+#define WM8400_VMID_MODE_MASK                   0x0006  /* VMID_MODE - [2:1] */
+#define WM8400_VMID_MODE_SHIFT                       1  /* VMID_MODE - [2:1] */
+#define WM8400_VMID_MODE_WIDTH                       2  /* VMID_MODE - [2:1] */
+#define WM8400_VREF_ENA                         0x0001  /* VREF_ENA */
+#define WM8400_VREF_ENA_MASK                    0x0001  /* VREF_ENA */
+#define WM8400_VREF_ENA_SHIFT                        0  /* VREF_ENA */
+#define WM8400_VREF_ENA_WIDTH                        1  /* VREF_ENA */
+
+/*
+ * R3 (0x03) - Power Management (2)
+ */
+#define WM8400_FLL_ENA                          0x8000  /* FLL_ENA */
+#define WM8400_FLL_ENA_MASK                     0x8000  /* FLL_ENA */
+#define WM8400_FLL_ENA_SHIFT                        15  /* FLL_ENA */
+#define WM8400_FLL_ENA_WIDTH                         1  /* FLL_ENA */
+#define WM8400_TSHUT_ENA                        0x4000  /* TSHUT_ENA */
+#define WM8400_TSHUT_ENA_MASK                   0x4000  /* TSHUT_ENA */
+#define WM8400_TSHUT_ENA_SHIFT                      14  /* TSHUT_ENA */
+#define WM8400_TSHUT_ENA_WIDTH                       1  /* TSHUT_ENA */
+#define WM8400_TSHUT_OPDIS                      0x2000  /* TSHUT_OPDIS */
+#define WM8400_TSHUT_OPDIS_MASK                 0x2000  /* TSHUT_OPDIS */
+#define WM8400_TSHUT_OPDIS_SHIFT                    13  /* TSHUT_OPDIS */
+#define WM8400_TSHUT_OPDIS_WIDTH                     1  /* TSHUT_OPDIS */
+#define WM8400_OPCLK_ENA                        0x0800  /* OPCLK_ENA */
+#define WM8400_OPCLK_ENA_MASK                   0x0800  /* OPCLK_ENA */
+#define WM8400_OPCLK_ENA_SHIFT                      11  /* OPCLK_ENA */
+#define WM8400_OPCLK_ENA_WIDTH                       1  /* OPCLK_ENA */
+#define WM8400_AINL_ENA                         0x0200  /* AINL_ENA */
+#define WM8400_AINL_ENA_MASK                    0x0200  /* AINL_ENA */
+#define WM8400_AINL_ENA_SHIFT                        9  /* AINL_ENA */
+#define WM8400_AINL_ENA_WIDTH                        1  /* AINL_ENA */
+#define WM8400_AINR_ENA                         0x0100  /* AINR_ENA */
+#define WM8400_AINR_ENA_MASK                    0x0100  /* AINR_ENA */
+#define WM8400_AINR_ENA_SHIFT                        8  /* AINR_ENA */
+#define WM8400_AINR_ENA_WIDTH                        1  /* AINR_ENA */
+#define WM8400_LIN34_ENA                        0x0080  /* LIN34_ENA */
+#define WM8400_LIN34_ENA_MASK                   0x0080  /* LIN34_ENA */
+#define WM8400_LIN34_ENA_SHIFT                       7  /* LIN34_ENA */
+#define WM8400_LIN34_ENA_WIDTH                       1  /* LIN34_ENA */
+#define WM8400_LIN12_ENA                        0x0040  /* LIN12_ENA */
+#define WM8400_LIN12_ENA_MASK                   0x0040  /* LIN12_ENA */
+#define WM8400_LIN12_ENA_SHIFT                       6  /* LIN12_ENA */
+#define WM8400_LIN12_ENA_WIDTH                       1  /* LIN12_ENA */
+#define WM8400_RIN34_ENA                        0x0020  /* RIN34_ENA */
+#define WM8400_RIN34_ENA_MASK                   0x0020  /* RIN34_ENA */
+#define WM8400_RIN34_ENA_SHIFT                       5  /* RIN34_ENA */
+#define WM8400_RIN34_ENA_WIDTH                       1  /* RIN34_ENA */
+#define WM8400_RIN12_ENA                        0x0010  /* RIN12_ENA */
+#define WM8400_RIN12_ENA_MASK                   0x0010  /* RIN12_ENA */
+#define WM8400_RIN12_ENA_SHIFT                       4  /* RIN12_ENA */
+#define WM8400_RIN12_ENA_WIDTH                       1  /* RIN12_ENA */
+#define WM8400_ADCL_ENA                         0x0002  /* ADCL_ENA */
+#define WM8400_ADCL_ENA_MASK                    0x0002  /* ADCL_ENA */
+#define WM8400_ADCL_ENA_SHIFT                        1  /* ADCL_ENA */
+#define WM8400_ADCL_ENA_WIDTH                        1  /* ADCL_ENA */
+#define WM8400_ADCR_ENA                         0x0001  /* ADCR_ENA */
+#define WM8400_ADCR_ENA_MASK                    0x0001  /* ADCR_ENA */
+#define WM8400_ADCR_ENA_SHIFT                        0  /* ADCR_ENA */
+#define WM8400_ADCR_ENA_WIDTH                        1  /* ADCR_ENA */
+
+/*
+ * R4 (0x04) - Power Management (3)
+ */
+#define WM8400_LON_ENA                          0x2000  /* LON_ENA */
+#define WM8400_LON_ENA_MASK                     0x2000  /* LON_ENA */
+#define WM8400_LON_ENA_SHIFT                        13  /* LON_ENA */
+#define WM8400_LON_ENA_WIDTH                         1  /* LON_ENA */
+#define WM8400_LOP_ENA                          0x1000  /* LOP_ENA */
+#define WM8400_LOP_ENA_MASK                     0x1000  /* LOP_ENA */
+#define WM8400_LOP_ENA_SHIFT                        12  /* LOP_ENA */
+#define WM8400_LOP_ENA_WIDTH                         1  /* LOP_ENA */
+#define WM8400_RON_ENA                          0x0800  /* RON_ENA */
+#define WM8400_RON_ENA_MASK                     0x0800  /* RON_ENA */
+#define WM8400_RON_ENA_SHIFT                        11  /* RON_ENA */
+#define WM8400_RON_ENA_WIDTH                         1  /* RON_ENA */
+#define WM8400_ROP_ENA                          0x0400  /* ROP_ENA */
+#define WM8400_ROP_ENA_MASK                     0x0400  /* ROP_ENA */
+#define WM8400_ROP_ENA_SHIFT                        10  /* ROP_ENA */
+#define WM8400_ROP_ENA_WIDTH                         1  /* ROP_ENA */
+#define WM8400_LOPGA_ENA                        0x0080  /* LOPGA_ENA */
+#define WM8400_LOPGA_ENA_MASK                   0x0080  /* LOPGA_ENA */
+#define WM8400_LOPGA_ENA_SHIFT                       7  /* LOPGA_ENA */
+#define WM8400_LOPGA_ENA_WIDTH                       1  /* LOPGA_ENA */
+#define WM8400_ROPGA_ENA                        0x0040  /* ROPGA_ENA */
+#define WM8400_ROPGA_ENA_MASK                   0x0040  /* ROPGA_ENA */
+#define WM8400_ROPGA_ENA_SHIFT                       6  /* ROPGA_ENA */
+#define WM8400_ROPGA_ENA_WIDTH                       1  /* ROPGA_ENA */
+#define WM8400_LOMIX_ENA                        0x0020  /* LOMIX_ENA */
+#define WM8400_LOMIX_ENA_MASK                   0x0020  /* LOMIX_ENA */
+#define WM8400_LOMIX_ENA_SHIFT                       5  /* LOMIX_ENA */
+#define WM8400_LOMIX_ENA_WIDTH                       1  /* LOMIX_ENA */
+#define WM8400_ROMIX_ENA                        0x0010  /* ROMIX_ENA */
+#define WM8400_ROMIX_ENA_MASK                   0x0010  /* ROMIX_ENA */
+#define WM8400_ROMIX_ENA_SHIFT                       4  /* ROMIX_ENA */
+#define WM8400_ROMIX_ENA_WIDTH                       1  /* ROMIX_ENA */
+#define WM8400_DACL_ENA                         0x0002  /* DACL_ENA */
+#define WM8400_DACL_ENA_MASK                    0x0002  /* DACL_ENA */
+#define WM8400_DACL_ENA_SHIFT                        1  /* DACL_ENA */
+#define WM8400_DACL_ENA_WIDTH                        1  /* DACL_ENA */
+#define WM8400_DACR_ENA                         0x0001  /* DACR_ENA */
+#define WM8400_DACR_ENA_MASK                    0x0001  /* DACR_ENA */
+#define WM8400_DACR_ENA_SHIFT                        0  /* DACR_ENA */
+#define WM8400_DACR_ENA_WIDTH                        1  /* DACR_ENA */
+
+/*
+ * R5 (0x05) - Audio Interface (1)
+ */
+#define WM8400_AIFADCL_SRC                      0x8000  /* AIFADCL_SRC */
+#define WM8400_AIFADCL_SRC_MASK                 0x8000  /* AIFADCL_SRC */
+#define WM8400_AIFADCL_SRC_SHIFT                    15  /* AIFADCL_SRC */
+#define WM8400_AIFADCL_SRC_WIDTH                     1  /* AIFADCL_SRC */
+#define WM8400_AIFADCR_SRC                      0x4000  /* AIFADCR_SRC */
+#define WM8400_AIFADCR_SRC_MASK                 0x4000  /* AIFADCR_SRC */
+#define WM8400_AIFADCR_SRC_SHIFT                    14  /* AIFADCR_SRC */
+#define WM8400_AIFADCR_SRC_WIDTH                     1  /* AIFADCR_SRC */
+#define WM8400_AIFADC_TDM                       0x2000  /* AIFADC_TDM */
+#define WM8400_AIFADC_TDM_MASK                  0x2000  /* AIFADC_TDM */
+#define WM8400_AIFADC_TDM_SHIFT                     13  /* AIFADC_TDM */
+#define WM8400_AIFADC_TDM_WIDTH                      1  /* AIFADC_TDM */
+#define WM8400_AIFADC_TDM_CHAN                  0x1000  /* AIFADC_TDM_CHAN */
+#define WM8400_AIFADC_TDM_CHAN_MASK             0x1000  /* AIFADC_TDM_CHAN */
+#define WM8400_AIFADC_TDM_CHAN_SHIFT                12  /* AIFADC_TDM_CHAN */
+#define WM8400_AIFADC_TDM_CHAN_WIDTH                 1  /* AIFADC_TDM_CHAN */
+#define WM8400_AIF_BCLK_INV                     0x0100  /* AIF_BCLK_INV */
+#define WM8400_AIF_BCLK_INV_MASK                0x0100  /* AIF_BCLK_INV */
+#define WM8400_AIF_BCLK_INV_SHIFT                    8  /* AIF_BCLK_INV */
+#define WM8400_AIF_BCLK_INV_WIDTH                    1  /* AIF_BCLK_INV */
+#define WM8400_AIF_LRCLK_INV                    0x0080  /* AIF_LRCLK_INV */
+#define WM8400_AIF_LRCLK_INV_MASK               0x0080  /* AIF_LRCLK_INV */
+#define WM8400_AIF_LRCLK_INV_SHIFT                   7  /* AIF_LRCLK_INV */
+#define WM8400_AIF_LRCLK_INV_WIDTH                   1  /* AIF_LRCLK_INV */
+#define WM8400_AIF_WL_MASK                      0x0060  /* AIF_WL - [6:5] */
+#define WM8400_AIF_WL_SHIFT                          5  /* AIF_WL - [6:5] */
+#define WM8400_AIF_WL_WIDTH                          2  /* AIF_WL - [6:5] */
+#define WM8400_AIF_WL_16BITS                   (0 << 5)
+#define WM8400_AIF_WL_20BITS                   (1 << 5)
+#define WM8400_AIF_WL_24BITS                   (2 << 5)
+#define WM8400_AIF_WL_32BITS                   (3 << 5)
+#define WM8400_AIF_FMT_MASK                     0x0018  /* AIF_FMT - [4:3] */
+#define WM8400_AIF_FMT_SHIFT                         3  /* AIF_FMT - [4:3] */
+#define WM8400_AIF_FMT_WIDTH                         2  /* AIF_FMT - [4:3] */
+#define WM8400_AIF_FMT_RIGHTJ                  (0 << 3)
+#define WM8400_AIF_FMT_LEFTJ                   (1 << 3)
+#define WM8400_AIF_FMT_I2S                     (2 << 3)
+#define WM8400_AIF_FMT_DSP                     (3 << 3)
+
+/*
+ * R6 (0x06) - Audio Interface (2)
+ */
+#define WM8400_DACL_SRC                         0x8000  /* DACL_SRC */
+#define WM8400_DACL_SRC_MASK                    0x8000  /* DACL_SRC */
+#define WM8400_DACL_SRC_SHIFT                       15  /* DACL_SRC */
+#define WM8400_DACL_SRC_WIDTH                        1  /* DACL_SRC */
+#define WM8400_DACR_SRC                         0x4000  /* DACR_SRC */
+#define WM8400_DACR_SRC_MASK                    0x4000  /* DACR_SRC */
+#define WM8400_DACR_SRC_SHIFT                       14  /* DACR_SRC */
+#define WM8400_DACR_SRC_WIDTH                        1  /* DACR_SRC */
+#define WM8400_AIFDAC_TDM                       0x2000  /* AIFDAC_TDM */
+#define WM8400_AIFDAC_TDM_MASK                  0x2000  /* AIFDAC_TDM */
+#define WM8400_AIFDAC_TDM_SHIFT                     13  /* AIFDAC_TDM */
+#define WM8400_AIFDAC_TDM_WIDTH                      1  /* AIFDAC_TDM */
+#define WM8400_AIFDAC_TDM_CHAN                  0x1000  /* AIFDAC_TDM_CHAN */
+#define WM8400_AIFDAC_TDM_CHAN_MASK             0x1000  /* AIFDAC_TDM_CHAN */
+#define WM8400_AIFDAC_TDM_CHAN_SHIFT                12  /* AIFDAC_TDM_CHAN */
+#define WM8400_AIFDAC_TDM_CHAN_WIDTH                 1  /* AIFDAC_TDM_CHAN */
+#define WM8400_DAC_BOOST_MASK                   0x0C00  /* DAC_BOOST - [11:10] */
+#define WM8400_DAC_BOOST_SHIFT                      10  /* DAC_BOOST - [11:10] */
+#define WM8400_DAC_BOOST_WIDTH                       2  /* DAC_BOOST - [11:10] */
+#define WM8400_DAC_COMP                         0x0010  /* DAC_COMP */
+#define WM8400_DAC_COMP_MASK                    0x0010  /* DAC_COMP */
+#define WM8400_DAC_COMP_SHIFT                        4  /* DAC_COMP */
+#define WM8400_DAC_COMP_WIDTH                        1  /* DAC_COMP */
+#define WM8400_DAC_COMPMODE                     0x0008  /* DAC_COMPMODE */
+#define WM8400_DAC_COMPMODE_MASK                0x0008  /* DAC_COMPMODE */
+#define WM8400_DAC_COMPMODE_SHIFT                    3  /* DAC_COMPMODE */
+#define WM8400_DAC_COMPMODE_WIDTH                    1  /* DAC_COMPMODE */
+#define WM8400_ADC_COMP                         0x0004  /* ADC_COMP */
+#define WM8400_ADC_COMP_MASK                    0x0004  /* ADC_COMP */
+#define WM8400_ADC_COMP_SHIFT                        2  /* ADC_COMP */
+#define WM8400_ADC_COMP_WIDTH                        1  /* ADC_COMP */
+#define WM8400_ADC_COMPMODE                     0x0002  /* ADC_COMPMODE */
+#define WM8400_ADC_COMPMODE_MASK                0x0002  /* ADC_COMPMODE */
+#define WM8400_ADC_COMPMODE_SHIFT                    1  /* ADC_COMPMODE */
+#define WM8400_ADC_COMPMODE_WIDTH                    1  /* ADC_COMPMODE */
+#define WM8400_LOOPBACK                         0x0001  /* LOOPBACK */
+#define WM8400_LOOPBACK_MASK                    0x0001  /* LOOPBACK */
+#define WM8400_LOOPBACK_SHIFT                        0  /* LOOPBACK */
+#define WM8400_LOOPBACK_WIDTH                        1  /* LOOPBACK */
+
+/*
+ * R7 (0x07) - Clocking (1)
+ */
+#define WM8400_TOCLK_RATE                       0x8000  /* TOCLK_RATE */
+#define WM8400_TOCLK_RATE_MASK                  0x8000  /* TOCLK_RATE */
+#define WM8400_TOCLK_RATE_SHIFT                     15  /* TOCLK_RATE */
+#define WM8400_TOCLK_RATE_WIDTH                      1  /* TOCLK_RATE */
+#define WM8400_TOCLK_ENA                        0x4000  /* TOCLK_ENA */
+#define WM8400_TOCLK_ENA_MASK                   0x4000  /* TOCLK_ENA */
+#define WM8400_TOCLK_ENA_SHIFT                      14  /* TOCLK_ENA */
+#define WM8400_TOCLK_ENA_WIDTH                       1  /* TOCLK_ENA */
+#define WM8400_OPCLKDIV_MASK                    0x1E00  /* OPCLKDIV - [12:9] */
+#define WM8400_OPCLKDIV_SHIFT                        9  /* OPCLKDIV - [12:9] */
+#define WM8400_OPCLKDIV_WIDTH                        4  /* OPCLKDIV - [12:9] */
+#define WM8400_DCLKDIV_MASK                     0x01C0  /* DCLKDIV - [8:6] */
+#define WM8400_DCLKDIV_SHIFT                         6  /* DCLKDIV - [8:6] */
+#define WM8400_DCLKDIV_WIDTH                         3  /* DCLKDIV - [8:6] */
+#define WM8400_BCLK_DIV_MASK                    0x001E  /* BCLK_DIV - [4:1] */
+#define WM8400_BCLK_DIV_SHIFT                        1  /* BCLK_DIV - [4:1] */
+#define WM8400_BCLK_DIV_WIDTH                        4  /* BCLK_DIV - [4:1] */
+
+/*
+ * R8 (0x08) - Clocking (2)
+ */
+#define WM8400_MCLK_SRC                         0x8000  /* MCLK_SRC */
+#define WM8400_MCLK_SRC_MASK                    0x8000  /* MCLK_SRC */
+#define WM8400_MCLK_SRC_SHIFT                       15  /* MCLK_SRC */
+#define WM8400_MCLK_SRC_WIDTH                        1  /* MCLK_SRC */
+#define WM8400_SYSCLK_SRC                       0x4000  /* SYSCLK_SRC */
+#define WM8400_SYSCLK_SRC_MASK                  0x4000  /* SYSCLK_SRC */
+#define WM8400_SYSCLK_SRC_SHIFT                     14  /* SYSCLK_SRC */
+#define WM8400_SYSCLK_SRC_WIDTH                      1  /* SYSCLK_SRC */
+#define WM8400_CLK_FORCE                        0x2000  /* CLK_FORCE */
+#define WM8400_CLK_FORCE_MASK                   0x2000  /* CLK_FORCE */
+#define WM8400_CLK_FORCE_SHIFT                      13  /* CLK_FORCE */
+#define WM8400_CLK_FORCE_WIDTH                       1  /* CLK_FORCE */
+#define WM8400_MCLK_DIV_MASK                    0x1800  /* MCLK_DIV - [12:11] */
+#define WM8400_MCLK_DIV_SHIFT                       11  /* MCLK_DIV - [12:11] */
+#define WM8400_MCLK_DIV_WIDTH                        2  /* MCLK_DIV - [12:11] */
+#define WM8400_MCLK_INV                         0x0400  /* MCLK_INV */
+#define WM8400_MCLK_INV_MASK                    0x0400  /* MCLK_INV */
+#define WM8400_MCLK_INV_SHIFT                       10  /* MCLK_INV */
+#define WM8400_MCLK_INV_WIDTH                        1  /* MCLK_INV */
+#define WM8400_ADC_CLKDIV_MASK                  0x00E0  /* ADC_CLKDIV - [7:5] */
+#define WM8400_ADC_CLKDIV_SHIFT                      5  /* ADC_CLKDIV - [7:5] */
+#define WM8400_ADC_CLKDIV_WIDTH                      3  /* ADC_CLKDIV - [7:5] */
+#define WM8400_DAC_CLKDIV_MASK                  0x001C  /* DAC_CLKDIV - [4:2] */
+#define WM8400_DAC_CLKDIV_SHIFT                      2  /* DAC_CLKDIV - [4:2] */
+#define WM8400_DAC_CLKDIV_WIDTH                      3  /* DAC_CLKDIV - [4:2] */
+
+/*
+ * R9 (0x09) - Audio Interface (3)
+ */
+#define WM8400_AIF_MSTR1                        0x8000  /* AIF_MSTR1 */
+#define WM8400_AIF_MSTR1_MASK                   0x8000  /* AIF_MSTR1 */
+#define WM8400_AIF_MSTR1_SHIFT                      15  /* AIF_MSTR1 */
+#define WM8400_AIF_MSTR1_WIDTH                       1  /* AIF_MSTR1 */
+#define WM8400_AIF_MSTR2                        0x4000  /* AIF_MSTR2 */
+#define WM8400_AIF_MSTR2_MASK                   0x4000  /* AIF_MSTR2 */
+#define WM8400_AIF_MSTR2_SHIFT                      14  /* AIF_MSTR2 */
+#define WM8400_AIF_MSTR2_WIDTH                       1  /* AIF_MSTR2 */
+#define WM8400_AIF_SEL                          0x2000  /* AIF_SEL */
+#define WM8400_AIF_SEL_MASK                     0x2000  /* AIF_SEL */
+#define WM8400_AIF_SEL_SHIFT                        13  /* AIF_SEL */
+#define WM8400_AIF_SEL_WIDTH                         1  /* AIF_SEL */
+#define WM8400_ADCLRC_DIR                       0x0800  /* ADCLRC_DIR */
+#define WM8400_ADCLRC_DIR_MASK                  0x0800  /* ADCLRC_DIR */
+#define WM8400_ADCLRC_DIR_SHIFT                     11  /* ADCLRC_DIR */
+#define WM8400_ADCLRC_DIR_WIDTH                      1  /* ADCLRC_DIR */
+#define WM8400_ADCLRC_RATE_MASK                 0x07FF  /* ADCLRC_RATE - [10:0] */
+#define WM8400_ADCLRC_RATE_SHIFT                     0  /* ADCLRC_RATE - [10:0] */
+#define WM8400_ADCLRC_RATE_WIDTH                    11  /* ADCLRC_RATE - [10:0] */
+
+/*
+ * R10 (0x0A) - Audio Interface (4)
+ */
+#define WM8400_ALRCGPIO1                        0x8000  /* ALRCGPIO1 */
+#define WM8400_ALRCGPIO1_MASK                   0x8000  /* ALRCGPIO1 */
+#define WM8400_ALRCGPIO1_SHIFT                      15  /* ALRCGPIO1 */
+#define WM8400_ALRCGPIO1_WIDTH                       1  /* ALRCGPIO1 */
+#define WM8400_ALRCBGPIO6                       0x4000  /* ALRCBGPIO6 */
+#define WM8400_ALRCBGPIO6_MASK                  0x4000  /* ALRCBGPIO6 */
+#define WM8400_ALRCBGPIO6_SHIFT                     14  /* ALRCBGPIO6 */
+#define WM8400_ALRCBGPIO6_WIDTH                      1  /* ALRCBGPIO6 */
+#define WM8400_AIF_TRIS                         0x2000  /* AIF_TRIS */
+#define WM8400_AIF_TRIS_MASK                    0x2000  /* AIF_TRIS */
+#define WM8400_AIF_TRIS_SHIFT                       13  /* AIF_TRIS */
+#define WM8400_AIF_TRIS_WIDTH                        1  /* AIF_TRIS */
+#define WM8400_DACLRC_DIR                       0x0800  /* DACLRC_DIR */
+#define WM8400_DACLRC_DIR_MASK                  0x0800  /* DACLRC_DIR */
+#define WM8400_DACLRC_DIR_SHIFT                     11  /* DACLRC_DIR */
+#define WM8400_DACLRC_DIR_WIDTH                      1  /* DACLRC_DIR */
+#define WM8400_DACLRC_RATE_MASK                 0x07FF  /* DACLRC_RATE - [10:0] */
+#define WM8400_DACLRC_RATE_SHIFT                     0  /* DACLRC_RATE - [10:0] */
+#define WM8400_DACLRC_RATE_WIDTH                    11  /* DACLRC_RATE - [10:0] */
+
+/*
+ * R11 (0x0B) - DAC CTRL
+ */
+#define WM8400_DAC_SDMCLK_RATE                  0x2000  /* DAC_SDMCLK_RATE */
+#define WM8400_DAC_SDMCLK_RATE_MASK             0x2000  /* DAC_SDMCLK_RATE */
+#define WM8400_DAC_SDMCLK_RATE_SHIFT                13  /* DAC_SDMCLK_RATE */
+#define WM8400_DAC_SDMCLK_RATE_WIDTH                 1  /* DAC_SDMCLK_RATE */
+#define WM8400_AIF_LRCLKRATE                    0x0400  /* AIF_LRCLKRATE */
+#define WM8400_AIF_LRCLKRATE_MASK               0x0400  /* AIF_LRCLKRATE */
+#define WM8400_AIF_LRCLKRATE_SHIFT                  10  /* AIF_LRCLKRATE */
+#define WM8400_AIF_LRCLKRATE_WIDTH                   1  /* AIF_LRCLKRATE */
+#define WM8400_DAC_MONO                         0x0200  /* DAC_MONO */
+#define WM8400_DAC_MONO_MASK                    0x0200  /* DAC_MONO */
+#define WM8400_DAC_MONO_SHIFT                        9  /* DAC_MONO */
+#define WM8400_DAC_MONO_WIDTH                        1  /* DAC_MONO */
+#define WM8400_DAC_SB_FILT                      0x0100  /* DAC_SB_FILT */
+#define WM8400_DAC_SB_FILT_MASK                 0x0100  /* DAC_SB_FILT */
+#define WM8400_DAC_SB_FILT_SHIFT                     8  /* DAC_SB_FILT */
+#define WM8400_DAC_SB_FILT_WIDTH                     1  /* DAC_SB_FILT */
+#define WM8400_DAC_MUTERATE                     0x0080  /* DAC_MUTERATE */
+#define WM8400_DAC_MUTERATE_MASK                0x0080  /* DAC_MUTERATE */
+#define WM8400_DAC_MUTERATE_SHIFT                    7  /* DAC_MUTERATE */
+#define WM8400_DAC_MUTERATE_WIDTH                    1  /* DAC_MUTERATE */
+#define WM8400_DAC_MUTEMODE                     0x0040  /* DAC_MUTEMODE */
+#define WM8400_DAC_MUTEMODE_MASK                0x0040  /* DAC_MUTEMODE */
+#define WM8400_DAC_MUTEMODE_SHIFT                    6  /* DAC_MUTEMODE */
+#define WM8400_DAC_MUTEMODE_WIDTH                    1  /* DAC_MUTEMODE */
+#define WM8400_DEEMP_MASK                       0x0030  /* DEEMP - [5:4] */
+#define WM8400_DEEMP_SHIFT                           4  /* DEEMP - [5:4] */
+#define WM8400_DEEMP_WIDTH                           2  /* DEEMP - [5:4] */
+#define WM8400_DAC_MUTE                         0x0004  /* DAC_MUTE */
+#define WM8400_DAC_MUTE_MASK                    0x0004  /* DAC_MUTE */
+#define WM8400_DAC_MUTE_SHIFT                        2  /* DAC_MUTE */
+#define WM8400_DAC_MUTE_WIDTH                        1  /* DAC_MUTE */
+#define WM8400_DACL_DATINV                      0x0002  /* DACL_DATINV */
+#define WM8400_DACL_DATINV_MASK                 0x0002  /* DACL_DATINV */
+#define WM8400_DACL_DATINV_SHIFT                     1  /* DACL_DATINV */
+#define WM8400_DACL_DATINV_WIDTH                     1  /* DACL_DATINV */
+#define WM8400_DACR_DATINV                      0x0001  /* DACR_DATINV */
+#define WM8400_DACR_DATINV_MASK                 0x0001  /* DACR_DATINV */
+#define WM8400_DACR_DATINV_SHIFT                     0  /* DACR_DATINV */
+#define WM8400_DACR_DATINV_WIDTH                     1  /* DACR_DATINV */
+
+/*
+ * R12 (0x0C) - Left DAC Digital Volume
+ */
+#define WM8400_DAC_VU                           0x0100  /* DAC_VU */
+#define WM8400_DAC_VU_MASK                      0x0100  /* DAC_VU */
+#define WM8400_DAC_VU_SHIFT                          8  /* DAC_VU */
+#define WM8400_DAC_VU_WIDTH                          1  /* DAC_VU */
+#define WM8400_DACL_VOL_MASK                    0x00FF  /* DACL_VOL - [7:0] */
+#define WM8400_DACL_VOL_SHIFT                        0  /* DACL_VOL - [7:0] */
+#define WM8400_DACL_VOL_WIDTH                        8  /* DACL_VOL - [7:0] */
+
+/*
+ * R13 (0x0D) - Right DAC Digital Volume
+ */
+#define WM8400_DAC_VU                           0x0100  /* DAC_VU */
+#define WM8400_DAC_VU_MASK                      0x0100  /* DAC_VU */
+#define WM8400_DAC_VU_SHIFT                          8  /* DAC_VU */
+#define WM8400_DAC_VU_WIDTH                          1  /* DAC_VU */
+#define WM8400_DACR_VOL_MASK                    0x00FF  /* DACR_VOL - [7:0] */
+#define WM8400_DACR_VOL_SHIFT                        0  /* DACR_VOL - [7:0] */
+#define WM8400_DACR_VOL_WIDTH                        8  /* DACR_VOL - [7:0] */
+
+/*
+ * R14 (0x0E) - Digital Side Tone
+ */
+#define WM8400_ADCL_DAC_SVOL_MASK               0x1E00  /*   ADCL_DAC_SVOL - [12:9] */
+#define WM8400_ADCL_DAC_SVOL_SHIFT                   9  /*   ADCL_DAC_SVOL - [12:9] */
+#define WM8400_ADCL_DAC_SVOL_WIDTH                   4  /*   ADCL_DAC_SVOL - [12:9] */
+#define WM8400_ADCR_DAC_SVOL_MASK               0x01E0  /* ADCR_DAC_SVOL - [8:5] */
+#define WM8400_ADCR_DAC_SVOL_SHIFT                   5  /* ADCR_DAC_SVOL - [8:5] */
+#define WM8400_ADCR_DAC_SVOL_WIDTH                   4  /* ADCR_DAC_SVOL - [8:5] */
+#define WM8400_ADC_TO_DACL_MASK                 0x000C  /* ADC_TO_DACL - [3:2] */
+#define WM8400_ADC_TO_DACL_SHIFT                     2  /* ADC_TO_DACL - [3:2] */
+#define WM8400_ADC_TO_DACL_WIDTH                     2  /* ADC_TO_DACL - [3:2] */
+#define WM8400_ADC_TO_DACR_MASK                 0x0003  /* ADC_TO_DACR - [1:0] */
+#define WM8400_ADC_TO_DACR_SHIFT                     0  /* ADC_TO_DACR - [1:0] */
+#define WM8400_ADC_TO_DACR_WIDTH                     2  /* ADC_TO_DACR - [1:0] */
+
+/*
+ * R15 (0x0F) - ADC CTRL
+ */
+#define WM8400_ADC_HPF_ENA                      0x0100  /* ADC_HPF_ENA */
+#define WM8400_ADC_HPF_ENA_MASK                 0x0100  /* ADC_HPF_ENA */
+#define WM8400_ADC_HPF_ENA_SHIFT                     8  /* ADC_HPF_ENA */
+#define WM8400_ADC_HPF_ENA_WIDTH                     1  /* ADC_HPF_ENA */
+#define WM8400_ADC_HPF_CUT_MASK                 0x0060  /* ADC_HPF_CUT - [6:5] */
+#define WM8400_ADC_HPF_CUT_SHIFT                     5  /* ADC_HPF_CUT - [6:5] */
+#define WM8400_ADC_HPF_CUT_WIDTH                     2  /* ADC_HPF_CUT - [6:5] */
+#define WM8400_ADCL_DATINV                      0x0002  /* ADCL_DATINV */
+#define WM8400_ADCL_DATINV_MASK                 0x0002  /* ADCL_DATINV */
+#define WM8400_ADCL_DATINV_SHIFT                     1  /* ADCL_DATINV */
+#define WM8400_ADCL_DATINV_WIDTH                     1  /* ADCL_DATINV */
+#define WM8400_ADCR_DATINV                      0x0001  /* ADCR_DATINV */
+#define WM8400_ADCR_DATINV_MASK                 0x0001  /* ADCR_DATINV */
+#define WM8400_ADCR_DATINV_SHIFT                     0  /* ADCR_DATINV */
+#define WM8400_ADCR_DATINV_WIDTH                     1  /* ADCR_DATINV */
+
+/*
+ * R16 (0x10) - Left ADC Digital Volume
+ */
+#define WM8400_ADC_VU                           0x0100  /* ADC_VU */
+#define WM8400_ADC_VU_MASK                      0x0100  /* ADC_VU */
+#define WM8400_ADC_VU_SHIFT                          8  /* ADC_VU */
+#define WM8400_ADC_VU_WIDTH                          1  /* ADC_VU */
+#define WM8400_ADCL_VOL_MASK                    0x00FF  /* ADCL_VOL - [7:0] */
+#define WM8400_ADCL_VOL_SHIFT                        0  /* ADCL_VOL - [7:0] */
+#define WM8400_ADCL_VOL_WIDTH                        8  /* ADCL_VOL - [7:0] */
+
+/*
+ * R17 (0x11) - Right ADC Digital Volume
+ */
+#define WM8400_ADC_VU                           0x0100  /* ADC_VU */
+#define WM8400_ADC_VU_MASK                      0x0100  /* ADC_VU */
+#define WM8400_ADC_VU_SHIFT                          8  /* ADC_VU */
+#define WM8400_ADC_VU_WIDTH                          1  /* ADC_VU */
+#define WM8400_ADCR_VOL_MASK                    0x00FF  /* ADCR_VOL - [7:0] */
+#define WM8400_ADCR_VOL_SHIFT                        0  /* ADCR_VOL - [7:0] */
+#define WM8400_ADCR_VOL_WIDTH                        8  /* ADCR_VOL - [7:0] */
+
+/*
+ * R24 (0x18) - Left Line Input 1&2 Volume
+ */
+#define WM8400_IPVU                             0x0100  /* IPVU */
+#define WM8400_IPVU_MASK                        0x0100  /* IPVU */
+#define WM8400_IPVU_SHIFT                            8  /* IPVU */
+#define WM8400_IPVU_WIDTH                            1  /* IPVU */
+#define WM8400_LI12MUTE                         0x0080  /* LI12MUTE */
+#define WM8400_LI12MUTE_MASK                    0x0080  /* LI12MUTE */
+#define WM8400_LI12MUTE_SHIFT                        7  /* LI12MUTE */
+#define WM8400_LI12MUTE_WIDTH                        1  /* LI12MUTE */
+#define WM8400_LI12ZC                           0x0040  /* LI12ZC */
+#define WM8400_LI12ZC_MASK                      0x0040  /* LI12ZC */
+#define WM8400_LI12ZC_SHIFT                          6  /* LI12ZC */
+#define WM8400_LI12ZC_WIDTH                          1  /* LI12ZC */
+#define WM8400_LIN12VOL_MASK                    0x001F  /* LIN12VOL - [4:0] */
+#define WM8400_LIN12VOL_SHIFT                        0  /* LIN12VOL - [4:0] */
+#define WM8400_LIN12VOL_WIDTH                        5  /* LIN12VOL - [4:0] */
+
+/*
+ * R25 (0x19) - Left Line Input 3&4 Volume
+ */
+#define WM8400_IPVU                             0x0100  /* IPVU */
+#define WM8400_IPVU_MASK                        0x0100  /* IPVU */
+#define WM8400_IPVU_SHIFT                            8  /* IPVU */
+#define WM8400_IPVU_WIDTH                            1  /* IPVU */
+#define WM8400_LI34MUTE                         0x0080  /* LI34MUTE */
+#define WM8400_LI34MUTE_MASK                    0x0080  /* LI34MUTE */
+#define WM8400_LI34MUTE_SHIFT                        7  /* LI34MUTE */
+#define WM8400_LI34MUTE_WIDTH                        1  /* LI34MUTE */
+#define WM8400_LI34ZC                           0x0040  /* LI34ZC */
+#define WM8400_LI34ZC_MASK                      0x0040  /* LI34ZC */
+#define WM8400_LI34ZC_SHIFT                          6  /* LI34ZC */
+#define WM8400_LI34ZC_WIDTH                          1  /* LI34ZC */
+#define WM8400_LIN34VOL_MASK                    0x001F  /* LIN34VOL - [4:0] */
+#define WM8400_LIN34VOL_SHIFT                        0  /* LIN34VOL - [4:0] */
+#define WM8400_LIN34VOL_WIDTH                        5  /* LIN34VOL - [4:0] */
+
+/*
+ * R26 (0x1A) - Right Line Input 1&2 Volume
+ */
+#define WM8400_IPVU                             0x0100  /* IPVU */
+#define WM8400_IPVU_MASK                        0x0100  /* IPVU */
+#define WM8400_IPVU_SHIFT                            8  /* IPVU */
+#define WM8400_IPVU_WIDTH                            1  /* IPVU */
+#define WM8400_RI12MUTE                         0x0080  /* RI12MUTE */
+#define WM8400_RI12MUTE_MASK                    0x0080  /* RI12MUTE */
+#define WM8400_RI12MUTE_SHIFT                        7  /* RI12MUTE */
+#define WM8400_RI12MUTE_WIDTH                        1  /* RI12MUTE */
+#define WM8400_RI12ZC                           0x0040  /* RI12ZC */
+#define WM8400_RI12ZC_MASK                      0x0040  /* RI12ZC */
+#define WM8400_RI12ZC_SHIFT                          6  /* RI12ZC */
+#define WM8400_RI12ZC_WIDTH                          1  /* RI12ZC */
+#define WM8400_RIN12VOL_MASK                    0x001F  /* RIN12VOL - [4:0] */
+#define WM8400_RIN12VOL_SHIFT                        0  /* RIN12VOL - [4:0] */
+#define WM8400_RIN12VOL_WIDTH                        5  /* RIN12VOL - [4:0] */
+
+/*
+ * R27 (0x1B) - Right Line Input 3&4 Volume
+ */
+#define WM8400_IPVU                             0x0100  /* IPVU */
+#define WM8400_IPVU_MASK                        0x0100  /* IPVU */
+#define WM8400_IPVU_SHIFT                            8  /* IPVU */
+#define WM8400_IPVU_WIDTH                            1  /* IPVU */
+#define WM8400_RI34MUTE                         0x0080  /* RI34MUTE */
+#define WM8400_RI34MUTE_MASK                    0x0080  /* RI34MUTE */
+#define WM8400_RI34MUTE_SHIFT                        7  /* RI34MUTE */
+#define WM8400_RI34MUTE_WIDTH                        1  /* RI34MUTE */
+#define WM8400_RI34ZC                           0x0040  /* RI34ZC */
+#define WM8400_RI34ZC_MASK                      0x0040  /* RI34ZC */
+#define WM8400_RI34ZC_SHIFT                          6  /* RI34ZC */
+#define WM8400_RI34ZC_WIDTH                          1  /* RI34ZC */
+#define WM8400_RIN34VOL_MASK                    0x001F  /* RIN34VOL - [4:0] */
+#define WM8400_RIN34VOL_SHIFT                        0  /* RIN34VOL - [4:0] */
+#define WM8400_RIN34VOL_WIDTH                        5  /* RIN34VOL - [4:0] */
+
+/*
+ * R28 (0x1C) - Left Output Volume
+ */
+#define WM8400_OPVU                             0x0100  /* OPVU */
+#define WM8400_OPVU_MASK                        0x0100  /* OPVU */
+#define WM8400_OPVU_SHIFT                            8  /* OPVU */
+#define WM8400_OPVU_WIDTH                            1  /* OPVU */
+#define WM8400_LOZC                             0x0080  /* LOZC */
+#define WM8400_LOZC_MASK                        0x0080  /* LOZC */
+#define WM8400_LOZC_SHIFT                            7  /* LOZC */
+#define WM8400_LOZC_WIDTH                            1  /* LOZC */
+#define WM8400_LOUTVOL_MASK                     0x007F  /* LOUTVOL - [6:0] */
+#define WM8400_LOUTVOL_SHIFT                         0  /* LOUTVOL - [6:0] */
+#define WM8400_LOUTVOL_WIDTH                         7  /* LOUTVOL - [6:0] */
+
+/*
+ * R29 (0x1D) - Right Output Volume
+ */
+#define WM8400_OPVU                             0x0100  /* OPVU */
+#define WM8400_OPVU_MASK                        0x0100  /* OPVU */
+#define WM8400_OPVU_SHIFT                            8  /* OPVU */
+#define WM8400_OPVU_WIDTH                            1  /* OPVU */
+#define WM8400_ROZC                             0x0080  /* ROZC */
+#define WM8400_ROZC_MASK                        0x0080  /* ROZC */
+#define WM8400_ROZC_SHIFT                            7  /* ROZC */
+#define WM8400_ROZC_WIDTH                            1  /* ROZC */
+#define WM8400_ROUTVOL_MASK                     0x007F  /* ROUTVOL - [6:0] */
+#define WM8400_ROUTVOL_SHIFT                         0  /* ROUTVOL - [6:0] */
+#define WM8400_ROUTVOL_WIDTH                         7  /* ROUTVOL - [6:0] */
+
+/*
+ * R30 (0x1E) - Line Outputs Volume
+ */
+#define WM8400_LONMUTE                          0x0040  /* LONMUTE */
+#define WM8400_LONMUTE_MASK                     0x0040  /* LONMUTE */
+#define WM8400_LONMUTE_SHIFT                         6  /* LONMUTE */
+#define WM8400_LONMUTE_WIDTH                         1  /* LONMUTE */
+#define WM8400_LOPMUTE                          0x0020  /* LOPMUTE */
+#define WM8400_LOPMUTE_MASK                     0x0020  /* LOPMUTE */
+#define WM8400_LOPMUTE_SHIFT                         5  /* LOPMUTE */
+#define WM8400_LOPMUTE_WIDTH                         1  /* LOPMUTE */
+#define WM8400_LOATTN                           0x0010  /* LOATTN */
+#define WM8400_LOATTN_MASK                      0x0010  /* LOATTN */
+#define WM8400_LOATTN_SHIFT                          4  /* LOATTN */
+#define WM8400_LOATTN_WIDTH                          1  /* LOATTN */
+#define WM8400_RONMUTE                          0x0004  /* RONMUTE */
+#define WM8400_RONMUTE_MASK                     0x0004  /* RONMUTE */
+#define WM8400_RONMUTE_SHIFT                         2  /* RONMUTE */
+#define WM8400_RONMUTE_WIDTH                         1  /* RONMUTE */
+#define WM8400_ROPMUTE                          0x0002  /* ROPMUTE */
+#define WM8400_ROPMUTE_MASK                     0x0002  /* ROPMUTE */
+#define WM8400_ROPMUTE_SHIFT                         1  /* ROPMUTE */
+#define WM8400_ROPMUTE_WIDTH                         1  /* ROPMUTE */
+#define WM8400_ROATTN                           0x0001  /* ROATTN */
+#define WM8400_ROATTN_MASK                      0x0001  /* ROATTN */
+#define WM8400_ROATTN_SHIFT                          0  /* ROATTN */
+#define WM8400_ROATTN_WIDTH                          1  /* ROATTN */
+
+/*
+ * R31 (0x1F) - Out3/4 Volume
+ */
+#define WM8400_OUT3MUTE                         0x0020  /* OUT3MUTE */
+#define WM8400_OUT3MUTE_MASK                    0x0020  /* OUT3MUTE */
+#define WM8400_OUT3MUTE_SHIFT                        5  /* OUT3MUTE */
+#define WM8400_OUT3MUTE_WIDTH                        1  /* OUT3MUTE */
+#define WM8400_OUT3ATTN                         0x0010  /* OUT3ATTN */
+#define WM8400_OUT3ATTN_MASK                    0x0010  /* OUT3ATTN */
+#define WM8400_OUT3ATTN_SHIFT                        4  /* OUT3ATTN */
+#define WM8400_OUT3ATTN_WIDTH                        1  /* OUT3ATTN */
+#define WM8400_OUT4MUTE                         0x0002  /* OUT4MUTE */
+#define WM8400_OUT4MUTE_MASK                    0x0002  /* OUT4MUTE */
+#define WM8400_OUT4MUTE_SHIFT                        1  /* OUT4MUTE */
+#define WM8400_OUT4MUTE_WIDTH                        1  /* OUT4MUTE */
+#define WM8400_OUT4ATTN                         0x0001  /* OUT4ATTN */
+#define WM8400_OUT4ATTN_MASK                    0x0001  /* OUT4ATTN */
+#define WM8400_OUT4ATTN_SHIFT                        0  /* OUT4ATTN */
+#define WM8400_OUT4ATTN_WIDTH                        1  /* OUT4ATTN */
+
+/*
+ * R32 (0x20) - Left OPGA Volume
+ */
+#define WM8400_OPVU                             0x0100  /* OPVU */
+#define WM8400_OPVU_MASK                        0x0100  /* OPVU */
+#define WM8400_OPVU_SHIFT                            8  /* OPVU */
+#define WM8400_OPVU_WIDTH                            1  /* OPVU */
+#define WM8400_LOPGAZC                          0x0080  /* LOPGAZC */
+#define WM8400_LOPGAZC_MASK                     0x0080  /* LOPGAZC */
+#define WM8400_LOPGAZC_SHIFT                         7  /* LOPGAZC */
+#define WM8400_LOPGAZC_WIDTH                         1  /* LOPGAZC */
+#define WM8400_LOPGAVOL_MASK                    0x007F  /* LOPGAVOL - [6:0] */
+#define WM8400_LOPGAVOL_SHIFT                        0  /* LOPGAVOL - [6:0] */
+#define WM8400_LOPGAVOL_WIDTH                        7  /* LOPGAVOL - [6:0] */
+
+/*
+ * R33 (0x21) - Right OPGA Volume
+ */
+#define WM8400_OPVU                             0x0100  /* OPVU */
+#define WM8400_OPVU_MASK                        0x0100  /* OPVU */
+#define WM8400_OPVU_SHIFT                            8  /* OPVU */
+#define WM8400_OPVU_WIDTH                            1  /* OPVU */
+#define WM8400_ROPGAZC                          0x0080  /* ROPGAZC */
+#define WM8400_ROPGAZC_MASK                     0x0080  /* ROPGAZC */
+#define WM8400_ROPGAZC_SHIFT                         7  /* ROPGAZC */
+#define WM8400_ROPGAZC_WIDTH                         1  /* ROPGAZC */
+#define WM8400_ROPGAVOL_MASK                    0x007F  /* ROPGAVOL - [6:0] */
+#define WM8400_ROPGAVOL_SHIFT                        0  /* ROPGAVOL - [6:0] */
+#define WM8400_ROPGAVOL_WIDTH                        7  /* ROPGAVOL - [6:0] */
+
+/*
+ * R34 (0x22) - Speaker Volume
+ */
+#define WM8400_SPKATTN_MASK                     0x0003  /* SPKATTN - [1:0] */
+#define WM8400_SPKATTN_SHIFT                         0  /* SPKATTN - [1:0] */
+#define WM8400_SPKATTN_WIDTH                         2  /* SPKATTN - [1:0] */
+
+/*
+ * R35 (0x23) - ClassD1
+ */
+#define WM8400_CDMODE                           0x0100  /* CDMODE */
+#define WM8400_CDMODE_MASK                      0x0100  /* CDMODE */
+#define WM8400_CDMODE_SHIFT                          8  /* CDMODE */
+#define WM8400_CDMODE_WIDTH                          1  /* CDMODE */
+#define WM8400_CLASSD_CLK_SEL                   0x0080  /* CLASSD_CLK_SEL */
+#define WM8400_CLASSD_CLK_SEL_MASK              0x0080  /* CLASSD_CLK_SEL */
+#define WM8400_CLASSD_CLK_SEL_SHIFT                  7  /* CLASSD_CLK_SEL */
+#define WM8400_CLASSD_CLK_SEL_WIDTH                  1  /* CLASSD_CLK_SEL */
+#define WM8400_CD_SRCTRL                        0x0040  /* CD_SRCTRL */
+#define WM8400_CD_SRCTRL_MASK                   0x0040  /* CD_SRCTRL */
+#define WM8400_CD_SRCTRL_SHIFT                       6  /* CD_SRCTRL */
+#define WM8400_CD_SRCTRL_WIDTH                       1  /* CD_SRCTRL */
+#define WM8400_SPKNOPOP                         0x0020  /* SPKNOPOP */
+#define WM8400_SPKNOPOP_MASK                    0x0020  /* SPKNOPOP */
+#define WM8400_SPKNOPOP_SHIFT                        5  /* SPKNOPOP */
+#define WM8400_SPKNOPOP_WIDTH                        1  /* SPKNOPOP */
+#define WM8400_DBLERATE                         0x0010  /* DBLERATE */
+#define WM8400_DBLERATE_MASK                    0x0010  /* DBLERATE */
+#define WM8400_DBLERATE_SHIFT                        4  /* DBLERATE */
+#define WM8400_DBLERATE_WIDTH                        1  /* DBLERATE */
+#define WM8400_LOOPTEST                         0x0008  /* LOOPTEST */
+#define WM8400_LOOPTEST_MASK                    0x0008  /* LOOPTEST */
+#define WM8400_LOOPTEST_SHIFT                        3  /* LOOPTEST */
+#define WM8400_LOOPTEST_WIDTH                        1  /* LOOPTEST */
+#define WM8400_HALFABBIAS                       0x0004  /* HALFABBIAS */
+#define WM8400_HALFABBIAS_MASK                  0x0004  /* HALFABBIAS */
+#define WM8400_HALFABBIAS_SHIFT                      2  /* HALFABBIAS */
+#define WM8400_HALFABBIAS_WIDTH                      1  /* HALFABBIAS */
+#define WM8400_TRIDEL_MASK                      0x0003  /* TRIDEL - [1:0] */
+#define WM8400_TRIDEL_SHIFT                          0  /* TRIDEL - [1:0] */
+#define WM8400_TRIDEL_WIDTH                          2  /* TRIDEL - [1:0] */
+
+/*
+ * R37 (0x25) - ClassD3
+ */
+#define WM8400_DCGAIN_MASK                      0x0038  /* DCGAIN - [5:3] */
+#define WM8400_DCGAIN_SHIFT                          3  /* DCGAIN - [5:3] */
+#define WM8400_DCGAIN_WIDTH                          3  /* DCGAIN - [5:3] */
+#define WM8400_ACGAIN_MASK                      0x0007  /* ACGAIN - [2:0] */
+#define WM8400_ACGAIN_SHIFT                          0  /* ACGAIN - [2:0] */
+#define WM8400_ACGAIN_WIDTH                          3  /* ACGAIN - [2:0] */
+
+/*
+ * R39 (0x27) - Input Mixer1
+ */
+#define WM8400_AINLMODE_MASK                    0x000C  /* AINLMODE - [3:2] */
+#define WM8400_AINLMODE_SHIFT                        2  /* AINLMODE - [3:2] */
+#define WM8400_AINLMODE_WIDTH                        2  /* AINLMODE - [3:2] */
+#define WM8400_AINRMODE_MASK                    0x0003  /* AINRMODE - [1:0] */
+#define WM8400_AINRMODE_SHIFT                        0  /* AINRMODE - [1:0] */
+#define WM8400_AINRMODE_WIDTH                        2  /* AINRMODE - [1:0] */
+
+/*
+ * R40 (0x28) - Input Mixer2
+ */
+#define WM8400_LMP4                             0x0080  /* LMP4 */
+#define WM8400_LMP4_MASK                        0x0080  /* LMP4 */
+#define WM8400_LMP4_SHIFT                            7  /* LMP4 */
+#define WM8400_LMP4_WIDTH                            1  /* LMP4 */
+#define WM8400_LMN3                             0x0040  /* LMN3 */
+#define WM8400_LMN3_MASK                        0x0040  /* LMN3 */
+#define WM8400_LMN3_SHIFT                            6  /* LMN3 */
+#define WM8400_LMN3_WIDTH                            1  /* LMN3 */
+#define WM8400_LMP2                             0x0020  /* LMP2 */
+#define WM8400_LMP2_MASK                        0x0020  /* LMP2 */
+#define WM8400_LMP2_SHIFT                            5  /* LMP2 */
+#define WM8400_LMP2_WIDTH                            1  /* LMP2 */
+#define WM8400_LMN1                             0x0010  /* LMN1 */
+#define WM8400_LMN1_MASK                        0x0010  /* LMN1 */
+#define WM8400_LMN1_SHIFT                            4  /* LMN1 */
+#define WM8400_LMN1_WIDTH                            1  /* LMN1 */
+#define WM8400_RMP4                             0x0008  /* RMP4 */
+#define WM8400_RMP4_MASK                        0x0008  /* RMP4 */
+#define WM8400_RMP4_SHIFT                            3  /* RMP4 */
+#define WM8400_RMP4_WIDTH                            1  /* RMP4 */
+#define WM8400_RMN3                             0x0004  /* RMN3 */
+#define WM8400_RMN3_MASK                        0x0004  /* RMN3 */
+#define WM8400_RMN3_SHIFT                            2  /* RMN3 */
+#define WM8400_RMN3_WIDTH                            1  /* RMN3 */
+#define WM8400_RMP2                             0x0002  /* RMP2 */
+#define WM8400_RMP2_MASK                        0x0002  /* RMP2 */
+#define WM8400_RMP2_SHIFT                            1  /* RMP2 */
+#define WM8400_RMP2_WIDTH                            1  /* RMP2 */
+#define WM8400_RMN1                             0x0001  /* RMN1 */
+#define WM8400_RMN1_MASK                        0x0001  /* RMN1 */
+#define WM8400_RMN1_SHIFT                            0  /* RMN1 */
+#define WM8400_RMN1_WIDTH                            1  /* RMN1 */
+
+/*
+ * R41 (0x29) - Input Mixer3
+ */
+#define WM8400_L34MNB                           0x0100  /* L34MNB */
+#define WM8400_L34MNB_MASK                      0x0100  /* L34MNB */
+#define WM8400_L34MNB_SHIFT                          8  /* L34MNB */
+#define WM8400_L34MNB_WIDTH                          1  /* L34MNB */
+#define WM8400_L34MNBST                         0x0080  /* L34MNBST */
+#define WM8400_L34MNBST_MASK                    0x0080  /* L34MNBST */
+#define WM8400_L34MNBST_SHIFT                        7  /* L34MNBST */
+#define WM8400_L34MNBST_WIDTH                        1  /* L34MNBST */
+#define WM8400_L12MNB                           0x0020  /* L12MNB */
+#define WM8400_L12MNB_MASK                      0x0020  /* L12MNB */
+#define WM8400_L12MNB_SHIFT                          5  /* L12MNB */
+#define WM8400_L12MNB_WIDTH                          1  /* L12MNB */
+#define WM8400_L12MNBST                         0x0010  /* L12MNBST */
+#define WM8400_L12MNBST_MASK                    0x0010  /* L12MNBST */
+#define WM8400_L12MNBST_SHIFT                        4  /* L12MNBST */
+#define WM8400_L12MNBST_WIDTH                        1  /* L12MNBST */
+#define WM8400_LDBVOL_MASK                      0x0007  /* LDBVOL - [2:0] */
+#define WM8400_LDBVOL_SHIFT                          0  /* LDBVOL - [2:0] */
+#define WM8400_LDBVOL_WIDTH                          3  /* LDBVOL - [2:0] */
+
+/*
+ * R42 (0x2A) - Input Mixer4
+ */
+#define WM8400_R34MNB                           0x0100  /* R34MNB */
+#define WM8400_R34MNB_MASK                      0x0100  /* R34MNB */
+#define WM8400_R34MNB_SHIFT                          8  /* R34MNB */
+#define WM8400_R34MNB_WIDTH                          1  /* R34MNB */
+#define WM8400_R34MNBST                         0x0080  /* R34MNBST */
+#define WM8400_R34MNBST_MASK                    0x0080  /* R34MNBST */
+#define WM8400_R34MNBST_SHIFT                        7  /* R34MNBST */
+#define WM8400_R34MNBST_WIDTH                        1  /* R34MNBST */
+#define WM8400_R12MNB                           0x0020  /* R12MNB */
+#define WM8400_R12MNB_MASK                      0x0020  /* R12MNB */
+#define WM8400_R12MNB_SHIFT                          5  /* R12MNB */
+#define WM8400_R12MNB_WIDTH                          1  /* R12MNB */
+#define WM8400_R12MNBST                         0x0010  /* R12MNBST */
+#define WM8400_R12MNBST_MASK                    0x0010  /* R12MNBST */
+#define WM8400_R12MNBST_SHIFT                        4  /* R12MNBST */
+#define WM8400_R12MNBST_WIDTH                        1  /* R12MNBST */
+#define WM8400_RDBVOL_MASK                      0x0007  /* RDBVOL - [2:0] */
+#define WM8400_RDBVOL_SHIFT                          0  /* RDBVOL - [2:0] */
+#define WM8400_RDBVOL_WIDTH                          3  /* RDBVOL - [2:0] */
+
+/*
+ * R43 (0x2B) - Input Mixer5
+ */
+#define WM8400_LI2BVOL_MASK                     0x01C0  /* LI2BVOL - [8:6] */
+#define WM8400_LI2BVOL_SHIFT                         6  /* LI2BVOL - [8:6] */
+#define WM8400_LI2BVOL_WIDTH                         3  /* LI2BVOL - [8:6] */
+#define WM8400_LR4BVOL_MASK                     0x0038  /* LR4BVOL - [5:3] */
+#define WM8400_LR4BVOL_SHIFT                         3  /* LR4BVOL - [5:3] */
+#define WM8400_LR4BVOL_WIDTH                         3  /* LR4BVOL - [5:3] */
+#define WM8400_LL4BVOL_MASK                     0x0007  /* LL4BVOL - [2:0] */
+#define WM8400_LL4BVOL_SHIFT                         0  /* LL4BVOL - [2:0] */
+#define WM8400_LL4BVOL_WIDTH                         3  /* LL4BVOL - [2:0] */
+
+/*
+ * R44 (0x2C) - Input Mixer6
+ */
+#define WM8400_RI2BVOL_MASK                     0x01C0  /* RI2BVOL - [8:6] */
+#define WM8400_RI2BVOL_SHIFT                         6  /* RI2BVOL - [8:6] */
+#define WM8400_RI2BVOL_WIDTH                         3  /* RI2BVOL - [8:6] */
+#define WM8400_RL4BVOL_MASK                     0x0038  /* RL4BVOL - [5:3] */
+#define WM8400_RL4BVOL_SHIFT                         3  /* RL4BVOL - [5:3] */
+#define WM8400_RL4BVOL_WIDTH                         3  /* RL4BVOL - [5:3] */
+#define WM8400_RR4BVOL_MASK                     0x0007  /* RR4BVOL - [2:0] */
+#define WM8400_RR4BVOL_SHIFT                         0  /* RR4BVOL - [2:0] */
+#define WM8400_RR4BVOL_WIDTH                         3  /* RR4BVOL - [2:0] */
+
+/*
+ * R45 (0x2D) - Output Mixer1
+ */
+#define WM8400_LRBLO                            0x0080  /* LRBLO */
+#define WM8400_LRBLO_MASK                       0x0080  /* LRBLO */
+#define WM8400_LRBLO_SHIFT                           7  /* LRBLO */
+#define WM8400_LRBLO_WIDTH                           1  /* LRBLO */
+#define WM8400_LLBLO                            0x0040  /* LLBLO */
+#define WM8400_LLBLO_MASK                       0x0040  /* LLBLO */
+#define WM8400_LLBLO_SHIFT                           6  /* LLBLO */
+#define WM8400_LLBLO_WIDTH                           1  /* LLBLO */
+#define WM8400_LRI3LO                           0x0020  /* LRI3LO */
+#define WM8400_LRI3LO_MASK                      0x0020  /* LRI3LO */
+#define WM8400_LRI3LO_SHIFT                          5  /* LRI3LO */
+#define WM8400_LRI3LO_WIDTH                          1  /* LRI3LO */
+#define WM8400_LLI3LO                           0x0010  /* LLI3LO */
+#define WM8400_LLI3LO_MASK                      0x0010  /* LLI3LO */
+#define WM8400_LLI3LO_SHIFT                          4  /* LLI3LO */
+#define WM8400_LLI3LO_WIDTH                          1  /* LLI3LO */
+#define WM8400_LR12LO                           0x0008  /* LR12LO */
+#define WM8400_LR12LO_MASK                      0x0008  /* LR12LO */
+#define WM8400_LR12LO_SHIFT                          3  /* LR12LO */
+#define WM8400_LR12LO_WIDTH                          1  /* LR12LO */
+#define WM8400_LL12LO                           0x0004  /* LL12LO */
+#define WM8400_LL12LO_MASK                      0x0004  /* LL12LO */
+#define WM8400_LL12LO_SHIFT                          2  /* LL12LO */
+#define WM8400_LL12LO_WIDTH                          1  /* LL12LO */
+#define WM8400_LDLO                             0x0001  /* LDLO */
+#define WM8400_LDLO_MASK                        0x0001  /* LDLO */
+#define WM8400_LDLO_SHIFT                            0  /* LDLO */
+#define WM8400_LDLO_WIDTH                            1  /* LDLO */
+
+/*
+ * R46 (0x2E) - Output Mixer2
+ */
+#define WM8400_RLBRO                            0x0080  /* RLBRO */
+#define WM8400_RLBRO_MASK                       0x0080  /* RLBRO */
+#define WM8400_RLBRO_SHIFT                           7  /* RLBRO */
+#define WM8400_RLBRO_WIDTH                           1  /* RLBRO */
+#define WM8400_RRBRO                            0x0040  /* RRBRO */
+#define WM8400_RRBRO_MASK                       0x0040  /* RRBRO */
+#define WM8400_RRBRO_SHIFT                           6  /* RRBRO */
+#define WM8400_RRBRO_WIDTH                           1  /* RRBRO */
+#define WM8400_RLI3RO                           0x0020  /* RLI3RO */
+#define WM8400_RLI3RO_MASK                      0x0020  /* RLI3RO */
+#define WM8400_RLI3RO_SHIFT                          5  /* RLI3RO */
+#define WM8400_RLI3RO_WIDTH                          1  /* RLI3RO */
+#define WM8400_RRI3RO                           0x0010  /* RRI3RO */
+#define WM8400_RRI3RO_MASK                      0x0010  /* RRI3RO */
+#define WM8400_RRI3RO_SHIFT                          4  /* RRI3RO */
+#define WM8400_RRI3RO_WIDTH                          1  /* RRI3RO */
+#define WM8400_RL12RO                           0x0008  /* RL12RO */
+#define WM8400_RL12RO_MASK                      0x0008  /* RL12RO */
+#define WM8400_RL12RO_SHIFT                          3  /* RL12RO */
+#define WM8400_RL12RO_WIDTH                          1  /* RL12RO */
+#define WM8400_RR12RO                           0x0004  /* RR12RO */
+#define WM8400_RR12RO_MASK                      0x0004  /* RR12RO */
+#define WM8400_RR12RO_SHIFT                          2  /* RR12RO */
+#define WM8400_RR12RO_WIDTH                          1  /* RR12RO */
+#define WM8400_RDRO                             0x0001  /* RDRO */
+#define WM8400_RDRO_MASK                        0x0001  /* RDRO */
+#define WM8400_RDRO_SHIFT                            0  /* RDRO */
+#define WM8400_RDRO_WIDTH                            1  /* RDRO */
+
+/*
+ * R47 (0x2F) - Output Mixer3
+ */
+#define WM8400_LLI3LOVOL_MASK                   0x01C0  /* LLI3LOVOL - [8:6] */
+#define WM8400_LLI3LOVOL_SHIFT                       6  /* LLI3LOVOL - [8:6] */
+#define WM8400_LLI3LOVOL_WIDTH                       3  /* LLI3LOVOL - [8:6] */
+#define WM8400_LR12LOVOL_MASK                   0x0038  /* LR12LOVOL - [5:3] */
+#define WM8400_LR12LOVOL_SHIFT                       3  /* LR12LOVOL - [5:3] */
+#define WM8400_LR12LOVOL_WIDTH                       3  /* LR12LOVOL - [5:3] */
+#define WM8400_LL12LOVOL_MASK                   0x0007  /* LL12LOVOL - [2:0] */
+#define WM8400_LL12LOVOL_SHIFT                       0  /* LL12LOVOL - [2:0] */
+#define WM8400_LL12LOVOL_WIDTH                       3  /* LL12LOVOL - [2:0] */
+
+/*
+ * R48 (0x30) - Output Mixer4
+ */
+#define WM8400_RRI3ROVOL_MASK                   0x01C0  /* RRI3ROVOL - [8:6] */
+#define WM8400_RRI3ROVOL_SHIFT                       6  /* RRI3ROVOL - [8:6] */
+#define WM8400_RRI3ROVOL_WIDTH                       3  /* RRI3ROVOL - [8:6] */
+#define WM8400_RL12ROVOL_MASK                   0x0038  /* RL12ROVOL - [5:3] */
+#define WM8400_RL12ROVOL_SHIFT                       3  /* RL12ROVOL - [5:3] */
+#define WM8400_RL12ROVOL_WIDTH                       3  /* RL12ROVOL - [5:3] */
+#define WM8400_RR12ROVOL_MASK                   0x0007  /* RR12ROVOL - [2:0] */
+#define WM8400_RR12ROVOL_SHIFT                       0  /* RR12ROVOL - [2:0] */
+#define WM8400_RR12ROVOL_WIDTH                       3  /* RR12ROVOL - [2:0] */
+
+/*
+ * R49 (0x31) - Output Mixer5
+ */
+#define WM8400_LRI3LOVOL_MASK                   0x01C0  /* LRI3LOVOL - [8:6] */
+#define WM8400_LRI3LOVOL_SHIFT                       6  /* LRI3LOVOL - [8:6] */
+#define WM8400_LRI3LOVOL_WIDTH                       3  /* LRI3LOVOL - [8:6] */
+#define WM8400_LRBLOVOL_MASK                    0x0038  /* LRBLOVOL - [5:3] */
+#define WM8400_LRBLOVOL_SHIFT                        3  /* LRBLOVOL - [5:3] */
+#define WM8400_LRBLOVOL_WIDTH                        3  /* LRBLOVOL - [5:3] */
+#define WM8400_LLBLOVOL_MASK                    0x0007  /* LLBLOVOL - [2:0] */
+#define WM8400_LLBLOVOL_SHIFT                        0  /* LLBLOVOL - [2:0] */
+#define WM8400_LLBLOVOL_WIDTH                        3  /* LLBLOVOL - [2:0] */
+
+/*
+ * R50 (0x32) - Output Mixer6
+ */
+#define WM8400_RLI3ROVOL_MASK                   0x01C0  /* RLI3ROVOL - [8:6] */
+#define WM8400_RLI3ROVOL_SHIFT                       6  /* RLI3ROVOL - [8:6] */
+#define WM8400_RLI3ROVOL_WIDTH                       3  /* RLI3ROVOL - [8:6] */
+#define WM8400_RLBROVOL_MASK                    0x0038  /* RLBROVOL - [5:3] */
+#define WM8400_RLBROVOL_SHIFT                        3  /* RLBROVOL - [5:3] */
+#define WM8400_RLBROVOL_WIDTH                        3  /* RLBROVOL - [5:3] */
+#define WM8400_RRBROVOL_MASK                    0x0007  /* RRBROVOL - [2:0] */
+#define WM8400_RRBROVOL_SHIFT                        0  /* RRBROVOL - [2:0] */
+#define WM8400_RRBROVOL_WIDTH                        3  /* RRBROVOL - [2:0] */
+
+/*
+ * R51 (0x33) - Out3/4 Mixer
+ */
+#define WM8400_VSEL_MASK                        0x0180  /* VSEL - [8:7] */
+#define WM8400_VSEL_SHIFT                            7  /* VSEL - [8:7] */
+#define WM8400_VSEL_WIDTH                            2  /* VSEL - [8:7] */
+#define WM8400_LI4O3                            0x0020  /* LI4O3 */
+#define WM8400_LI4O3_MASK                       0x0020  /* LI4O3 */
+#define WM8400_LI4O3_SHIFT                           5  /* LI4O3 */
+#define WM8400_LI4O3_WIDTH                           1  /* LI4O3 */
+#define WM8400_LPGAO3                           0x0010  /* LPGAO3 */
+#define WM8400_LPGAO3_MASK                      0x0010  /* LPGAO3 */
+#define WM8400_LPGAO3_SHIFT                          4  /* LPGAO3 */
+#define WM8400_LPGAO3_WIDTH                          1  /* LPGAO3 */
+#define WM8400_RI4O4                            0x0002  /* RI4O4 */
+#define WM8400_RI4O4_MASK                       0x0002  /* RI4O4 */
+#define WM8400_RI4O4_SHIFT                           1  /* RI4O4 */
+#define WM8400_RI4O4_WIDTH                           1  /* RI4O4 */
+#define WM8400_RPGAO4                           0x0001  /* RPGAO4 */
+#define WM8400_RPGAO4_MASK                      0x0001  /* RPGAO4 */
+#define WM8400_RPGAO4_SHIFT                          0  /* RPGAO4 */
+#define WM8400_RPGAO4_WIDTH                          1  /* RPGAO4 */
+
+/*
+ * R52 (0x34) - Line Mixer1
+ */
+#define WM8400_LLOPGALON                        0x0040  /* LLOPGALON */
+#define WM8400_LLOPGALON_MASK                   0x0040  /* LLOPGALON */
+#define WM8400_LLOPGALON_SHIFT                       6  /* LLOPGALON */
+#define WM8400_LLOPGALON_WIDTH                       1  /* LLOPGALON */
+#define WM8400_LROPGALON                        0x0020  /* LROPGALON */
+#define WM8400_LROPGALON_MASK                   0x0020  /* LROPGALON */
+#define WM8400_LROPGALON_SHIFT                       5  /* LROPGALON */
+#define WM8400_LROPGALON_WIDTH                       1  /* LROPGALON */
+#define WM8400_LOPLON                           0x0010  /* LOPLON */
+#define WM8400_LOPLON_MASK                      0x0010  /* LOPLON */
+#define WM8400_LOPLON_SHIFT                          4  /* LOPLON */
+#define WM8400_LOPLON_WIDTH                          1  /* LOPLON */
+#define WM8400_LR12LOP                          0x0004  /* LR12LOP */
+#define WM8400_LR12LOP_MASK                     0x0004  /* LR12LOP */
+#define WM8400_LR12LOP_SHIFT                         2  /* LR12LOP */
+#define WM8400_LR12LOP_WIDTH                         1  /* LR12LOP */
+#define WM8400_LL12LOP                          0x0002  /* LL12LOP */
+#define WM8400_LL12LOP_MASK                     0x0002  /* LL12LOP */
+#define WM8400_LL12LOP_SHIFT                         1  /* LL12LOP */
+#define WM8400_LL12LOP_WIDTH                         1  /* LL12LOP */
+#define WM8400_LLOPGALOP                        0x0001  /* LLOPGALOP */
+#define WM8400_LLOPGALOP_MASK                   0x0001  /* LLOPGALOP */
+#define WM8400_LLOPGALOP_SHIFT                       0  /* LLOPGALOP */
+#define WM8400_LLOPGALOP_WIDTH                       1  /* LLOPGALOP */
+
+/*
+ * R53 (0x35) - Line Mixer2
+ */
+#define WM8400_RROPGARON                        0x0040  /* RROPGARON */
+#define WM8400_RROPGARON_MASK                   0x0040  /* RROPGARON */
+#define WM8400_RROPGARON_SHIFT                       6  /* RROPGARON */
+#define WM8400_RROPGARON_WIDTH                       1  /* RROPGARON */
+#define WM8400_RLOPGARON                        0x0020  /* RLOPGARON */
+#define WM8400_RLOPGARON_MASK                   0x0020  /* RLOPGARON */
+#define WM8400_RLOPGARON_SHIFT                       5  /* RLOPGARON */
+#define WM8400_RLOPGARON_WIDTH                       1  /* RLOPGARON */
+#define WM8400_ROPRON                           0x0010  /* ROPRON */
+#define WM8400_ROPRON_MASK                      0x0010  /* ROPRON */
+#define WM8400_ROPRON_SHIFT                          4  /* ROPRON */
+#define WM8400_ROPRON_WIDTH                          1  /* ROPRON */
+#define WM8400_RL12ROP                          0x0004  /* RL12ROP */
+#define WM8400_RL12ROP_MASK                     0x0004  /* RL12ROP */
+#define WM8400_RL12ROP_SHIFT                         2  /* RL12ROP */
+#define WM8400_RL12ROP_WIDTH                         1  /* RL12ROP */
+#define WM8400_RR12ROP                          0x0002  /* RR12ROP */
+#define WM8400_RR12ROP_MASK                     0x0002  /* RR12ROP */
+#define WM8400_RR12ROP_SHIFT                         1  /* RR12ROP */
+#define WM8400_RR12ROP_WIDTH                         1  /* RR12ROP */
+#define WM8400_RROPGAROP                        0x0001  /* RROPGAROP */
+#define WM8400_RROPGAROP_MASK                   0x0001  /* RROPGAROP */
+#define WM8400_RROPGAROP_SHIFT                       0  /* RROPGAROP */
+#define WM8400_RROPGAROP_WIDTH                       1  /* RROPGAROP */
+
+/*
+ * R54 (0x36) - Speaker Mixer
+ */
+#define WM8400_LB2SPK                           0x0080  /* LB2SPK */
+#define WM8400_LB2SPK_MASK                      0x0080  /* LB2SPK */
+#define WM8400_LB2SPK_SHIFT                          7  /* LB2SPK */
+#define WM8400_LB2SPK_WIDTH                          1  /* LB2SPK */
+#define WM8400_RB2SPK                           0x0040  /* RB2SPK */
+#define WM8400_RB2SPK_MASK                      0x0040  /* RB2SPK */
+#define WM8400_RB2SPK_SHIFT                          6  /* RB2SPK */
+#define WM8400_RB2SPK_WIDTH                          1  /* RB2SPK */
+#define WM8400_LI2SPK                           0x0020  /* LI2SPK */
+#define WM8400_LI2SPK_MASK                      0x0020  /* LI2SPK */
+#define WM8400_LI2SPK_SHIFT                          5  /* LI2SPK */
+#define WM8400_LI2SPK_WIDTH                          1  /* LI2SPK */
+#define WM8400_RI2SPK                           0x0010  /* RI2SPK */
+#define WM8400_RI2SPK_MASK                      0x0010  /* RI2SPK */
+#define WM8400_RI2SPK_SHIFT                          4  /* RI2SPK */
+#define WM8400_RI2SPK_WIDTH                          1  /* RI2SPK */
+#define WM8400_LOPGASPK                         0x0008  /* LOPGASPK */
+#define WM8400_LOPGASPK_MASK                    0x0008  /* LOPGASPK */
+#define WM8400_LOPGASPK_SHIFT                        3  /* LOPGASPK */
+#define WM8400_LOPGASPK_WIDTH                        1  /* LOPGASPK */
+#define WM8400_ROPGASPK                         0x0004  /* ROPGASPK */
+#define WM8400_ROPGASPK_MASK                    0x0004  /* ROPGASPK */
+#define WM8400_ROPGASPK_SHIFT                        2  /* ROPGASPK */
+#define WM8400_ROPGASPK_WIDTH                        1  /* ROPGASPK */
+#define WM8400_LDSPK                            0x0002  /* LDSPK */
+#define WM8400_LDSPK_MASK                       0x0002  /* LDSPK */
+#define WM8400_LDSPK_SHIFT                           1  /* LDSPK */
+#define WM8400_LDSPK_WIDTH                           1  /* LDSPK */
+#define WM8400_RDSPK                            0x0001  /* RDSPK */
+#define WM8400_RDSPK_MASK                       0x0001  /* RDSPK */
+#define WM8400_RDSPK_SHIFT                           0  /* RDSPK */
+#define WM8400_RDSPK_WIDTH                           1  /* RDSPK */
+
+/*
+ * R55 (0x37) - Additional Control
+ */
+#define WM8400_VROI                             0x0001  /* VROI */
+#define WM8400_VROI_MASK                        0x0001  /* VROI */
+#define WM8400_VROI_SHIFT                            0  /* VROI */
+#define WM8400_VROI_WIDTH                            1  /* VROI */
+
+/*
+ * R56 (0x38) - AntiPOP1
+ */
+#define WM8400_DIS_LLINE                        0x0020  /* DIS_LLINE */
+#define WM8400_DIS_LLINE_MASK                   0x0020  /* DIS_LLINE */
+#define WM8400_DIS_LLINE_SHIFT                       5  /* DIS_LLINE */
+#define WM8400_DIS_LLINE_WIDTH                       1  /* DIS_LLINE */
+#define WM8400_DIS_RLINE                        0x0010  /* DIS_RLINE */
+#define WM8400_DIS_RLINE_MASK                   0x0010  /* DIS_RLINE */
+#define WM8400_DIS_RLINE_SHIFT                       4  /* DIS_RLINE */
+#define WM8400_DIS_RLINE_WIDTH                       1  /* DIS_RLINE */
+#define WM8400_DIS_OUT3                         0x0008  /* DIS_OUT3 */
+#define WM8400_DIS_OUT3_MASK                    0x0008  /* DIS_OUT3 */
+#define WM8400_DIS_OUT3_SHIFT                        3  /* DIS_OUT3 */
+#define WM8400_DIS_OUT3_WIDTH                        1  /* DIS_OUT3 */
+#define WM8400_DIS_OUT4                         0x0004  /* DIS_OUT4 */
+#define WM8400_DIS_OUT4_MASK                    0x0004  /* DIS_OUT4 */
+#define WM8400_DIS_OUT4_SHIFT                        2  /* DIS_OUT4 */
+#define WM8400_DIS_OUT4_WIDTH                        1  /* DIS_OUT4 */
+#define WM8400_DIS_LOUT                         0x0002  /* DIS_LOUT */
+#define WM8400_DIS_LOUT_MASK                    0x0002  /* DIS_LOUT */
+#define WM8400_DIS_LOUT_SHIFT                        1  /* DIS_LOUT */
+#define WM8400_DIS_LOUT_WIDTH                        1  /* DIS_LOUT */
+#define WM8400_DIS_ROUT                         0x0001  /* DIS_ROUT */
+#define WM8400_DIS_ROUT_MASK                    0x0001  /* DIS_ROUT */
+#define WM8400_DIS_ROUT_SHIFT                        0  /* DIS_ROUT */
+#define WM8400_DIS_ROUT_WIDTH                        1  /* DIS_ROUT */
+
+/*
+ * R57 (0x39) - AntiPOP2
+ */
+#define WM8400_SOFTST                           0x0040  /* SOFTST */
+#define WM8400_SOFTST_MASK                      0x0040  /* SOFTST */
+#define WM8400_SOFTST_SHIFT                          6  /* SOFTST */
+#define WM8400_SOFTST_WIDTH                          1  /* SOFTST */
+#define WM8400_BUFIOEN                          0x0008  /* BUFIOEN */
+#define WM8400_BUFIOEN_MASK                     0x0008  /* BUFIOEN */
+#define WM8400_BUFIOEN_SHIFT                         3  /* BUFIOEN */
+#define WM8400_BUFIOEN_WIDTH                         1  /* BUFIOEN */
+#define WM8400_BUFDCOPEN                        0x0004  /* BUFDCOPEN */
+#define WM8400_BUFDCOPEN_MASK                   0x0004  /* BUFDCOPEN */
+#define WM8400_BUFDCOPEN_SHIFT                       2  /* BUFDCOPEN */
+#define WM8400_BUFDCOPEN_WIDTH                       1  /* BUFDCOPEN */
+#define WM8400_POBCTRL                          0x0002  /* POBCTRL */
+#define WM8400_POBCTRL_MASK                     0x0002  /* POBCTRL */
+#define WM8400_POBCTRL_SHIFT                         1  /* POBCTRL */
+#define WM8400_POBCTRL_WIDTH                         1  /* POBCTRL */
+#define WM8400_VMIDTOG                          0x0001  /* VMIDTOG */
+#define WM8400_VMIDTOG_MASK                     0x0001  /* VMIDTOG */
+#define WM8400_VMIDTOG_SHIFT                         0  /* VMIDTOG */
+#define WM8400_VMIDTOG_WIDTH                         1  /* VMIDTOG */
+
+/*
+ * R58 (0x3A) - MICBIAS
+ */
+#define WM8400_MCDSCTH_MASK                     0x00C0  /* MCDSCTH - [7:6] */
+#define WM8400_MCDSCTH_SHIFT                         6  /* MCDSCTH - [7:6] */
+#define WM8400_MCDSCTH_WIDTH                         2  /* MCDSCTH - [7:6] */
+#define WM8400_MCDTHR_MASK                      0x0038  /* MCDTHR - [5:3] */
+#define WM8400_MCDTHR_SHIFT                          3  /* MCDTHR - [5:3] */
+#define WM8400_MCDTHR_WIDTH                          3  /* MCDTHR - [5:3] */
+#define WM8400_MCD                              0x0004  /* MCD */
+#define WM8400_MCD_MASK                         0x0004  /* MCD */
+#define WM8400_MCD_SHIFT                             2  /* MCD */
+#define WM8400_MCD_WIDTH                             1  /* MCD */
+#define WM8400_MBSEL                            0x0001  /* MBSEL */
+#define WM8400_MBSEL_MASK                       0x0001  /* MBSEL */
+#define WM8400_MBSEL_SHIFT                           0  /* MBSEL */
+#define WM8400_MBSEL_WIDTH                           1  /* MBSEL */
+
+/*
+ * R60 (0x3C) - FLL Control 1
+ */
+#define WM8400_FLL_REF_FREQ                     0x1000  /* FLL_REF_FREQ */
+#define WM8400_FLL_REF_FREQ_MASK                0x1000  /* FLL_REF_FREQ */
+#define WM8400_FLL_REF_FREQ_SHIFT                   12  /* FLL_REF_FREQ */
+#define WM8400_FLL_REF_FREQ_WIDTH                    1  /* FLL_REF_FREQ */
+#define WM8400_FLL_CLK_SRC_MASK                 0x0C00  /* FLL_CLK_SRC - [11:10] */
+#define WM8400_FLL_CLK_SRC_SHIFT                    10  /* FLL_CLK_SRC - [11:10] */
+#define WM8400_FLL_CLK_SRC_WIDTH                     2  /* FLL_CLK_SRC - [11:10] */
+#define WM8400_FLL_FRAC                         0x0200  /* FLL_FRAC */
+#define WM8400_FLL_FRAC_MASK                    0x0200  /* FLL_FRAC */
+#define WM8400_FLL_FRAC_SHIFT                        9  /* FLL_FRAC */
+#define WM8400_FLL_FRAC_WIDTH                        1  /* FLL_FRAC */
+#define WM8400_FLL_OSC_ENA                      0x0100  /* FLL_OSC_ENA */
+#define WM8400_FLL_OSC_ENA_MASK                 0x0100  /* FLL_OSC_ENA */
+#define WM8400_FLL_OSC_ENA_SHIFT                     8  /* FLL_OSC_ENA */
+#define WM8400_FLL_OSC_ENA_WIDTH                     1  /* FLL_OSC_ENA */
+#define WM8400_FLL_CTRL_RATE_MASK               0x00E0  /* FLL_CTRL_RATE - [7:5] */
+#define WM8400_FLL_CTRL_RATE_SHIFT                   5  /* FLL_CTRL_RATE - [7:5] */
+#define WM8400_FLL_CTRL_RATE_WIDTH                   3  /* FLL_CTRL_RATE - [7:5] */
+#define WM8400_FLL_FRATIO_MASK                  0x001F  /* FLL_FRATIO - [4:0] */
+#define WM8400_FLL_FRATIO_SHIFT                      0  /* FLL_FRATIO - [4:0] */
+#define WM8400_FLL_FRATIO_WIDTH                      5  /* FLL_FRATIO - [4:0] */
+
+/*
+ * R61 (0x3D) - FLL Control 2
+ */
+#define WM8400_FLL_K_MASK                       0xFFFF  /* FLL_K - [15:0] */
+#define WM8400_FLL_K_SHIFT                           0  /* FLL_K - [15:0] */
+#define WM8400_FLL_K_WIDTH                          16  /* FLL_K - [15:0] */
+
+/*
+ * R62 (0x3E) - FLL Control 3
+ */
+#define WM8400_FLL_N_MASK                       0x03FF  /* FLL_N - [9:0] */
+#define WM8400_FLL_N_SHIFT                           0  /* FLL_N - [9:0] */
+#define WM8400_FLL_N_WIDTH                          10  /* FLL_N - [9:0] */
+
+/*
+ * R63 (0x3F) - FLL Control 4
+ */
+#define WM8400_FLL_TRK_GAIN_MASK                0x0078  /* FLL_TRK_GAIN - [6:3] */
+#define WM8400_FLL_TRK_GAIN_SHIFT                    3  /* FLL_TRK_GAIN - [6:3] */
+#define WM8400_FLL_TRK_GAIN_WIDTH                    4  /* FLL_TRK_GAIN - [6:3] */
+#define WM8400_FLL_OUTDIV_MASK                  0x0007  /* FLL_OUTDIV - [2:0] */
+#define WM8400_FLL_OUTDIV_SHIFT                      0  /* FLL_OUTDIV - [2:0] */
+#define WM8400_FLL_OUTDIV_WIDTH                      3  /* FLL_OUTDIV - [2:0] */
+
+void wm8400_reset_codec_reg_cache(struct wm8400 *wm8400);
+
+#endif
diff --git a/include/linux/mfd/wm8400-private.h b/include/linux/mfd/wm8400-private.h
new file mode 100644 (file)
index 0000000..2aab4e9
--- /dev/null
@@ -0,0 +1,936 @@
+/*
+ * wm8400 private definitions.
+ *
+ * Copyright 2008 Wolfson Microelectronics plc
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef __LINUX_MFD_WM8400_PRIV_H
+#define __LINUX_MFD_WM8400_PRIV_H
+
+#include <linux/mfd/wm8400.h>
+#include <linux/mutex.h>
+#include <linux/platform_device.h>
+
+#define WM8400_REGISTER_COUNT 0x55
+
+struct wm8400 {
+       struct device *dev;
+
+       int (*read_dev)(void *data, char reg, int count, u16 *dst);
+       int (*write_dev)(void *data, char reg, int count, const u16 *src);
+
+       struct mutex io_lock;
+       void *io_data;
+
+       u16 reg_cache[WM8400_REGISTER_COUNT];
+
+       struct platform_device regulators[6];
+};
+
+/*
+ * Register values.
+ */
+#define WM8400_RESET_ID                         0x00
+#define WM8400_ID                               0x01
+#define WM8400_POWER_MANAGEMENT_1               0x02
+#define WM8400_POWER_MANAGEMENT_2               0x03
+#define WM8400_POWER_MANAGEMENT_3               0x04
+#define WM8400_AUDIO_INTERFACE_1                0x05
+#define WM8400_AUDIO_INTERFACE_2                0x06
+#define WM8400_CLOCKING_1                       0x07
+#define WM8400_CLOCKING_2                       0x08
+#define WM8400_AUDIO_INTERFACE_3                0x09
+#define WM8400_AUDIO_INTERFACE_4                0x0A
+#define WM8400_DAC_CTRL                         0x0B
+#define WM8400_LEFT_DAC_DIGITAL_VOLUME          0x0C
+#define WM8400_RIGHT_DAC_DIGITAL_VOLUME         0x0D
+#define WM8400_DIGITAL_SIDE_TONE                0x0E
+#define WM8400_ADC_CTRL                         0x0F
+#define WM8400_LEFT_ADC_DIGITAL_VOLUME          0x10
+#define WM8400_RIGHT_ADC_DIGITAL_VOLUME         0x11
+#define WM8400_GPIO_CTRL_1                      0x12
+#define WM8400_GPIO1_GPIO2                      0x13
+#define WM8400_GPIO3_GPIO4                      0x14
+#define WM8400_GPIO5_GPIO6                      0x15
+#define WM8400_GPIOCTRL_2                       0x16
+#define WM8400_GPIO_POL                         0x17
+#define WM8400_LEFT_LINE_INPUT_1_2_VOLUME       0x18
+#define WM8400_LEFT_LINE_INPUT_3_4_VOLUME       0x19
+#define WM8400_RIGHT_LINE_INPUT_1_2_VOLUME      0x1A
+#define WM8400_RIGHT_LINE_INPUT_3_4_VOLUME      0x1B
+#define WM8400_LEFT_OUTPUT_VOLUME               0x1C
+#define WM8400_RIGHT_OUTPUT_VOLUME              0x1D
+#define WM8400_LINE_OUTPUTS_VOLUME              0x1E
+#define WM8400_OUT3_4_VOLUME                    0x1F
+#define WM8400_LEFT_OPGA_VOLUME                 0x20
+#define WM8400_RIGHT_OPGA_VOLUME                0x21
+#define WM8400_SPEAKER_VOLUME                   0x22
+#define WM8400_CLASSD1                          0x23
+#define WM8400_CLASSD3                          0x25
+#define WM8400_INPUT_MIXER1                     0x27
+#define WM8400_INPUT_MIXER2                     0x28
+#define WM8400_INPUT_MIXER3                     0x29
+#define WM8400_INPUT_MIXER4                     0x2A
+#define WM8400_INPUT_MIXER5                     0x2B
+#define WM8400_INPUT_MIXER6                     0x2C
+#define WM8400_OUTPUT_MIXER1                    0x2D
+#define WM8400_OUTPUT_MIXER2                    0x2E
+#define WM8400_OUTPUT_MIXER3                    0x2F
+#define WM8400_OUTPUT_MIXER4                    0x30
+#define WM8400_OUTPUT_MIXER5                    0x31
+#define WM8400_OUTPUT_MIXER6                    0x32
+#define WM8400_OUT3_4_MIXER                     0x33
+#define WM8400_LINE_MIXER1                      0x34
+#define WM8400_LINE_MIXER2                      0x35
+#define WM8400_SPEAKER_MIXER                    0x36
+#define WM8400_ADDITIONAL_CONTROL               0x37
+#define WM8400_ANTIPOP1                         0x38
+#define WM8400_ANTIPOP2                         0x39
+#define WM8400_MICBIAS                          0x3A
+#define WM8400_FLL_CONTROL_1                    0x3C
+#define WM8400_FLL_CONTROL_2                    0x3D
+#define WM8400_FLL_CONTROL_3                    0x3E
+#define WM8400_FLL_CONTROL_4                    0x3F
+#define WM8400_LDO1_CONTROL                     0x41
+#define WM8400_LDO2_CONTROL                     0x42
+#define WM8400_LDO3_CONTROL                     0x43
+#define WM8400_LDO4_CONTROL                     0x44
+#define WM8400_DCDC1_CONTROL_1                  0x46
+#define WM8400_DCDC1_CONTROL_2                  0x47
+#define WM8400_DCDC2_CONTROL_1                  0x48
+#define WM8400_DCDC2_CONTROL_2                  0x49
+#define WM8400_INTERFACE                        0x4B
+#define WM8400_PM_GENERAL                       0x4C
+#define WM8400_PM_SHUTDOWN_CONTROL              0x4E
+#define WM8400_INTERRUPT_STATUS_1               0x4F
+#define WM8400_INTERRUPT_STATUS_1_MASK          0x50
+#define WM8400_INTERRUPT_LEVELS                 0x51
+#define WM8400_SHUTDOWN_REASON                  0x52
+#define WM8400_LINE_CIRCUITS                    0x54
+
+/*
+ * Field Definitions.
+ */
+
+/*
+ * R0 (0x00) - Reset/ID
+ */
+#define WM8400_SW_RESET_CHIP_ID_MASK            0xFFFF  /* SW_RESET/CHIP_ID - [15:0] */
+#define WM8400_SW_RESET_CHIP_ID_SHIFT                0  /* SW_RESET/CHIP_ID - [15:0] */
+#define WM8400_SW_RESET_CHIP_ID_WIDTH               16  /* SW_RESET/CHIP_ID - [15:0] */
+
+/*
+ * R1 (0x01) - ID
+ */
+#define WM8400_CHIP_REV_MASK                    0x7000  /* CHIP_REV - [14:12] */
+#define WM8400_CHIP_REV_SHIFT                       12  /* CHIP_REV - [14:12] */
+#define WM8400_CHIP_REV_WIDTH                        3  /* CHIP_REV - [14:12] */
+
+/*
+ * R18 (0x12) - GPIO CTRL 1
+ */
+#define WM8400_IRQ                              0x1000  /* IRQ */
+#define WM8400_IRQ_MASK                         0x1000  /* IRQ */
+#define WM8400_IRQ_SHIFT                            12  /* IRQ */
+#define WM8400_IRQ_WIDTH                             1  /* IRQ */
+#define WM8400_TEMPOK                           0x0800  /* TEMPOK */
+#define WM8400_TEMPOK_MASK                      0x0800  /* TEMPOK */
+#define WM8400_TEMPOK_SHIFT                         11  /* TEMPOK */
+#define WM8400_TEMPOK_WIDTH                          1  /* TEMPOK */
+#define WM8400_MIC1SHRT                         0x0400  /* MIC1SHRT */
+#define WM8400_MIC1SHRT_MASK                    0x0400  /* MIC1SHRT */
+#define WM8400_MIC1SHRT_SHIFT                       10  /* MIC1SHRT */
+#define WM8400_MIC1SHRT_WIDTH                        1  /* MIC1SHRT */
+#define WM8400_MIC1DET                          0x0200  /* MIC1DET */
+#define WM8400_MIC1DET_MASK                     0x0200  /* MIC1DET */
+#define WM8400_MIC1DET_SHIFT                         9  /* MIC1DET */
+#define WM8400_MIC1DET_WIDTH                         1  /* MIC1DET */
+#define WM8400_FLL_LCK                          0x0100  /* FLL_LCK */
+#define WM8400_FLL_LCK_MASK                     0x0100  /* FLL_LCK */
+#define WM8400_FLL_LCK_SHIFT                         8  /* FLL_LCK */
+#define WM8400_FLL_LCK_WIDTH                         1  /* FLL_LCK */
+#define WM8400_GPIO_STATUS_MASK                 0x00FF  /* GPIO_STATUS - [7:0] */
+#define WM8400_GPIO_STATUS_SHIFT                     0  /* GPIO_STATUS - [7:0] */
+#define WM8400_GPIO_STATUS_WIDTH                     8  /* GPIO_STATUS - [7:0] */
+
+/*
+ * R19 (0x13) - GPIO1 & GPIO2
+ */
+#define WM8400_GPIO2_DEB_ENA                    0x8000  /* GPIO2_DEB_ENA */
+#define WM8400_GPIO2_DEB_ENA_MASK               0x8000  /* GPIO2_DEB_ENA */
+#define WM8400_GPIO2_DEB_ENA_SHIFT                  15  /* GPIO2_DEB_ENA */
+#define WM8400_GPIO2_DEB_ENA_WIDTH                   1  /* GPIO2_DEB_ENA */
+#define WM8400_GPIO2_IRQ_ENA                    0x4000  /* GPIO2_IRQ_ENA */
+#define WM8400_GPIO2_IRQ_ENA_MASK               0x4000  /* GPIO2_IRQ_ENA */
+#define WM8400_GPIO2_IRQ_ENA_SHIFT                  14  /* GPIO2_IRQ_ENA */
+#define WM8400_GPIO2_IRQ_ENA_WIDTH                   1  /* GPIO2_IRQ_ENA */
+#define WM8400_GPIO2_PU                         0x2000  /* GPIO2_PU */
+#define WM8400_GPIO2_PU_MASK                    0x2000  /* GPIO2_PU */
+#define WM8400_GPIO2_PU_SHIFT                       13  /* GPIO2_PU */
+#define WM8400_GPIO2_PU_WIDTH                        1  /* GPIO2_PU */
+#define WM8400_GPIO2_PD                         0x1000  /* GPIO2_PD */
+#define WM8400_GPIO2_PD_MASK                    0x1000  /* GPIO2_PD */
+#define WM8400_GPIO2_PD_SHIFT                       12  /* GPIO2_PD */
+#define WM8400_GPIO2_PD_WIDTH                        1  /* GPIO2_PD */
+#define WM8400_GPIO2_SEL_MASK                   0x0F00  /* GPIO2_SEL - [11:8] */
+#define WM8400_GPIO2_SEL_SHIFT                       8  /* GPIO2_SEL - [11:8] */
+#define WM8400_GPIO2_SEL_WIDTH                       4  /* GPIO2_SEL - [11:8] */
+#define WM8400_GPIO1_DEB_ENA                    0x0080  /* GPIO1_DEB_ENA */
+#define WM8400_GPIO1_DEB_ENA_MASK               0x0080  /* GPIO1_DEB_ENA */
+#define WM8400_GPIO1_DEB_ENA_SHIFT                   7  /* GPIO1_DEB_ENA */
+#define WM8400_GPIO1_DEB_ENA_WIDTH                   1  /* GPIO1_DEB_ENA */
+#define WM8400_GPIO1_IRQ_ENA                    0x0040  /* GPIO1_IRQ_ENA */
+#define WM8400_GPIO1_IRQ_ENA_MASK               0x0040  /* GPIO1_IRQ_ENA */
+#define WM8400_GPIO1_IRQ_ENA_SHIFT                   6  /* GPIO1_IRQ_ENA */
+#define WM8400_GPIO1_IRQ_ENA_WIDTH                   1  /* GPIO1_IRQ_ENA */
+#define WM8400_GPIO1_PU                         0x0020  /* GPIO1_PU */
+#define WM8400_GPIO1_PU_MASK                    0x0020  /* GPIO1_PU */
+#define WM8400_GPIO1_PU_SHIFT                        5  /* GPIO1_PU */
+#define WM8400_GPIO1_PU_WIDTH                        1  /* GPIO1_PU */
+#define WM8400_GPIO1_PD                         0x0010  /* GPIO1_PD */
+#define WM8400_GPIO1_PD_MASK                    0x0010  /* GPIO1_PD */
+#define WM8400_GPIO1_PD_SHIFT                        4  /* GPIO1_PD */
+#define WM8400_GPIO1_PD_WIDTH                        1  /* GPIO1_PD */
+#define WM8400_GPIO1_SEL_MASK                   0x000F  /* GPIO1_SEL - [3:0] */
+#define WM8400_GPIO1_SEL_SHIFT                       0  /* GPIO1_SEL - [3:0] */
+#define WM8400_GPIO1_SEL_WIDTH                       4  /* GPIO1_SEL - [3:0] */
+
+/*
+ * R20 (0x14) - GPIO3 & GPIO4
+ */
+#define WM8400_GPIO4_DEB_ENA                    0x8000  /* GPIO4_DEB_ENA */
+#define WM8400_GPIO4_DEB_ENA_MASK               0x8000  /* GPIO4_DEB_ENA */
+#define WM8400_GPIO4_DEB_ENA_SHIFT                  15  /* GPIO4_DEB_ENA */
+#define WM8400_GPIO4_DEB_ENA_WIDTH                   1  /* GPIO4_DEB_ENA */
+#define WM8400_GPIO4_IRQ_ENA                    0x4000  /* GPIO4_IRQ_ENA */
+#define WM8400_GPIO4_IRQ_ENA_MASK               0x4000  /* GPIO4_IRQ_ENA */
+#define WM8400_GPIO4_IRQ_ENA_SHIFT                  14  /* GPIO4_IRQ_ENA */
+#define WM8400_GPIO4_IRQ_ENA_WIDTH                   1  /* GPIO4_IRQ_ENA */
+#define WM8400_GPIO4_PU                         0x2000  /* GPIO4_PU */
+#define WM8400_GPIO4_PU_MASK                    0x2000  /* GPIO4_PU */
+#define WM8400_GPIO4_PU_SHIFT                       13  /* GPIO4_PU */
+#define WM8400_GPIO4_PU_WIDTH                        1  /* GPIO4_PU */
+#define WM8400_GPIO4_PD                         0x1000  /* GPIO4_PD */
+#define WM8400_GPIO4_PD_MASK                    0x1000  /* GPIO4_PD */
+#define WM8400_GPIO4_PD_SHIFT                       12  /* GPIO4_PD */
+#define WM8400_GPIO4_PD_WIDTH                        1  /* GPIO4_PD */
+#define WM8400_GPIO4_SEL_MASK                   0x0F00  /* GPIO4_SEL - [11:8] */
+#define WM8400_GPIO4_SEL_SHIFT                       8  /* GPIO4_SEL - [11:8] */
+#define WM8400_GPIO4_SEL_WIDTH                       4  /* GPIO4_SEL - [11:8] */
+#define WM8400_GPIO3_DEB_ENA                    0x0080  /* GPIO3_DEB_ENA */
+#define WM8400_GPIO3_DEB_ENA_MASK               0x0080  /* GPIO3_DEB_ENA */
+#define WM8400_GPIO3_DEB_ENA_SHIFT                   7  /* GPIO3_DEB_ENA */
+#define WM8400_GPIO3_DEB_ENA_WIDTH                   1  /* GPIO3_DEB_ENA */
+#define WM8400_GPIO3_IRQ_ENA                    0x0040  /* GPIO3_IRQ_ENA */
+#define WM8400_GPIO3_IRQ_ENA_MASK               0x0040  /* GPIO3_IRQ_ENA */
+#define WM8400_GPIO3_IRQ_ENA_SHIFT                   6  /* GPIO3_IRQ_ENA */
+#define WM8400_GPIO3_IRQ_ENA_WIDTH                   1  /* GPIO3_IRQ_ENA */
+#define WM8400_GPIO3_PU                         0x0020  /* GPIO3_PU */
+#define WM8400_GPIO3_PU_MASK                    0x0020  /* GPIO3_PU */
+#define WM8400_GPIO3_PU_SHIFT                        5  /* GPIO3_PU */
+#define WM8400_GPIO3_PU_WIDTH                        1  /* GPIO3_PU */
+#define WM8400_GPIO3_PD                         0x0010  /* GPIO3_PD */
+#define WM8400_GPIO3_PD_MASK                    0x0010  /* GPIO3_PD */
+#define WM8400_GPIO3_PD_SHIFT                        4  /* GPIO3_PD */
+#define WM8400_GPIO3_PD_WIDTH                        1  /* GPIO3_PD */
+#define WM8400_GPIO3_SEL_MASK                   0x000F  /* GPIO3_SEL - [3:0] */
+#define WM8400_GPIO3_SEL_SHIFT                       0  /* GPIO3_SEL - [3:0] */
+#define WM8400_GPIO3_SEL_WIDTH                       4  /* GPIO3_SEL - [3:0] */
+
+/*
+ * R21 (0x15) - GPIO5 & GPIO6
+ */
+#define WM8400_GPIO6_DEB_ENA                    0x8000  /* GPIO6_DEB_ENA */
+#define WM8400_GPIO6_DEB_ENA_MASK               0x8000  /* GPIO6_DEB_ENA */
+#define WM8400_GPIO6_DEB_ENA_SHIFT                  15  /* GPIO6_DEB_ENA */
+#define WM8400_GPIO6_DEB_ENA_WIDTH                   1  /* GPIO6_DEB_ENA */
+#define WM8400_GPIO6_IRQ_ENA                    0x4000  /* GPIO6_IRQ_ENA */
+#define WM8400_GPIO6_IRQ_ENA_MASK               0x4000  /* GPIO6_IRQ_ENA */
+#define WM8400_GPIO6_IRQ_ENA_SHIFT                  14  /* GPIO6_IRQ_ENA */
+#define WM8400_GPIO6_IRQ_ENA_WIDTH                   1  /* GPIO6_IRQ_ENA */
+#define WM8400_GPIO6_PU                         0x2000  /* GPIO6_PU */
+#define WM8400_GPIO6_PU_MASK                    0x2000  /* GPIO6_PU */
+#define WM8400_GPIO6_PU_SHIFT                       13  /* GPIO6_PU */
+#define WM8400_GPIO6_PU_WIDTH                        1  /* GPIO6_PU */
+#define WM8400_GPIO6_PD                         0x1000  /* GPIO6_PD */
+#define WM8400_GPIO6_PD_MASK                    0x1000  /* GPIO6_PD */
+#define WM8400_GPIO6_PD_SHIFT                       12  /* GPIO6_PD */
+#define WM8400_GPIO6_PD_WIDTH                        1  /* GPIO6_PD */
+#define WM8400_GPIO6_SEL_MASK                   0x0F00  /* GPIO6_SEL - [11:8] */
+#define WM8400_GPIO6_SEL_SHIFT                       8  /* GPIO6_SEL - [11:8] */
+#define WM8400_GPIO6_SEL_WIDTH                       4  /* GPIO6_SEL - [11:8] */
+#define WM8400_GPIO5_DEB_ENA                    0x0080  /* GPIO5_DEB_ENA */
+#define WM8400_GPIO5_DEB_ENA_MASK               0x0080  /* GPIO5_DEB_ENA */
+#define WM8400_GPIO5_DEB_ENA_SHIFT                   7  /* GPIO5_DEB_ENA */
+#define WM8400_GPIO5_DEB_ENA_WIDTH                   1  /* GPIO5_DEB_ENA */
+#define WM8400_GPIO5_IRQ_ENA                    0x0040  /* GPIO5_IRQ_ENA */
+#define WM8400_GPIO5_IRQ_ENA_MASK               0x0040  /* GPIO5_IRQ_ENA */
+#define WM8400_GPIO5_IRQ_ENA_SHIFT                   6  /* GPIO5_IRQ_ENA */
+#define WM8400_GPIO5_IRQ_ENA_WIDTH                   1  /* GPIO5_IRQ_ENA */
+#define WM8400_GPIO5_PU                         0x0020  /* GPIO5_PU */
+#define WM8400_GPIO5_PU_MASK                    0x0020  /* GPIO5_PU */
+#define WM8400_GPIO5_PU_SHIFT                        5  /* GPIO5_PU */
+#define WM8400_GPIO5_PU_WIDTH                        1  /* GPIO5_PU */
+#define WM8400_GPIO5_PD                         0x0010  /* GPIO5_PD */
+#define WM8400_GPIO5_PD_MASK                    0x0010  /* GPIO5_PD */
+#define WM8400_GPIO5_PD_SHIFT                        4  /* GPIO5_PD */
+#define WM8400_GPIO5_PD_WIDTH                        1  /* GPIO5_PD */
+#define WM8400_GPIO5_SEL_MASK                   0x000F  /* GPIO5_SEL - [3:0] */
+#define WM8400_GPIO5_SEL_SHIFT                       0  /* GPIO5_SEL - [3:0] */
+#define WM8400_GPIO5_SEL_WIDTH                       4  /* GPIO5_SEL - [3:0] */
+
+/*
+ * R22 (0x16) - GPIOCTRL 2
+ */
+#define WM8400_TEMPOK_IRQ_ENA                   0x0800  /* TEMPOK_IRQ_ENA */
+#define WM8400_TEMPOK_IRQ_ENA_MASK              0x0800  /* TEMPOK_IRQ_ENA */
+#define WM8400_TEMPOK_IRQ_ENA_SHIFT                 11  /* TEMPOK_IRQ_ENA */
+#define WM8400_TEMPOK_IRQ_ENA_WIDTH                  1  /* TEMPOK_IRQ_ENA */
+#define WM8400_MIC1SHRT_IRQ_ENA                 0x0400  /* MIC1SHRT_IRQ_ENA */
+#define WM8400_MIC1SHRT_IRQ_ENA_MASK            0x0400  /* MIC1SHRT_IRQ_ENA */
+#define WM8400_MIC1SHRT_IRQ_ENA_SHIFT               10  /* MIC1SHRT_IRQ_ENA */
+#define WM8400_MIC1SHRT_IRQ_ENA_WIDTH                1  /* MIC1SHRT_IRQ_ENA */
+#define WM8400_MIC1DET_IRQ_ENA                  0x0200  /* MIC1DET_IRQ_ENA */
+#define WM8400_MIC1DET_IRQ_ENA_MASK             0x0200  /* MIC1DET_IRQ_ENA */
+#define WM8400_MIC1DET_IRQ_ENA_SHIFT                 9  /* MIC1DET_IRQ_ENA */
+#define WM8400_MIC1DET_IRQ_ENA_WIDTH                 1  /* MIC1DET_IRQ_ENA */
+#define WM8400_FLL_LCK_IRQ_ENA                  0x0100  /* FLL_LCK_IRQ_ENA */
+#define WM8400_FLL_LCK_IRQ_ENA_MASK             0x0100  /* FLL_LCK_IRQ_ENA */
+#define WM8400_FLL_LCK_IRQ_ENA_SHIFT                 8  /* FLL_LCK_IRQ_ENA */
+#define WM8400_FLL_LCK_IRQ_ENA_WIDTH                 1  /* FLL_LCK_IRQ_ENA */
+#define WM8400_GPI8_DEB_ENA                     0x0080  /* GPI8_DEB_ENA */
+#define WM8400_GPI8_DEB_ENA_MASK                0x0080  /* GPI8_DEB_ENA */
+#define WM8400_GPI8_DEB_ENA_SHIFT                    7  /* GPI8_DEB_ENA */
+#define WM8400_GPI8_DEB_ENA_WIDTH                    1  /* GPI8_DEB_ENA */
+#define WM8400_GPI8_IRQ_ENA                     0x0040  /* GPI8_IRQ_ENA */
+#define WM8400_GPI8_IRQ_ENA_MASK                0x0040  /* GPI8_IRQ_ENA */
+#define WM8400_GPI8_IRQ_ENA_SHIFT                    6  /* GPI8_IRQ_ENA */
+#define WM8400_GPI8_IRQ_ENA_WIDTH                    1  /* GPI8_IRQ_ENA */
+#define WM8400_GPI8_ENA                         0x0010  /* GPI8_ENA */
+#define WM8400_GPI8_ENA_MASK                    0x0010  /* GPI8_ENA */
+#define WM8400_GPI8_ENA_SHIFT                        4  /* GPI8_ENA */
+#define WM8400_GPI8_ENA_WIDTH                        1  /* GPI8_ENA */
+#define WM8400_GPI7_DEB_ENA                     0x0008  /* GPI7_DEB_ENA */
+#define WM8400_GPI7_DEB_ENA_MASK                0x0008  /* GPI7_DEB_ENA */
+#define WM8400_GPI7_DEB_ENA_SHIFT                    3  /* GPI7_DEB_ENA */
+#define WM8400_GPI7_DEB_ENA_WIDTH                    1  /* GPI7_DEB_ENA */
+#define WM8400_GPI7_IRQ_ENA                     0x0004  /* GPI7_IRQ_ENA */
+#define WM8400_GPI7_IRQ_ENA_MASK                0x0004  /* GPI7_IRQ_ENA */
+#define WM8400_GPI7_IRQ_ENA_SHIFT                    2  /* GPI7_IRQ_ENA */
+#define WM8400_GPI7_IRQ_ENA_WIDTH                    1  /* GPI7_IRQ_ENA */
+#define WM8400_GPI7_ENA                         0x0001  /* GPI7_ENA */
+#define WM8400_GPI7_ENA_MASK                    0x0001  /* GPI7_ENA */
+#define WM8400_GPI7_ENA_SHIFT                        0  /* GPI7_ENA */
+#define WM8400_GPI7_ENA_WIDTH                        1  /* GPI7_ENA */
+
+/*
+ * R23 (0x17) - GPIO_POL
+ */
+#define WM8400_IRQ_INV                          0x1000  /* IRQ_INV */
+#define WM8400_IRQ_INV_MASK                     0x1000  /* IRQ_INV */
+#define WM8400_IRQ_INV_SHIFT                        12  /* IRQ_INV */
+#define WM8400_IRQ_INV_WIDTH                         1  /* IRQ_INV */
+#define WM8400_TEMPOK_POL                       0x0800  /* TEMPOK_POL */
+#define WM8400_TEMPOK_POL_MASK                  0x0800  /* TEMPOK_POL */
+#define WM8400_TEMPOK_POL_SHIFT                     11  /* TEMPOK_POL */
+#define WM8400_TEMPOK_POL_WIDTH                      1  /* TEMPOK_POL */
+#define WM8400_MIC1SHRT_POL                     0x0400  /* MIC1SHRT_POL */
+#define WM8400_MIC1SHRT_POL_MASK                0x0400  /* MIC1SHRT_POL */
+#define WM8400_MIC1SHRT_POL_SHIFT                   10  /* MIC1SHRT_POL */
+#define WM8400_MIC1SHRT_POL_WIDTH                    1  /* MIC1SHRT_POL */
+#define WM8400_MIC1DET_POL                      0x0200  /* MIC1DET_POL */
+#define WM8400_MIC1DET_POL_MASK                 0x0200  /* MIC1DET_POL */
+#define WM8400_MIC1DET_POL_SHIFT                     9  /* MIC1DET_POL */
+#define WM8400_MIC1DET_POL_WIDTH                     1  /* MIC1DET_POL */
+#define WM8400_FLL_LCK_POL                      0x0100  /* FLL_LCK_POL */
+#define WM8400_FLL_LCK_POL_MASK                 0x0100  /* FLL_LCK_POL */
+#define WM8400_FLL_LCK_POL_SHIFT                     8  /* FLL_LCK_POL */
+#define WM8400_FLL_LCK_POL_WIDTH                     1  /* FLL_LCK_POL */
+#define WM8400_GPIO_POL_MASK                    0x00FF  /* GPIO_POL - [7:0] */
+#define WM8400_GPIO_POL_SHIFT                        0  /* GPIO_POL - [7:0] */
+#define WM8400_GPIO_POL_WIDTH                        8  /* GPIO_POL - [7:0] */
+
+/*
+ * R65 (0x41) - LDO 1 Control
+ */
+#define WM8400_LDO1_ENA                         0x8000  /* LDO1_ENA */
+#define WM8400_LDO1_ENA_MASK                    0x8000  /* LDO1_ENA */
+#define WM8400_LDO1_ENA_SHIFT                       15  /* LDO1_ENA */
+#define WM8400_LDO1_ENA_WIDTH                        1  /* LDO1_ENA */
+#define WM8400_LDO1_SWI                         0x4000  /* LDO1_SWI */
+#define WM8400_LDO1_SWI_MASK                    0x4000  /* LDO1_SWI */
+#define WM8400_LDO1_SWI_SHIFT                       14  /* LDO1_SWI */
+#define WM8400_LDO1_SWI_WIDTH                        1  /* LDO1_SWI */
+#define WM8400_LDO1_OPFLT                       0x1000  /* LDO1_OPFLT */
+#define WM8400_LDO1_OPFLT_MASK                  0x1000  /* LDO1_OPFLT */
+#define WM8400_LDO1_OPFLT_SHIFT                     12  /* LDO1_OPFLT */
+#define WM8400_LDO1_OPFLT_WIDTH                      1  /* LDO1_OPFLT */
+#define WM8400_LDO1_ERRACT                      0x0800  /* LDO1_ERRACT */
+#define WM8400_LDO1_ERRACT_MASK                 0x0800  /* LDO1_ERRACT */
+#define WM8400_LDO1_ERRACT_SHIFT                    11  /* LDO1_ERRACT */
+#define WM8400_LDO1_ERRACT_WIDTH                     1  /* LDO1_ERRACT */
+#define WM8400_LDO1_HIB_MODE                    0x0400  /* LDO1_HIB_MODE */
+#define WM8400_LDO1_HIB_MODE_MASK               0x0400  /* LDO1_HIB_MODE */
+#define WM8400_LDO1_HIB_MODE_SHIFT                  10  /* LDO1_HIB_MODE */
+#define WM8400_LDO1_HIB_MODE_WIDTH                   1  /* LDO1_HIB_MODE */
+#define WM8400_LDO1_VIMG_MASK                   0x03E0  /* LDO1_VIMG - [9:5] */
+#define WM8400_LDO1_VIMG_SHIFT                       5  /* LDO1_VIMG - [9:5] */
+#define WM8400_LDO1_VIMG_WIDTH                       5  /* LDO1_VIMG - [9:5] */
+#define WM8400_LDO1_VSEL_MASK                   0x001F  /* LDO1_VSEL - [4:0] */
+#define WM8400_LDO1_VSEL_SHIFT                       0  /* LDO1_VSEL - [4:0] */
+#define WM8400_LDO1_VSEL_WIDTH                       5  /* LDO1_VSEL - [4:0] */
+
+/*
+ * R66 (0x42) - LDO 2 Control
+ */
+#define WM8400_LDO2_ENA                         0x8000  /* LDO2_ENA */
+#define WM8400_LDO2_ENA_MASK                    0x8000  /* LDO2_ENA */
+#define WM8400_LDO2_ENA_SHIFT                       15  /* LDO2_ENA */
+#define WM8400_LDO2_ENA_WIDTH                        1  /* LDO2_ENA */
+#define WM8400_LDO2_SWI                         0x4000  /* LDO2_SWI */
+#define WM8400_LDO2_SWI_MASK                    0x4000  /* LDO2_SWI */
+#define WM8400_LDO2_SWI_SHIFT                       14  /* LDO2_SWI */
+#define WM8400_LDO2_SWI_WIDTH                        1  /* LDO2_SWI */
+#define WM8400_LDO2_OPFLT                       0x1000  /* LDO2_OPFLT */
+#define WM8400_LDO2_OPFLT_MASK                  0x1000  /* LDO2_OPFLT */
+#define WM8400_LDO2_OPFLT_SHIFT                     12  /* LDO2_OPFLT */
+#define WM8400_LDO2_OPFLT_WIDTH                      1  /* LDO2_OPFLT */
+#define WM8400_LDO2_ERRACT                      0x0800  /* LDO2_ERRACT */
+#define WM8400_LDO2_ERRACT_MASK                 0x0800  /* LDO2_ERRACT */
+#define WM8400_LDO2_ERRACT_SHIFT                    11  /* LDO2_ERRACT */
+#define WM8400_LDO2_ERRACT_WIDTH                     1  /* LDO2_ERRACT */
+#define WM8400_LDO2_HIB_MODE                    0x0400  /* LDO2_HIB_MODE */
+#define WM8400_LDO2_HIB_MODE_MASK               0x0400  /* LDO2_HIB_MODE */
+#define WM8400_LDO2_HIB_MODE_SHIFT                  10  /* LDO2_HIB_MODE */
+#define WM8400_LDO2_HIB_MODE_WIDTH                   1  /* LDO2_HIB_MODE */
+#define WM8400_LDO2_VIMG_MASK                   0x03E0  /* LDO2_VIMG - [9:5] */
+#define WM8400_LDO2_VIMG_SHIFT                       5  /* LDO2_VIMG - [9:5] */
+#define WM8400_LDO2_VIMG_WIDTH                       5  /* LDO2_VIMG - [9:5] */
+#define WM8400_LDO2_VSEL_MASK                   0x001F  /* LDO2_VSEL - [4:0] */
+#define WM8400_LDO2_VSEL_SHIFT                       0  /* LDO2_VSEL - [4:0] */
+#define WM8400_LDO2_VSEL_WIDTH                       5  /* LDO2_VSEL - [4:0] */
+
+/*
+ * R67 (0x43) - LDO 3 Control
+ */
+#define WM8400_LDO3_ENA                         0x8000  /* LDO3_ENA */
+#define WM8400_LDO3_ENA_MASK                    0x8000  /* LDO3_ENA */
+#define WM8400_LDO3_ENA_SHIFT                       15  /* LDO3_ENA */
+#define WM8400_LDO3_ENA_WIDTH                        1  /* LDO3_ENA */
+#define WM8400_LDO3_SWI                         0x4000  /* LDO3_SWI */
+#define WM8400_LDO3_SWI_MASK                    0x4000  /* LDO3_SWI */
+#define WM8400_LDO3_SWI_SHIFT                       14  /* LDO3_SWI */
+#define WM8400_LDO3_SWI_WIDTH                        1  /* LDO3_SWI */
+#define WM8400_LDO3_OPFLT                       0x1000  /* LDO3_OPFLT */
+#define WM8400_LDO3_OPFLT_MASK                  0x1000  /* LDO3_OPFLT */
+#define WM8400_LDO3_OPFLT_SHIFT                     12  /* LDO3_OPFLT */
+#define WM8400_LDO3_OPFLT_WIDTH                      1  /* LDO3_OPFLT */
+#define WM8400_LDO3_ERRACT                      0x0800  /* LDO3_ERRACT */
+#define WM8400_LDO3_ERRACT_MASK                 0x0800  /* LDO3_ERRACT */
+#define WM8400_LDO3_ERRACT_SHIFT                    11  /* LDO3_ERRACT */
+#define WM8400_LDO3_ERRACT_WIDTH                     1  /* LDO3_ERRACT */
+#define WM8400_LDO3_HIB_MODE                    0x0400  /* LDO3_HIB_MODE */
+#define WM8400_LDO3_HIB_MODE_MASK               0x0400  /* LDO3_HIB_MODE */
+#define WM8400_LDO3_HIB_MODE_SHIFT                  10  /* LDO3_HIB_MODE */
+#define WM8400_LDO3_HIB_MODE_WIDTH                   1  /* LDO3_HIB_MODE */
+#define WM8400_LDO3_VIMG_MASK                   0x03E0  /* LDO3_VIMG - [9:5] */
+#define WM8400_LDO3_VIMG_SHIFT                       5  /* LDO3_VIMG - [9:5] */
+#define WM8400_LDO3_VIMG_WIDTH                       5  /* LDO3_VIMG - [9:5] */
+#define WM8400_LDO3_VSEL_MASK                   0x001F  /* LDO3_VSEL - [4:0] */
+#define WM8400_LDO3_VSEL_SHIFT                       0  /* LDO3_VSEL - [4:0] */
+#define WM8400_LDO3_VSEL_WIDTH                       5  /* LDO3_VSEL - [4:0] */
+
+/*
+ * R68 (0x44) - LDO 4 Control
+ */
+#define WM8400_LDO4_ENA                         0x8000  /* LDO4_ENA */
+#define WM8400_LDO4_ENA_MASK                    0x8000  /* LDO4_ENA */
+#define WM8400_LDO4_ENA_SHIFT                       15  /* LDO4_ENA */
+#define WM8400_LDO4_ENA_WIDTH                        1  /* LDO4_ENA */
+#define WM8400_LDO4_SWI                         0x4000  /* LDO4_SWI */
+#define WM8400_LDO4_SWI_MASK                    0x4000  /* LDO4_SWI */
+#define WM8400_LDO4_SWI_SHIFT                       14  /* LDO4_SWI */
+#define WM8400_LDO4_SWI_WIDTH                        1  /* LDO4_SWI */
+#define WM8400_LDO4_OPFLT                       0x1000  /* LDO4_OPFLT */
+#define WM8400_LDO4_OPFLT_MASK                  0x1000  /* LDO4_OPFLT */
+#define WM8400_LDO4_OPFLT_SHIFT                     12  /* LDO4_OPFLT */
+#define WM8400_LDO4_OPFLT_WIDTH                      1  /* LDO4_OPFLT */
+#define WM8400_LDO4_ERRACT                      0x0800  /* LDO4_ERRACT */
+#define WM8400_LDO4_ERRACT_MASK                 0x0800  /* LDO4_ERRACT */
+#define WM8400_LDO4_ERRACT_SHIFT                    11  /* LDO4_ERRACT */
+#define WM8400_LDO4_ERRACT_WIDTH                     1  /* LDO4_ERRACT */
+#define WM8400_LDO4_HIB_MODE                    0x0400  /* LDO4_HIB_MODE */
+#define WM8400_LDO4_HIB_MODE_MASK               0x0400  /* LDO4_HIB_MODE */
+#define WM8400_LDO4_HIB_MODE_SHIFT                  10  /* LDO4_HIB_MODE */
+#define WM8400_LDO4_HIB_MODE_WIDTH                   1  /* LDO4_HIB_MODE */
+#define WM8400_LDO4_VIMG_MASK                   0x03E0  /* LDO4_VIMG - [9:5] */
+#define WM8400_LDO4_VIMG_SHIFT                       5  /* LDO4_VIMG - [9:5] */
+#define WM8400_LDO4_VIMG_WIDTH                       5  /* LDO4_VIMG - [9:5] */
+#define WM8400_LDO4_VSEL_MASK                   0x001F  /* LDO4_VSEL - [4:0] */
+#define WM8400_LDO4_VSEL_SHIFT                       0  /* LDO4_VSEL - [4:0] */
+#define WM8400_LDO4_VSEL_WIDTH                       5  /* LDO4_VSEL - [4:0] */
+
+/*
+ * R70 (0x46) - DCDC1 Control 1
+ */
+#define WM8400_DC1_ENA                          0x8000  /* DC1_ENA */
+#define WM8400_DC1_ENA_MASK                     0x8000  /* DC1_ENA */
+#define WM8400_DC1_ENA_SHIFT                        15  /* DC1_ENA */
+#define WM8400_DC1_ENA_WIDTH                         1  /* DC1_ENA */
+#define WM8400_DC1_ACTIVE                       0x4000  /* DC1_ACTIVE */
+#define WM8400_DC1_ACTIVE_MASK                  0x4000  /* DC1_ACTIVE */
+#define WM8400_DC1_ACTIVE_SHIFT                     14  /* DC1_ACTIVE */
+#define WM8400_DC1_ACTIVE_WIDTH                      1  /* DC1_ACTIVE */
+#define WM8400_DC1_SLEEP                        0x2000  /* DC1_SLEEP */
+#define WM8400_DC1_SLEEP_MASK                   0x2000  /* DC1_SLEEP */
+#define WM8400_DC1_SLEEP_SHIFT                      13  /* DC1_SLEEP */
+#define WM8400_DC1_SLEEP_WIDTH                       1  /* DC1_SLEEP */
+#define WM8400_DC1_OPFLT                        0x1000  /* DC1_OPFLT */
+#define WM8400_DC1_OPFLT_MASK                   0x1000  /* DC1_OPFLT */
+#define WM8400_DC1_OPFLT_SHIFT                      12  /* DC1_OPFLT */
+#define WM8400_DC1_OPFLT_WIDTH                       1  /* DC1_OPFLT */
+#define WM8400_DC1_ERRACT                       0x0800  /* DC1_ERRACT */
+#define WM8400_DC1_ERRACT_MASK                  0x0800  /* DC1_ERRACT */
+#define WM8400_DC1_ERRACT_SHIFT                     11  /* DC1_ERRACT */
+#define WM8400_DC1_ERRACT_WIDTH                      1  /* DC1_ERRACT */
+#define WM8400_DC1_HIB_MODE                     0x0400  /* DC1_HIB_MODE */
+#define WM8400_DC1_HIB_MODE_MASK                0x0400  /* DC1_HIB_MODE */
+#define WM8400_DC1_HIB_MODE_SHIFT                   10  /* DC1_HIB_MODE */
+#define WM8400_DC1_HIB_MODE_WIDTH                    1  /* DC1_HIB_MODE */
+#define WM8400_DC1_SOFTST_MASK                  0x0300  /* DC1_SOFTST - [9:8] */
+#define WM8400_DC1_SOFTST_SHIFT                      8  /* DC1_SOFTST - [9:8] */
+#define WM8400_DC1_SOFTST_WIDTH                      2  /* DC1_SOFTST - [9:8] */
+#define WM8400_DC1_OV_PROT                      0x0080  /* DC1_OV_PROT */
+#define WM8400_DC1_OV_PROT_MASK                 0x0080  /* DC1_OV_PROT */
+#define WM8400_DC1_OV_PROT_SHIFT                     7  /* DC1_OV_PROT */
+#define WM8400_DC1_OV_PROT_WIDTH                     1  /* DC1_OV_PROT */
+#define WM8400_DC1_VSEL_MASK                    0x007F  /* DC1_VSEL - [6:0] */
+#define WM8400_DC1_VSEL_SHIFT                        0  /* DC1_VSEL - [6:0] */
+#define WM8400_DC1_VSEL_WIDTH                        7  /* DC1_VSEL - [6:0] */
+
+/*
+ * R71 (0x47) - DCDC1 Control 2
+ */
+#define WM8400_DC1_FRC_PWM                      0x2000  /* DC1_FRC_PWM */
+#define WM8400_DC1_FRC_PWM_MASK                 0x2000  /* DC1_FRC_PWM */
+#define WM8400_DC1_FRC_PWM_SHIFT                    13  /* DC1_FRC_PWM */
+#define WM8400_DC1_FRC_PWM_WIDTH                     1  /* DC1_FRC_PWM */
+#define WM8400_DC1_STBY_LIM_MASK                0x0300  /* DC1_STBY_LIM - [9:8] */
+#define WM8400_DC1_STBY_LIM_SHIFT                    8  /* DC1_STBY_LIM - [9:8] */
+#define WM8400_DC1_STBY_LIM_WIDTH                    2  /* DC1_STBY_LIM - [9:8] */
+#define WM8400_DC1_ACT_LIM                      0x0080  /* DC1_ACT_LIM */
+#define WM8400_DC1_ACT_LIM_MASK                 0x0080  /* DC1_ACT_LIM */
+#define WM8400_DC1_ACT_LIM_SHIFT                     7  /* DC1_ACT_LIM */
+#define WM8400_DC1_ACT_LIM_WIDTH                     1  /* DC1_ACT_LIM */
+#define WM8400_DC1_VIMG_MASK                    0x007F  /* DC1_VIMG - [6:0] */
+#define WM8400_DC1_VIMG_SHIFT                        0  /* DC1_VIMG - [6:0] */
+#define WM8400_DC1_VIMG_WIDTH                        7  /* DC1_VIMG - [6:0] */
+
+/*
+ * R72 (0x48) - DCDC2 Control 1
+ */
+#define WM8400_DC2_ENA                          0x8000  /* DC2_ENA */
+#define WM8400_DC2_ENA_MASK                     0x8000  /* DC2_ENA */
+#define WM8400_DC2_ENA_SHIFT                        15  /* DC2_ENA */
+#define WM8400_DC2_ENA_WIDTH                         1  /* DC2_ENA */
+#define WM8400_DC2_ACTIVE                       0x4000  /* DC2_ACTIVE */
+#define WM8400_DC2_ACTIVE_MASK                  0x4000  /* DC2_ACTIVE */
+#define WM8400_DC2_ACTIVE_SHIFT                     14  /* DC2_ACTIVE */
+#define WM8400_DC2_ACTIVE_WIDTH                      1  /* DC2_ACTIVE */
+#define WM8400_DC2_SLEEP                        0x2000  /* DC2_SLEEP */
+#define WM8400_DC2_SLEEP_MASK                   0x2000  /* DC2_SLEEP */
+#define WM8400_DC2_SLEEP_SHIFT                      13  /* DC2_SLEEP */
+#define WM8400_DC2_SLEEP_WIDTH                       1  /* DC2_SLEEP */
+#define WM8400_DC2_OPFLT                        0x1000  /* DC2_OPFLT */
+#define WM8400_DC2_OPFLT_MASK                   0x1000  /* DC2_OPFLT */
+#define WM8400_DC2_OPFLT_SHIFT                      12  /* DC2_OPFLT */
+#define WM8400_DC2_OPFLT_WIDTH                       1  /* DC2_OPFLT */
+#define WM8400_DC2_ERRACT                       0x0800  /* DC2_ERRACT */
+#define WM8400_DC2_ERRACT_MASK                  0x0800  /* DC2_ERRACT */
+#define WM8400_DC2_ERRACT_SHIFT                     11  /* DC2_ERRACT */
+#define WM8400_DC2_ERRACT_WIDTH                      1  /* DC2_ERRACT */
+#define WM8400_DC2_HIB_MODE                     0x0400  /* DC2_HIB_MODE */
+#define WM8400_DC2_HIB_MODE_MASK                0x0400  /* DC2_HIB_MODE */
+#define WM8400_DC2_HIB_MODE_SHIFT                   10  /* DC2_HIB_MODE */
+#define WM8400_DC2_HIB_MODE_WIDTH                    1  /* DC2_HIB_MODE */
+#define WM8400_DC2_SOFTST_MASK                  0x0300  /* DC2_SOFTST - [9:8] */
+#define WM8400_DC2_SOFTST_SHIFT                      8  /* DC2_SOFTST - [9:8] */
+#define WM8400_DC2_SOFTST_WIDTH                      2  /* DC2_SOFTST - [9:8] */
+#define WM8400_DC2_OV_PROT                      0x0080  /* DC2_OV_PROT */
+#define WM8400_DC2_OV_PROT_MASK                 0x0080  /* DC2_OV_PROT */
+#define WM8400_DC2_OV_PROT_SHIFT                     7  /* DC2_OV_PROT */
+#define WM8400_DC2_OV_PROT_WIDTH                     1  /* DC2_OV_PROT */
+#define WM8400_DC2_VSEL_MASK                    0x007F  /* DC2_VSEL - [6:0] */
+#define WM8400_DC2_VSEL_SHIFT                        0  /* DC2_VSEL - [6:0] */
+#define WM8400_DC2_VSEL_WIDTH                        7  /* DC2_VSEL - [6:0] */
+
+/*
+ * R73 (0x49) - DCDC2 Control 2
+ */
+#define WM8400_DC2_FRC_PWM                      0x2000  /* DC2_FRC_PWM */
+#define WM8400_DC2_FRC_PWM_MASK                 0x2000  /* DC2_FRC_PWM */
+#define WM8400_DC2_FRC_PWM_SHIFT                    13  /* DC2_FRC_PWM */
+#define WM8400_DC2_FRC_PWM_WIDTH                     1  /* DC2_FRC_PWM */
+#define WM8400_DC2_STBY_LIM_MASK                0x0300  /* DC2_STBY_LIM - [9:8] */
+#define WM8400_DC2_STBY_LIM_SHIFT                    8  /* DC2_STBY_LIM - [9:8] */
+#define WM8400_DC2_STBY_LIM_WIDTH                    2  /* DC2_STBY_LIM - [9:8] */
+#define WM8400_DC2_ACT_LIM                      0x0080  /* DC2_ACT_LIM */
+#define WM8400_DC2_ACT_LIM_MASK                 0x0080  /* DC2_ACT_LIM */
+#define WM8400_DC2_ACT_LIM_SHIFT                     7  /* DC2_ACT_LIM */
+#define WM8400_DC2_ACT_LIM_WIDTH                     1  /* DC2_ACT_LIM */
+#define WM8400_DC2_VIMG_MASK                    0x007F  /* DC2_VIMG - [6:0] */
+#define WM8400_DC2_VIMG_SHIFT                        0  /* DC2_VIMG - [6:0] */
+#define WM8400_DC2_VIMG_WIDTH                        7  /* DC2_VIMG - [6:0] */
+
+/*
+ * R75 (0x4B) - Interface
+ */
+#define WM8400_AUTOINC                          0x0008  /* AUTOINC */
+#define WM8400_AUTOINC_MASK                     0x0008  /* AUTOINC */
+#define WM8400_AUTOINC_SHIFT                         3  /* AUTOINC */
+#define WM8400_AUTOINC_WIDTH                         1  /* AUTOINC */
+#define WM8400_ARA_ENA                          0x0004  /* ARA_ENA */
+#define WM8400_ARA_ENA_MASK                     0x0004  /* ARA_ENA */
+#define WM8400_ARA_ENA_SHIFT                         2  /* ARA_ENA */
+#define WM8400_ARA_ENA_WIDTH                         1  /* ARA_ENA */
+#define WM8400_SPI_CFG                          0x0002  /* SPI_CFG */
+#define WM8400_SPI_CFG_MASK                     0x0002  /* SPI_CFG */
+#define WM8400_SPI_CFG_SHIFT                         1  /* SPI_CFG */
+#define WM8400_SPI_CFG_WIDTH                         1  /* SPI_CFG */
+
+/*
+ * R76 (0x4C) - PM GENERAL
+ */
+#define WM8400_CODEC_SOFTST                     0x8000  /* CODEC_SOFTST */
+#define WM8400_CODEC_SOFTST_MASK                0x8000  /* CODEC_SOFTST */
+#define WM8400_CODEC_SOFTST_SHIFT                   15  /* CODEC_SOFTST */
+#define WM8400_CODEC_SOFTST_WIDTH                    1  /* CODEC_SOFTST */
+#define WM8400_CODEC_SOFTSD                     0x4000  /* CODEC_SOFTSD */
+#define WM8400_CODEC_SOFTSD_MASK                0x4000  /* CODEC_SOFTSD */
+#define WM8400_CODEC_SOFTSD_SHIFT                   14  /* CODEC_SOFTSD */
+#define WM8400_CODEC_SOFTSD_WIDTH                    1  /* CODEC_SOFTSD */
+#define WM8400_CHIP_SOFTSD                      0x2000  /* CHIP_SOFTSD */
+#define WM8400_CHIP_SOFTSD_MASK                 0x2000  /* CHIP_SOFTSD */
+#define WM8400_CHIP_SOFTSD_SHIFT                    13  /* CHIP_SOFTSD */
+#define WM8400_CHIP_SOFTSD_WIDTH                     1  /* CHIP_SOFTSD */
+#define WM8400_DSLEEP1_POL                      0x0008  /* DSLEEP1_POL */
+#define WM8400_DSLEEP1_POL_MASK                 0x0008  /* DSLEEP1_POL */
+#define WM8400_DSLEEP1_POL_SHIFT                     3  /* DSLEEP1_POL */
+#define WM8400_DSLEEP1_POL_WIDTH                     1  /* DSLEEP1_POL */
+#define WM8400_DSLEEP2_POL                      0x0004  /* DSLEEP2_POL */
+#define WM8400_DSLEEP2_POL_MASK                 0x0004  /* DSLEEP2_POL */
+#define WM8400_DSLEEP2_POL_SHIFT                     2  /* DSLEEP2_POL */
+#define WM8400_DSLEEP2_POL_WIDTH                     1  /* DSLEEP2_POL */
+#define WM8400_PWR_STATE_MASK                   0x0003  /* PWR_STATE - [1:0] */
+#define WM8400_PWR_STATE_SHIFT                       0  /* PWR_STATE - [1:0] */
+#define WM8400_PWR_STATE_WIDTH                       2  /* PWR_STATE - [1:0] */
+
+/*
+ * R78 (0x4E) - PM Shutdown Control
+ */
+#define WM8400_CHIP_GT150_ERRACT                0x0200  /* CHIP_GT150_ERRACT */
+#define WM8400_CHIP_GT150_ERRACT_MASK           0x0200  /* CHIP_GT150_ERRACT */
+#define WM8400_CHIP_GT150_ERRACT_SHIFT               9  /* CHIP_GT150_ERRACT */
+#define WM8400_CHIP_GT150_ERRACT_WIDTH               1  /* CHIP_GT150_ERRACT */
+#define WM8400_CHIP_GT115_ERRACT                0x0100  /* CHIP_GT115_ERRACT */
+#define WM8400_CHIP_GT115_ERRACT_MASK           0x0100  /* CHIP_GT115_ERRACT */
+#define WM8400_CHIP_GT115_ERRACT_SHIFT               8  /* CHIP_GT115_ERRACT */
+#define WM8400_CHIP_GT115_ERRACT_WIDTH               1  /* CHIP_GT115_ERRACT */
+#define WM8400_LINE_CMP_ERRACT                  0x0080  /* LINE_CMP_ERRACT */
+#define WM8400_LINE_CMP_ERRACT_MASK             0x0080  /* LINE_CMP_ERRACT */
+#define WM8400_LINE_CMP_ERRACT_SHIFT                 7  /* LINE_CMP_ERRACT */
+#define WM8400_LINE_CMP_ERRACT_WIDTH                 1  /* LINE_CMP_ERRACT */
+#define WM8400_UVLO_ERRACT                      0x0040  /* UVLO_ERRACT */
+#define WM8400_UVLO_ERRACT_MASK                 0x0040  /* UVLO_ERRACT */
+#define WM8400_UVLO_ERRACT_SHIFT                     6  /* UVLO_ERRACT */
+#define WM8400_UVLO_ERRACT_WIDTH                     1  /* UVLO_ERRACT */
+
+/*
+ * R79 (0x4F) - Interrupt Status 1
+ */
+#define WM8400_MICD_CINT                        0x8000  /* MICD_CINT */
+#define WM8400_MICD_CINT_MASK                   0x8000  /* MICD_CINT */
+#define WM8400_MICD_CINT_SHIFT                      15  /* MICD_CINT */
+#define WM8400_MICD_CINT_WIDTH                       1  /* MICD_CINT */
+#define WM8400_MICSCD_CINT                      0x4000  /* MICSCD_CINT */
+#define WM8400_MICSCD_CINT_MASK                 0x4000  /* MICSCD_CINT */
+#define WM8400_MICSCD_CINT_SHIFT                    14  /* MICSCD_CINT */
+#define WM8400_MICSCD_CINT_WIDTH                     1  /* MICSCD_CINT */
+#define WM8400_JDL_CINT                         0x2000  /* JDL_CINT */
+#define WM8400_JDL_CINT_MASK                    0x2000  /* JDL_CINT */
+#define WM8400_JDL_CINT_SHIFT                       13  /* JDL_CINT */
+#define WM8400_JDL_CINT_WIDTH                        1  /* JDL_CINT */
+#define WM8400_JDR_CINT                         0x1000  /* JDR_CINT */
+#define WM8400_JDR_CINT_MASK                    0x1000  /* JDR_CINT */
+#define WM8400_JDR_CINT_SHIFT                       12  /* JDR_CINT */
+#define WM8400_JDR_CINT_WIDTH                        1  /* JDR_CINT */
+#define WM8400_CODEC_SEQ_END_EINT               0x0800  /* CODEC_SEQ_END_EINT */
+#define WM8400_CODEC_SEQ_END_EINT_MASK          0x0800  /* CODEC_SEQ_END_EINT */
+#define WM8400_CODEC_SEQ_END_EINT_SHIFT             11  /* CODEC_SEQ_END_EINT */
+#define WM8400_CODEC_SEQ_END_EINT_WIDTH              1  /* CODEC_SEQ_END_EINT */
+#define WM8400_CDEL_TO_EINT                     0x0400  /* CDEL_TO_EINT */
+#define WM8400_CDEL_TO_EINT_MASK                0x0400  /* CDEL_TO_EINT */
+#define WM8400_CDEL_TO_EINT_SHIFT                   10  /* CDEL_TO_EINT */
+#define WM8400_CDEL_TO_EINT_WIDTH                    1  /* CDEL_TO_EINT */
+#define WM8400_CHIP_GT150_EINT                  0x0200  /* CHIP_GT150_EINT */
+#define WM8400_CHIP_GT150_EINT_MASK             0x0200  /* CHIP_GT150_EINT */
+#define WM8400_CHIP_GT150_EINT_SHIFT                 9  /* CHIP_GT150_EINT */
+#define WM8400_CHIP_GT150_EINT_WIDTH                 1  /* CHIP_GT150_EINT */
+#define WM8400_CHIP_GT115_EINT                  0x0100  /* CHIP_GT115_EINT */
+#define WM8400_CHIP_GT115_EINT_MASK             0x0100  /* CHIP_GT115_EINT */
+#define WM8400_CHIP_GT115_EINT_SHIFT                 8  /* CHIP_GT115_EINT */
+#define WM8400_CHIP_GT115_EINT_WIDTH                 1  /* CHIP_GT115_EINT */
+#define WM8400_LINE_CMP_EINT                    0x0080  /* LINE_CMP_EINT */
+#define WM8400_LINE_CMP_EINT_MASK               0x0080  /* LINE_CMP_EINT */
+#define WM8400_LINE_CMP_EINT_SHIFT                   7  /* LINE_CMP_EINT */
+#define WM8400_LINE_CMP_EINT_WIDTH                   1  /* LINE_CMP_EINT */
+#define WM8400_UVLO_EINT                        0x0040  /* UVLO_EINT */
+#define WM8400_UVLO_EINT_MASK                   0x0040  /* UVLO_EINT */
+#define WM8400_UVLO_EINT_SHIFT                       6  /* UVLO_EINT */
+#define WM8400_UVLO_EINT_WIDTH                       1  /* UVLO_EINT */
+#define WM8400_DC2_UV_EINT                      0x0020  /* DC2_UV_EINT */
+#define WM8400_DC2_UV_EINT_MASK                 0x0020  /* DC2_UV_EINT */
+#define WM8400_DC2_UV_EINT_SHIFT                     5  /* DC2_UV_EINT */
+#define WM8400_DC2_UV_EINT_WIDTH                     1  /* DC2_UV_EINT */
+#define WM8400_DC1_UV_EINT                      0x0010  /* DC1_UV_EINT */
+#define WM8400_DC1_UV_EINT_MASK                 0x0010  /* DC1_UV_EINT */
+#define WM8400_DC1_UV_EINT_SHIFT                     4  /* DC1_UV_EINT */
+#define WM8400_DC1_UV_EINT_WIDTH                     1  /* DC1_UV_EINT */
+#define WM8400_LDO4_UV_EINT                     0x0008  /* LDO4_UV_EINT */
+#define WM8400_LDO4_UV_EINT_MASK                0x0008  /* LDO4_UV_EINT */
+#define WM8400_LDO4_UV_EINT_SHIFT                    3  /* LDO4_UV_EINT */
+#define WM8400_LDO4_UV_EINT_WIDTH                    1  /* LDO4_UV_EINT */
+#define WM8400_LDO3_UV_EINT                     0x0004  /* LDO3_UV_EINT */
+#define WM8400_LDO3_UV_EINT_MASK                0x0004  /* LDO3_UV_EINT */
+#define WM8400_LDO3_UV_EINT_SHIFT                    2  /* LDO3_UV_EINT */
+#define WM8400_LDO3_UV_EINT_WIDTH                    1  /* LDO3_UV_EINT */
+#define WM8400_LDO2_UV_EINT                     0x0002  /* LDO2_UV_EINT */
+#define WM8400_LDO2_UV_EINT_MASK                0x0002  /* LDO2_UV_EINT */
+#define WM8400_LDO2_UV_EINT_SHIFT                    1  /* LDO2_UV_EINT */
+#define WM8400_LDO2_UV_EINT_WIDTH                    1  /* LDO2_UV_EINT */
+#define WM8400_LDO1_UV_EINT                     0x0001  /* LDO1_UV_EINT */
+#define WM8400_LDO1_UV_EINT_MASK                0x0001  /* LDO1_UV_EINT */
+#define WM8400_LDO1_UV_EINT_SHIFT                    0  /* LDO1_UV_EINT */
+#define WM8400_LDO1_UV_EINT_WIDTH                    1  /* LDO1_UV_EINT */
+
+/*
+ * R80 (0x50) - Interrupt Status 1 Mask
+ */
+#define WM8400_IM_MICD_CINT                     0x8000  /* IM_MICD_CINT */
+#define WM8400_IM_MICD_CINT_MASK                0x8000  /* IM_MICD_CINT */
+#define WM8400_IM_MICD_CINT_SHIFT                   15  /* IM_MICD_CINT */
+#define WM8400_IM_MICD_CINT_WIDTH                    1  /* IM_MICD_CINT */
+#define WM8400_IM_MICSCD_CINT                   0x4000  /* IM_MICSCD_CINT */
+#define WM8400_IM_MICSCD_CINT_MASK              0x4000  /* IM_MICSCD_CINT */
+#define WM8400_IM_MICSCD_CINT_SHIFT                 14  /* IM_MICSCD_CINT */
+#define WM8400_IM_MICSCD_CINT_WIDTH                  1  /* IM_MICSCD_CINT */
+#define WM8400_IM_JDL_CINT                      0x2000  /* IM_JDL_CINT */
+#define WM8400_IM_JDL_CINT_MASK                 0x2000  /* IM_JDL_CINT */
+#define WM8400_IM_JDL_CINT_SHIFT                    13  /* IM_JDL_CINT */
+#define WM8400_IM_JDL_CINT_WIDTH                     1  /* IM_JDL_CINT */
+#define WM8400_IM_JDR_CINT                      0x1000  /* IM_JDR_CINT */
+#define WM8400_IM_JDR_CINT_MASK                 0x1000  /* IM_JDR_CINT */
+#define WM8400_IM_JDR_CINT_SHIFT                    12  /* IM_JDR_CINT */
+#define WM8400_IM_JDR_CINT_WIDTH                     1  /* IM_JDR_CINT */
+#define WM8400_IM_CODEC_SEQ_END_EINT            0x0800  /* IM_CODEC_SEQ_END_EINT */
+#define WM8400_IM_CODEC_SEQ_END_EINT_MASK       0x0800  /* IM_CODEC_SEQ_END_EINT */
+#define WM8400_IM_CODEC_SEQ_END_EINT_SHIFT          11  /* IM_CODEC_SEQ_END_EINT */
+#define WM8400_IM_CODEC_SEQ_END_EINT_WIDTH           1  /* IM_CODEC_SEQ_END_EINT */
+#define WM8400_IM_CDEL_TO_EINT                  0x0400  /* IM_CDEL_TO_EINT */
+#define WM8400_IM_CDEL_TO_EINT_MASK             0x0400  /* IM_CDEL_TO_EINT */
+#define WM8400_IM_CDEL_TO_EINT_SHIFT                10  /* IM_CDEL_TO_EINT */
+#define WM8400_IM_CDEL_TO_EINT_WIDTH                 1  /* IM_CDEL_TO_EINT */
+#define WM8400_IM_CHIP_GT150_EINT               0x0200  /* IM_CHIP_GT150_EINT */
+#define WM8400_IM_CHIP_GT150_EINT_MASK          0x0200  /* IM_CHIP_GT150_EINT */
+#define WM8400_IM_CHIP_GT150_EINT_SHIFT              9  /* IM_CHIP_GT150_EINT */
+#define WM8400_IM_CHIP_GT150_EINT_WIDTH              1  /* IM_CHIP_GT150_EINT */
+#define WM8400_IM_CHIP_GT115_EINT               0x0100  /* IM_CHIP_GT115_EINT */
+#define WM8400_IM_CHIP_GT115_EINT_MASK          0x0100  /* IM_CHIP_GT115_EINT */
+#define WM8400_IM_CHIP_GT115_EINT_SHIFT              8  /* IM_CHIP_GT115_EINT */
+#define WM8400_IM_CHIP_GT115_EINT_WIDTH              1  /* IM_CHIP_GT115_EINT */
+#define WM8400_IM_LINE_CMP_EINT                 0x0080  /* IM_LINE_CMP_EINT */
+#define WM8400_IM_LINE_CMP_EINT_MASK            0x0080  /* IM_LINE_CMP_EINT */
+#define WM8400_IM_LINE_CMP_EINT_SHIFT                7  /* IM_LINE_CMP_EINT */
+#define WM8400_IM_LINE_CMP_EINT_WIDTH                1  /* IM_LINE_CMP_EINT */
+#define WM8400_IM_UVLO_EINT                     0x0040  /* IM_UVLO_EINT */
+#define WM8400_IM_UVLO_EINT_MASK                0x0040  /* IM_UVLO_EINT */
+#define WM8400_IM_UVLO_EINT_SHIFT                    6  /* IM_UVLO_EINT */
+#define WM8400_IM_UVLO_EINT_WIDTH                    1  /* IM_UVLO_EINT */
+#define WM8400_IM_DC2_UV_EINT                   0x0020  /* IM_DC2_UV_EINT */
+#define WM8400_IM_DC2_UV_EINT_MASK              0x0020  /* IM_DC2_UV_EINT */
+#define WM8400_IM_DC2_UV_EINT_SHIFT                  5  /* IM_DC2_UV_EINT */
+#define WM8400_IM_DC2_UV_EINT_WIDTH                  1  /* IM_DC2_UV_EINT */
+#define WM8400_IM_DC1_UV_EINT                   0x0010  /* IM_DC1_UV_EINT */
+#define WM8400_IM_DC1_UV_EINT_MASK              0x0010  /* IM_DC1_UV_EINT */
+#define WM8400_IM_DC1_UV_EINT_SHIFT                  4  /* IM_DC1_UV_EINT */
+#define WM8400_IM_DC1_UV_EINT_WIDTH                  1  /* IM_DC1_UV_EINT */
+#define WM8400_IM_LDO4_UV_EINT                  0x0008  /* IM_LDO4_UV_EINT */
+#define WM8400_IM_LDO4_UV_EINT_MASK             0x0008  /* IM_LDO4_UV_EINT */
+#define WM8400_IM_LDO4_UV_EINT_SHIFT                 3  /* IM_LDO4_UV_EINT */
+#define WM8400_IM_LDO4_UV_EINT_WIDTH                 1  /* IM_LDO4_UV_EINT */
+#define WM8400_IM_LDO3_UV_EINT                  0x0004  /* IM_LDO3_UV_EINT */
+#define WM8400_IM_LDO3_UV_EINT_MASK             0x0004  /* IM_LDO3_UV_EINT */
+#define WM8400_IM_LDO3_UV_EINT_SHIFT                 2  /* IM_LDO3_UV_EINT */
+#define WM8400_IM_LDO3_UV_EINT_WIDTH                 1  /* IM_LDO3_UV_EINT */
+#define WM8400_IM_LDO2_UV_EINT                  0x0002  /* IM_LDO2_UV_EINT */
+#define WM8400_IM_LDO2_UV_EINT_MASK             0x0002  /* IM_LDO2_UV_EINT */
+#define WM8400_IM_LDO2_UV_EINT_SHIFT                 1  /* IM_LDO2_UV_EINT */
+#define WM8400_IM_LDO2_UV_EINT_WIDTH                 1  /* IM_LDO2_UV_EINT */
+#define WM8400_IM_LDO1_UV_EINT                  0x0001  /* IM_LDO1_UV_EINT */
+#define WM8400_IM_LDO1_UV_EINT_MASK             0x0001  /* IM_LDO1_UV_EINT */
+#define WM8400_IM_LDO1_UV_EINT_SHIFT                 0  /* IM_LDO1_UV_EINT */
+#define WM8400_IM_LDO1_UV_EINT_WIDTH                 1  /* IM_LDO1_UV_EINT */
+
+/*
+ * R81 (0x51) - Interrupt Levels
+ */
+#define WM8400_MICD_LVL                         0x8000  /* MICD_LVL */
+#define WM8400_MICD_LVL_MASK                    0x8000  /* MICD_LVL */
+#define WM8400_MICD_LVL_SHIFT                       15  /* MICD_LVL */
+#define WM8400_MICD_LVL_WIDTH                        1  /* MICD_LVL */
+#define WM8400_MICSCD_LVL                       0x4000  /* MICSCD_LVL */
+#define WM8400_MICSCD_LVL_MASK                  0x4000  /* MICSCD_LVL */
+#define WM8400_MICSCD_LVL_SHIFT                     14  /* MICSCD_LVL */
+#define WM8400_MICSCD_LVL_WIDTH                      1  /* MICSCD_LVL */
+#define WM8400_JDL_LVL                          0x2000  /* JDL_LVL */
+#define WM8400_JDL_LVL_MASK                     0x2000  /* JDL_LVL */
+#define WM8400_JDL_LVL_SHIFT                        13  /* JDL_LVL */
+#define WM8400_JDL_LVL_WIDTH                         1  /* JDL_LVL */
+#define WM8400_JDR_LVL                          0x1000  /* JDR_LVL */
+#define WM8400_JDR_LVL_MASK                     0x1000  /* JDR_LVL */
+#define WM8400_JDR_LVL_SHIFT                        12  /* JDR_LVL */
+#define WM8400_JDR_LVL_WIDTH                         1  /* JDR_LVL */
+#define WM8400_CODEC_SEQ_END_LVL                0x0800  /* CODEC_SEQ_END_LVL */
+#define WM8400_CODEC_SEQ_END_LVL_MASK           0x0800  /* CODEC_SEQ_END_LVL */
+#define WM8400_CODEC_SEQ_END_LVL_SHIFT              11  /* CODEC_SEQ_END_LVL */
+#define WM8400_CODEC_SEQ_END_LVL_WIDTH               1  /* CODEC_SEQ_END_LVL */
+#define WM8400_CDEL_TO_LVL                      0x0400  /* CDEL_TO_LVL */
+#define WM8400_CDEL_TO_LVL_MASK                 0x0400  /* CDEL_TO_LVL */
+#define WM8400_CDEL_TO_LVL_SHIFT                    10  /* CDEL_TO_LVL */
+#define WM8400_CDEL_TO_LVL_WIDTH                     1  /* CDEL_TO_LVL */
+#define WM8400_CHIP_GT150_LVL                   0x0200  /* CHIP_GT150_LVL */
+#define WM8400_CHIP_GT150_LVL_MASK              0x0200  /* CHIP_GT150_LVL */
+#define WM8400_CHIP_GT150_LVL_SHIFT                  9  /* CHIP_GT150_LVL */
+#define WM8400_CHIP_GT150_LVL_WIDTH                  1  /* CHIP_GT150_LVL */
+#define WM8400_CHIP_GT115_LVL                   0x0100  /* CHIP_GT115_LVL */
+#define WM8400_CHIP_GT115_LVL_MASK              0x0100  /* CHIP_GT115_LVL */
+#define WM8400_CHIP_GT115_LVL_SHIFT                  8  /* CHIP_GT115_LVL */
+#define WM8400_CHIP_GT115_LVL_WIDTH                  1  /* CHIP_GT115_LVL */
+#define WM8400_LINE_CMP_LVL                     0x0080  /* LINE_CMP_LVL */
+#define WM8400_LINE_CMP_LVL_MASK                0x0080  /* LINE_CMP_LVL */
+#define WM8400_LINE_CMP_LVL_SHIFT                    7  /* LINE_CMP_LVL */
+#define WM8400_LINE_CMP_LVL_WIDTH                    1  /* LINE_CMP_LVL */
+#define WM8400_UVLO_LVL                         0x0040  /* UVLO_LVL */
+#define WM8400_UVLO_LVL_MASK                    0x0040  /* UVLO_LVL */
+#define WM8400_UVLO_LVL_SHIFT                        6  /* UVLO_LVL */
+#define WM8400_UVLO_LVL_WIDTH                        1  /* UVLO_LVL */
+#define WM8400_DC2_UV_LVL                       0x0020  /* DC2_UV_LVL */
+#define WM8400_DC2_UV_LVL_MASK                  0x0020  /* DC2_UV_LVL */
+#define WM8400_DC2_UV_LVL_SHIFT                      5  /* DC2_UV_LVL */
+#define WM8400_DC2_UV_LVL_WIDTH                      1  /* DC2_UV_LVL */
+#define WM8400_DC1_UV_LVL                       0x0010  /* DC1_UV_LVL */
+#define WM8400_DC1_UV_LVL_MASK                  0x0010  /* DC1_UV_LVL */
+#define WM8400_DC1_UV_LVL_SHIFT                      4  /* DC1_UV_LVL */
+#define WM8400_DC1_UV_LVL_WIDTH                      1  /* DC1_UV_LVL */
+#define WM8400_LDO4_UV_LVL                      0x0008  /* LDO4_UV_LVL */
+#define WM8400_LDO4_UV_LVL_MASK                 0x0008  /* LDO4_UV_LVL */
+#define WM8400_LDO4_UV_LVL_SHIFT                     3  /* LDO4_UV_LVL */
+#define WM8400_LDO4_UV_LVL_WIDTH                     1  /* LDO4_UV_LVL */
+#define WM8400_LDO3_UV_LVL                      0x0004  /* LDO3_UV_LVL */
+#define WM8400_LDO3_UV_LVL_MASK                 0x0004  /* LDO3_UV_LVL */
+#define WM8400_LDO3_UV_LVL_SHIFT                     2  /* LDO3_UV_LVL */
+#define WM8400_LDO3_UV_LVL_WIDTH                     1  /* LDO3_UV_LVL */
+#define WM8400_LDO2_UV_LVL                      0x0002  /* LDO2_UV_LVL */
+#define WM8400_LDO2_UV_LVL_MASK                 0x0002  /* LDO2_UV_LVL */
+#define WM8400_LDO2_UV_LVL_SHIFT                     1  /* LDO2_UV_LVL */
+#define WM8400_LDO2_UV_LVL_WIDTH                     1  /* LDO2_UV_LVL */
+#define WM8400_LDO1_UV_LVL                      0x0001  /* LDO1_UV_LVL */
+#define WM8400_LDO1_UV_LVL_MASK                 0x0001  /* LDO1_UV_LVL */
+#define WM8400_LDO1_UV_LVL_SHIFT                     0  /* LDO1_UV_LVL */
+#define WM8400_LDO1_UV_LVL_WIDTH                     1  /* LDO1_UV_LVL */
+
+/*
+ * R82 (0x52) - Shutdown Reason
+ */
+#define WM8400_SDR_CHIP_SOFTSD                  0x2000  /* SDR_CHIP_SOFTSD */
+#define WM8400_SDR_CHIP_SOFTSD_MASK             0x2000  /* SDR_CHIP_SOFTSD */
+#define WM8400_SDR_CHIP_SOFTSD_SHIFT                13  /* SDR_CHIP_SOFTSD */
+#define WM8400_SDR_CHIP_SOFTSD_WIDTH                 1  /* SDR_CHIP_SOFTSD */
+#define WM8400_SDR_NPDN                         0x0800  /* SDR_NPDN */
+#define WM8400_SDR_NPDN_MASK                    0x0800  /* SDR_NPDN */
+#define WM8400_SDR_NPDN_SHIFT                       11  /* SDR_NPDN */
+#define WM8400_SDR_NPDN_WIDTH                        1  /* SDR_NPDN */
+#define WM8400_SDR_CHIP_GT150                   0x0200  /* SDR_CHIP_GT150 */
+#define WM8400_SDR_CHIP_GT150_MASK              0x0200  /* SDR_CHIP_GT150 */
+#define WM8400_SDR_CHIP_GT150_SHIFT                  9  /* SDR_CHIP_GT150 */
+#define WM8400_SDR_CHIP_GT150_WIDTH                  1  /* SDR_CHIP_GT150 */
+#define WM8400_SDR_CHIP_GT115                   0x0100  /* SDR_CHIP_GT115 */
+#define WM8400_SDR_CHIP_GT115_MASK              0x0100  /* SDR_CHIP_GT115 */
+#define WM8400_SDR_CHIP_GT115_SHIFT                  8  /* SDR_CHIP_GT115 */
+#define WM8400_SDR_CHIP_GT115_WIDTH                  1  /* SDR_CHIP_GT115 */
+#define WM8400_SDR_LINE_CMP                     0x0080  /* SDR_LINE_CMP */
+#define WM8400_SDR_LINE_CMP_MASK                0x0080  /* SDR_LINE_CMP */
+#define WM8400_SDR_LINE_CMP_SHIFT                    7  /* SDR_LINE_CMP */
+#define WM8400_SDR_LINE_CMP_WIDTH                    1  /* SDR_LINE_CMP */
+#define WM8400_SDR_UVLO                         0x0040  /* SDR_UVLO */
+#define WM8400_SDR_UVLO_MASK                    0x0040  /* SDR_UVLO */
+#define WM8400_SDR_UVLO_SHIFT                        6  /* SDR_UVLO */
+#define WM8400_SDR_UVLO_WIDTH                        1  /* SDR_UVLO */
+#define WM8400_SDR_DC2_UV                       0x0020  /* SDR_DC2_UV */
+#define WM8400_SDR_DC2_UV_MASK                  0x0020  /* SDR_DC2_UV */
+#define WM8400_SDR_DC2_UV_SHIFT                      5  /* SDR_DC2_UV */
+#define WM8400_SDR_DC2_UV_WIDTH                      1  /* SDR_DC2_UV */
+#define WM8400_SDR_DC1_UV                       0x0010  /* SDR_DC1_UV */
+#define WM8400_SDR_DC1_UV_MASK                  0x0010  /* SDR_DC1_UV */
+#define WM8400_SDR_DC1_UV_SHIFT                      4  /* SDR_DC1_UV */
+#define WM8400_SDR_DC1_UV_WIDTH                      1  /* SDR_DC1_UV */
+#define WM8400_SDR_LDO4_UV                      0x0008  /* SDR_LDO4_UV */
+#define WM8400_SDR_LDO4_UV_MASK                 0x0008  /* SDR_LDO4_UV */
+#define WM8400_SDR_LDO4_UV_SHIFT                     3  /* SDR_LDO4_UV */
+#define WM8400_SDR_LDO4_UV_WIDTH                     1  /* SDR_LDO4_UV */
+#define WM8400_SDR_LDO3_UV                      0x0004  /* SDR_LDO3_UV */
+#define WM8400_SDR_LDO3_UV_MASK                 0x0004  /* SDR_LDO3_UV */
+#define WM8400_SDR_LDO3_UV_SHIFT                     2  /* SDR_LDO3_UV */
+#define WM8400_SDR_LDO3_UV_WIDTH                     1  /* SDR_LDO3_UV */
+#define WM8400_SDR_LDO2_UV                      0x0002  /* SDR_LDO2_UV */
+#define WM8400_SDR_LDO2_UV_MASK                 0x0002  /* SDR_LDO2_UV */
+#define WM8400_SDR_LDO2_UV_SHIFT                     1  /* SDR_LDO2_UV */
+#define WM8400_SDR_LDO2_UV_WIDTH                     1  /* SDR_LDO2_UV */
+#define WM8400_SDR_LDO1_UV                      0x0001  /* SDR_LDO1_UV */
+#define WM8400_SDR_LDO1_UV_MASK                 0x0001  /* SDR_LDO1_UV */
+#define WM8400_SDR_LDO1_UV_SHIFT                     0  /* SDR_LDO1_UV */
+#define WM8400_SDR_LDO1_UV_WIDTH                     1  /* SDR_LDO1_UV */
+
+/*
+ * R84 (0x54) - Line Circuits
+ */
+#define WM8400_BG_LINE_COMP                     0x8000  /* BG_LINE_COMP */
+#define WM8400_BG_LINE_COMP_MASK                0x8000  /* BG_LINE_COMP */
+#define WM8400_BG_LINE_COMP_SHIFT                   15  /* BG_LINE_COMP */
+#define WM8400_BG_LINE_COMP_WIDTH                    1  /* BG_LINE_COMP */
+#define WM8400_LINE_CMP_VTHI_MASK               0x00F0  /* LINE_CMP_VTHI - [7:4] */
+#define WM8400_LINE_CMP_VTHI_SHIFT                   4  /* LINE_CMP_VTHI - [7:4] */
+#define WM8400_LINE_CMP_VTHI_WIDTH                   4  /* LINE_CMP_VTHI - [7:4] */
+#define WM8400_LINE_CMP_VTHD_MASK               0x000F  /* LINE_CMP_VTHD - [3:0] */
+#define WM8400_LINE_CMP_VTHD_SHIFT                   0  /* LINE_CMP_VTHD - [3:0] */
+#define WM8400_LINE_CMP_VTHD_WIDTH                   4  /* LINE_CMP_VTHD - [3:0] */
+
+u16 wm8400_reg_read(struct wm8400 *wm8400, u8 reg);
+int wm8400_block_read(struct wm8400 *wm8400, u8 reg, int count, u16 *data);
+int wm8400_set_bits(struct wm8400 *wm8400, u8 reg, u16 mask, u16 val);
+
+#endif
diff --git a/include/linux/mfd/wm8400.h b/include/linux/mfd/wm8400.h
new file mode 100644 (file)
index 0000000..b46b566
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * wm8400 client interface
+ *
+ * Copyright 2008 Wolfson Microelectronics plc
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef __LINUX_MFD_WM8400_H
+#define __LINUX_MFD_WM8400_H
+
+#include <linux/regulator/machine.h>
+
+#define WM8400_LDO1  0
+#define WM8400_LDO2  1
+#define WM8400_LDO3  2
+#define WM8400_LDO4  3
+#define WM8400_DCDC1 4
+#define WM8400_DCDC2 5
+
+struct wm8400_platform_data {
+       int (*platform_init)(struct device *dev);
+};
+
+int wm8400_register_regulator(struct device *dev, int reg,
+                             struct regulator_init_data *initdata);
+
+#endif
index 3481a7d5bc0a0e143e4d78814e28628804b1fa13..d6a3f47e95cb81251800e85ce9a44d25f4be04ff 100644 (file)
@@ -131,6 +131,16 @@ struct usb_device_id {
 #define USB_DEVICE_ID_MATCH_INT_SUBCLASS       0x0100
 #define USB_DEVICE_ID_MATCH_INT_PROTOCOL       0x0200
 
+#define HID_ANY_ID                             (~0)
+
+struct hid_device_id {
+       __u16 bus;
+       __u32 vendor;
+       __u32 product;
+       kernel_ulong_t driver_data
+               __attribute__((aligned(sizeof(kernel_ulong_t))));
+};
+
 /* s390 CCW devices */
 struct ccw_device_id {
        __u16   match_flags;    /* which fields to match against */
index d3ea3de70a8a54afde98014602504ebd48437624..64875859d6542ffc0a40b23f371a586074d5292e 100644 (file)
@@ -11,7 +11,7 @@
  *             Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
  *             Corey Minyard <wf-rch!minyard@relay.EU.net>
  *             Donald J. Becker, <becker@cesdis.gsfc.nasa.gov>
- *             Alan Cox, <Alan.Cox@linux.org>
+ *             Alan Cox, <alan@lxorguk.ukuu.org.uk>
  *             Bjorn Ekwall. <bj0rn@blox.se>
  *              Pekka Riikonen <priikone@poseidon.pspt.fi>
  *
index 108f47e5fd9511796516d758dca90b1232321bea..21269405ffe27a503209dbb096293221dee65231 100644 (file)
@@ -38,6 +38,7 @@
 #define NFSD_MAY_LOCK          32
 #define NFSD_MAY_OWNER_OVERRIDE        64
 #define NFSD_MAY_LOCAL_ACCESS  128 /* IRIX doing local access check on device special file*/
+#define NFSD_MAY_BYPASS_GSS_ON_ROOT 256
 
 #define NFSD_MAY_CREATE                (NFSD_MAY_EXEC|NFSD_MAY_WRITE)
 #define NFSD_MAY_REMOVE                (NFSD_MAY_EXEC|NFSD_MAY_WRITE|NFSD_MAY_TRUNC)
@@ -125,7 +126,7 @@ int         nfsd_truncate(struct svc_rqst *, struct svc_fh *,
 __be32         nfsd_readdir(struct svc_rqst *, struct svc_fh *,
                             loff_t *, struct readdir_cd *, filldir_t);
 __be32         nfsd_statfs(struct svc_rqst *, struct svc_fh *,
-                               struct kstatfs *);
+                               struct kstatfs *, int access);
 
 int            nfsd_notify_change(struct inode *, struct iattr *);
 __be32         nfsd_permission(struct svc_rqst *, struct svc_export *,
index bb01f8b92b56208bf7b433d842f7d0821647e691..7646637221f3ac45b0e2f9381fd733950b6ff492 100644 (file)
@@ -1,9 +1,13 @@
 #ifndef _LINUX_PFN_H_
 #define _LINUX_PFN_H_
 
+#ifndef __ASSEMBLY__
+#include <linux/types.h>
+#endif
+
 #define PFN_ALIGN(x)   (((unsigned long)(x) + (PAGE_SIZE - 1)) & PAGE_MASK)
 #define PFN_UP(x)      (((x) + PAGE_SIZE-1) >> PAGE_SHIFT)
 #define PFN_DOWN(x)    ((x) >> PAGE_SHIFT)
-#define PFN_PHYS(x)    ((x) << PAGE_SHIFT)
+#define PFN_PHYS(x)    ((phys_addr_t)(x) << PAGE_SHIFT)
 
 #endif
index 1d712c7172a234094b95a24923e7706672ae591a..e37d80561985df64f9497e0aa26a4718e5c24ab5 100644 (file)
@@ -18,8 +18,8 @@
 #include <linux/device.h>
 #include <linux/regulator/consumer.h>
 
-struct regulator_constraints;
 struct regulator_dev;
+struct regulator_init_data;
 
 /**
  * struct regulator_ops - regulator operations.
@@ -51,7 +51,7 @@ struct regulator_ops {
                                          int output_uV, int load_uA);
 
        /* the operations below are for configuration of regulator state when
-        * it's parent PMIC enters a global STANBY/HIBERNATE state */
+        * its parent PMIC enters a global STANDBY/HIBERNATE state */
 
        /* set regulator suspend voltage */
        int (*set_suspend_voltage) (struct regulator_dev *, int uV);
@@ -85,15 +85,17 @@ struct regulator_desc {
        struct module *owner;
 };
 
-
 struct regulator_dev *regulator_register(struct regulator_desc *regulator_desc,
-                                         void *reg_data);
+       struct device *dev, void *driver_data);
 void regulator_unregister(struct regulator_dev *rdev);
 
 int regulator_notifier_call_chain(struct regulator_dev *rdev,
                                  unsigned long event, void *data);
 
 void *rdev_get_drvdata(struct regulator_dev *rdev);
+struct device *rdev_get_dev(struct regulator_dev *rdev);
 int rdev_get_id(struct regulator_dev *rdev);
 
+void *regulator_get_init_drvdata(struct regulator_init_data *reg_init_data);
+
 #endif
index 11e737dbfcf21d32fd29cd1c561e03be2c1c373f..c6d69331a81ef4c78fd6dd6797307d3388cb0a41 100644 (file)
@@ -89,15 +89,33 @@ struct regulation_constraints {
        unsigned apply_uV:1;    /* apply uV constraint iff min == max */
 };
 
-int regulator_set_supply(const char *regulator, const char *regulator_supply);
+/**
+ * struct regulator_consumer_supply - supply -> device mapping
+ *
+ * This maps a supply name to a device.
+ */
+struct regulator_consumer_supply {
+       struct device *dev;     /* consumer */
+       const char *supply;     /* consumer supply - e.g. "vcc" */
+};
 
-const char *regulator_get_supply(const char *regulator);
+/**
+ * struct regulator_init_data - regulator platform initialisation data.
+ *
+ * Initialisation constraints, our supply and consumers supplies.
+ */
+struct regulator_init_data {
+       struct device *supply_regulator_dev; /* or NULL for LINE */
 
-int regulator_set_machine_constraints(const char *regulator,
-       struct regulation_constraints *constraints);
+       struct regulation_constraints constraints;
 
-int regulator_set_device_supply(const char *regulator, struct device *dev,
-                               const char *supply);
+       int num_consumer_supplies;
+       struct regulator_consumer_supply *consumer_supplies;
+
+       /* optional regulator machine specific init */
+       int (*regulator_init)(void *driver_data);
+       void *driver_data;      /* core does not touch this */
+};
 
 int regulator_suspend_prepare(suspend_state_t state);
 
index e5bfe01ee305feb20e70845fb7e7f14e108bd345..6f0ee1b84a4f5f64903f4c74af1004381c3d9dd1 100644 (file)
@@ -104,6 +104,7 @@ struct rpc_create_args {
        const struct rpc_timeout *timeout;
        char                    *servername;
        struct rpc_program      *program;
+       u32                     prognumber;     /* overrides program->number */
        u32                     version;
        rpc_authflavor_t        authflavor;
        unsigned long           flags;
@@ -124,10 +125,10 @@ struct rpc_clnt *rpc_clone_client(struct rpc_clnt *);
 void           rpc_shutdown_client(struct rpc_clnt *);
 void           rpc_release_client(struct rpc_clnt *);
 
-int            rpcb_register(u32, u32, int, unsigned short, int *);
+int            rpcb_register(u32, u32, int, unsigned short);
 int            rpcb_v4_register(const u32 program, const u32 version,
                                 const struct sockaddr *address,
-                                const char *netid, int *result);
+                                const char *netid);
 int            rpcb_getport_sync(struct sockaddr_in *, u32, u32, int);
 void           rpcb_getport_async(struct rpc_task *);
 
index dc69068d94c7e070c9c9758c17efd8eb7d0ccb5f..3afe7fb403b2733877a199485e4c804a8fc116ef 100644 (file)
@@ -66,6 +66,7 @@ struct svc_serv {
        struct list_head        sv_tempsocks;   /* all temporary sockets */
        int                     sv_tmpcnt;      /* count of temporary sockets */
        struct timer_list       sv_temptimer;   /* timer for aging temporary sockets */
+       sa_family_t             sv_family;      /* listener's address family */
 
        char *                  sv_name;        /* service name */
 
@@ -265,17 +266,17 @@ struct svc_rqst {
 /*
  * Rigorous type checking on sockaddr type conversions
  */
-static inline struct sockaddr_in *svc_addr_in(struct svc_rqst *rqst)
+static inline struct sockaddr_in *svc_addr_in(const struct svc_rqst *rqst)
 {
        return (struct sockaddr_in *) &rqst->rq_addr;
 }
 
-static inline struct sockaddr_in6 *svc_addr_in6(struct svc_rqst *rqst)
+static inline struct sockaddr_in6 *svc_addr_in6(const struct svc_rqst *rqst)
 {
        return (struct sockaddr_in6 *) &rqst->rq_addr;
 }
 
-static inline struct sockaddr *svc_addr(struct svc_rqst *rqst)
+static inline struct sockaddr *svc_addr(const struct svc_rqst *rqst)
 {
        return (struct sockaddr *) &rqst->rq_addr;
 }
@@ -381,18 +382,20 @@ struct svc_procedure {
 /*
  * Function prototypes.
  */
-struct svc_serv *  svc_create(struct svc_program *, unsigned int,
-                             void (*shutdown)(struct svc_serv*));
+struct svc_serv *svc_create(struct svc_program *, unsigned int, sa_family_t,
+                           void (*shutdown)(struct svc_serv *));
 struct svc_rqst *svc_prepare_thread(struct svc_serv *serv,
                                        struct svc_pool *pool);
 void              svc_exit_thread(struct svc_rqst *);
 struct svc_serv *  svc_create_pooled(struct svc_program *, unsigned int,
-                       void (*shutdown)(struct svc_serv*), svc_thread_fn,
-                       struct module *);
+                       sa_family_t, void (*shutdown)(struct svc_serv *),
+                       svc_thread_fn, struct module *);
 int               svc_set_num_threads(struct svc_serv *, struct svc_pool *, int);
 void              svc_destroy(struct svc_serv *);
 int               svc_process(struct svc_rqst *);
-int               svc_register(struct svc_serv *, int, unsigned short);
+int               svc_register(const struct svc_serv *, const unsigned short,
+                               const unsigned short);
+
 void              svc_wake_up(struct svc_serv *);
 void              svc_reserve(struct svc_rqst *rqstp, int space);
 struct svc_pool *  svc_pool_for_cpu(struct svc_serv *serv, int cpu);
index dc05b54bd3a3c4f7d7b0e167e219844c0f163f73..c14fe86dac594f069a08342f1790c03909586c41 100644 (file)
@@ -72,6 +72,7 @@ extern atomic_t rdma_stat_sq_prod;
  */
 struct svc_rdma_op_ctxt {
        struct svc_rdma_op_ctxt *read_hdr;
+       struct svc_rdma_fastreg_mr *frmr;
        int hdr_count;
        struct xdr_buf arg;
        struct list_head dto_q;
@@ -103,16 +104,30 @@ struct svc_rdma_chunk_sge {
        int start;              /* sge no for this chunk */
        int count;              /* sge count for this chunk */
 };
+struct svc_rdma_fastreg_mr {
+       struct ib_mr *mr;
+       void *kva;
+       struct ib_fast_reg_page_list *page_list;
+       int page_list_len;
+       unsigned long access_flags;
+       unsigned long map_len;
+       enum dma_data_direction direction;
+       struct list_head frmr_list;
+};
 struct svc_rdma_req_map {
+       struct svc_rdma_fastreg_mr *frmr;
        unsigned long count;
        union {
                struct kvec sge[RPCSVC_MAXPAGES];
                struct svc_rdma_chunk_sge ch[RPCSVC_MAXPAGES];
        };
 };
-
+#define RDMACTXT_F_FAST_UNREG  1
 #define RDMACTXT_F_LAST_CTXT   2
 
+#define        SVCRDMA_DEVCAP_FAST_REG         1       /* fast mr registration */
+#define        SVCRDMA_DEVCAP_READ_W_INV       2       /* read w/ invalidate */
+
 struct svcxprt_rdma {
        struct svc_xprt      sc_xprt;           /* SVC transport structure */
        struct rdma_cm_id    *sc_cm_id;         /* RDMA connection id */
@@ -136,6 +151,11 @@ struct svcxprt_rdma {
        struct ib_cq         *sc_rq_cq;
        struct ib_cq         *sc_sq_cq;
        struct ib_mr         *sc_phys_mr;       /* MR for server memory */
+       u32                  sc_dev_caps;       /* distilled device caps */
+       u32                  sc_dma_lkey;       /* local dma key */
+       unsigned int         sc_frmr_pg_list_len;
+       struct list_head     sc_frmr_q;
+       spinlock_t           sc_frmr_q_lock;
 
        spinlock_t           sc_lock;           /* transport lock */
 
@@ -192,8 +212,13 @@ extern int svc_rdma_post_recv(struct svcxprt_rdma *);
 extern int svc_rdma_create_listen(struct svc_serv *, int, struct sockaddr *);
 extern struct svc_rdma_op_ctxt *svc_rdma_get_context(struct svcxprt_rdma *);
 extern void svc_rdma_put_context(struct svc_rdma_op_ctxt *, int);
+extern void svc_rdma_unmap_dma(struct svc_rdma_op_ctxt *ctxt);
 extern struct svc_rdma_req_map *svc_rdma_get_req_map(void);
 extern void svc_rdma_put_req_map(struct svc_rdma_req_map *);
+extern int svc_rdma_fastreg(struct svcxprt_rdma *, struct svc_rdma_fastreg_mr *);
+extern struct svc_rdma_fastreg_mr *svc_rdma_get_frmr(struct svcxprt_rdma *);
+extern void svc_rdma_put_frmr(struct svcxprt_rdma *,
+                             struct svc_rdma_fastreg_mr *);
 extern void svc_sq_reap(struct svcxprt_rdma *);
 extern void svc_rq_reap(struct svcxprt_rdma *);
 extern struct svc_xprt_class svc_rdma_class;
index 8cff696dedf58413dac70ab00294c19ba898bcba..483e10380aae45429b8ad7aba5a930aa8e4f3695 100644 (file)
@@ -39,10 +39,7 @@ int          svc_send(struct svc_rqst *);
 void           svc_drop(struct svc_rqst *);
 void           svc_sock_update_bufs(struct svc_serv *serv);
 int            svc_sock_names(char *buf, struct svc_serv *serv, char *toclose);
-int            svc_addsock(struct svc_serv *serv,
-                           int fd,
-                           char *name_return,
-                           int *proto);
+int            svc_addsock(struct svc_serv *serv, int fd, char *name_return);
 void           svc_init_xprt_sock(void);
 void           svc_cleanup_xprt_sock(void);
 
index d4a9ce6e2760394a7fb29d4812e03a4728a9a555..f24f7beb47df0bcad9a481f6b14b83df34d12a1a 100644 (file)
@@ -191,12 +191,14 @@ typedef __u32 __bitwise __wsum;
 #ifdef __KERNEL__
 typedef unsigned __bitwise__ gfp_t;
 
-#ifdef CONFIG_RESOURCES_64BIT
-typedef u64 resource_size_t;
+#ifdef CONFIG_PHYS_ADDR_T_64BIT
+typedef u64 phys_addr_t;
 #else
-typedef u32 resource_size_t;
+typedef u32 phys_addr_t;
 #endif
 
+typedef phys_addr_t resource_size_t;
+
 struct ustat {
        __kernel_daddr_t        f_tfree;
        __kernel_ino_t          f_tinode;
index 693d24694a6c9f1839a4f7f2f7828f4671d16459..48b3fadd83ed90f846555079d02e87e9d708af0c 100644 (file)
  * The code for that is here.
  */
 
-static int __initdata raid_noautodetect, raid_autopart;
+#ifdef CONFIG_MD_AUTODETECT
+static int __initdata raid_noautodetect;
+#else
+static int __initdata raid_noautodetect=1;
+#endif
+static int __initdata raid_autopart;
 
 static struct {
        int minor;
@@ -252,6 +257,8 @@ static int __init raid_setup(char *str)
 
                if (!strncmp(str, "noautodetect", wlen))
                        raid_noautodetect = 1;
+               if (!strncmp(str, "autodetect", wlen))
+                       raid_noautodetect = 0;
                if (strncmp(str, "partitionable", wlen)==0)
                        raid_autopart = 1;
                if (strncmp(str, "part", wlen)==0)
@@ -264,17 +271,32 @@ static int __init raid_setup(char *str)
 __setup("raid=", raid_setup);
 __setup("md=", md_setup);
 
+static void autodetect_raid(void)
+{
+       int fd;
+
+       /*
+        * Since we don't want to detect and use half a raid array, we need to
+        * wait for the known devices to complete their probing
+        */
+       printk(KERN_INFO "md: Waiting for all devices to be available before autodetect\n");
+       printk(KERN_INFO "md: If you don't use raid, use raid=noautodetect\n");
+       while (driver_probe_done() < 0)
+               msleep(100);
+       fd = sys_open("/dev/md0", 0, 0);
+       if (fd >= 0) {
+               sys_ioctl(fd, RAID_AUTORUN, raid_autopart);
+               sys_close(fd);
+       }
+}
+
 void __init md_run_setup(void)
 {
        create_dev("/dev/md0", MKDEV(MD_MAJOR, 0));
+
        if (raid_noautodetect)
-               printk(KERN_INFO "md: Skipping autodetection of RAID arrays. (raid=noautodetect)\n");
-       else {
-               int fd = sys_open("/dev/md0", 0, 0);
-               if (fd >= 0) {
-                       sys_ioctl(fd, RAID_AUTORUN, raid_autopart);
-                       sys_close(fd);
-               }
-       }
+               printk(KERN_INFO "md: Skipping autodetection of RAID arrays. (raid=autodetect will force)\n");
+       else
+               autodetect_raid();
        md_setup_drive();
 }
index 3820323c4c8467c07831a8844532fb85976e9a9f..27f6bf6108e96908b10df5b20e0011aaebb3266c 100644 (file)
@@ -708,7 +708,7 @@ int do_one_initcall(initcall_t fn)
        int result;
 
        if (initcall_debug) {
-               printk("calling  %pF\n", fn);
+               printk("calling  %pF @ %i\n", fn, task_pid_nr(current));
                t0 = ktime_get();
        }
 
index a430fd04008b4b9565434ebefa0ee48a62dade90..aee891a869a4f04a4b5c72cedef14d4156da4570 100644 (file)
@@ -577,9 +577,6 @@ static int have_callable_console(void)
  * @fmt: format string
  *
  * This is printk().  It can be called from any context.  We want it to work.
- * Be aware of the fact that if oops_in_progress is not set, we might try to
- * wake klogd up which could deadlock on runqueue lock if printk() is called
- * from scheduler code.
  *
  * We try to grab the console_sem.  If we succeed, it's easy - we log the output and
  * call the console drivers.  If we fail to get the semaphore we place the output
@@ -982,10 +979,25 @@ int is_console_locked(void)
        return console_locked;
 }
 
-void wake_up_klogd(void)
+static DEFINE_PER_CPU(int, printk_pending);
+
+void printk_tick(void)
 {
-       if (!oops_in_progress && waitqueue_active(&log_wait))
+       if (__get_cpu_var(printk_pending)) {
+               __get_cpu_var(printk_pending) = 0;
                wake_up_interruptible(&log_wait);
+       }
+}
+
+int printk_needs_cpu(int cpu)
+{
+       return per_cpu(printk_pending, cpu);
+}
+
+void wake_up_klogd(void)
+{
+       if (waitqueue_active(&log_wait))
+               __raw_get_cpu_var(printk_pending) = 1;
 }
 
 /**
index 414d6fc9131ea3dde1c98087d9cd4ede65cf19f1..7797dae85b50b64e30d85c754ec0bc7bf9b411aa 100644 (file)
@@ -38,10 +38,6 @@ EXPORT_SYMBOL(iomem_resource);
 
 static DEFINE_RWLOCK(resource_lock);
 
-#ifdef CONFIG_PROC_FS
-
-enum { MAX_IORES_LEVEL = 5 };
-
 static void *r_next(struct seq_file *m, void *v, loff_t *pos)
 {
        struct resource *p = v;
@@ -53,6 +49,10 @@ static void *r_next(struct seq_file *m, void *v, loff_t *pos)
        return p->sibling;
 }
 
+#ifdef CONFIG_PROC_FS
+
+enum { MAX_IORES_LEVEL = 5 };
+
 static void *r_start(struct seq_file *m, loff_t *pos)
        __acquires(resource_lock)
 {
@@ -549,13 +549,9 @@ static void __init __reserve_region_with_split(struct resource *root,
        }
 
        if (!res) {
-               printk(KERN_DEBUG "    __reserve_region_with_split: (%s) [%llx, %llx], res: (%s) [%llx, %llx]\n",
-                        conflict->name, conflict->start, conflict->end,
-                        name, start, end);
-
                /* failed, split and try again */
 
-               /* conflict coverred whole area */
+               /* conflict covered whole area */
                if (conflict->start <= start && conflict->end >= end)
                        return;
 
@@ -831,3 +827,40 @@ static int __init reserve_setup(char *str)
 }
 
 __setup("reserve=", reserve_setup);
+
+/*
+ * Check if the requested addr and size spans more than any slot in the
+ * iomem resource tree.
+ */
+int iomem_map_sanity_check(resource_size_t addr, unsigned long size)
+{
+       struct resource *p = &iomem_resource;
+       int err = 0;
+       loff_t l;
+
+       read_lock(&resource_lock);
+       for (p = p->child; p ; p = r_next(NULL, p, &l)) {
+               /*
+                * We can probably skip the resources without
+                * IORESOURCE_IO attribute?
+                */
+               if (p->start >= addr + size)
+                       continue;
+               if (p->end < addr)
+                       continue;
+               if (p->start <= addr && (p->end >= addr + size - 1))
+                       continue;
+               printk(KERN_WARNING "resource map sanity check conflict: "
+                      "0x%llx 0x%llx 0x%llx 0x%llx %s\n",
+                      (unsigned long long)addr,
+                      (unsigned long long)(addr + size - 1),
+                      (unsigned long long)p->start,
+                      (unsigned long long)p->end,
+                      p->name);
+               err = -1;
+               break;
+       }
+       read_unlock(&resource_lock);
+
+       return err;
+}
index c506f266a6b90ee9d887b9eeecdd45061e74ef1f..be7a8292f992ce37ddc9d991abcacf6980a2b546 100644 (file)
@@ -46,7 +46,7 @@ irq_cpustat_t irq_stat[NR_CPUS] ____cacheline_aligned;
 EXPORT_SYMBOL(irq_stat);
 #endif
 
-static struct softirq_action softirq_vec[32] __cacheline_aligned_in_smp;
+static struct softirq_action softirq_vec[NR_SOFTIRQS] __cacheline_aligned_in_smp;
 
 static DEFINE_PER_CPU(struct task_struct *, ksoftirqd);
 
@@ -205,7 +205,18 @@ restart:
 
        do {
                if (pending & 1) {
+                       int prev_count = preempt_count();
+
                        h->action(h);
+
+                       if (unlikely(prev_count != preempt_count())) {
+                               printk(KERN_ERR "huh, entered softirq %d %p"
+                                      "with preempt_count %08x,"
+                                      " exited with %08x?\n", h - softirq_vec,
+                                      h->action, prev_count, preempt_count());
+                               preempt_count() = prev_count;
+                       }
+
                        rcu_bh_qsctr_inc(cpu);
                }
                h++;
index 08d6e1bb99ac351f28bfe768acf49c17e04b0593..503d8d4eb80acb5bc272e6dcb2be7d403955dab3 100644 (file)
@@ -125,6 +125,7 @@ cond_syscall(sys_vm86old);
 cond_syscall(sys_vm86);
 cond_syscall(compat_sys_ipc);
 cond_syscall(compat_sys_sysctl);
+cond_syscall(sys_flock);
 
 /* arch-specific weak syscall entries */
 cond_syscall(sys_pciconfig_read);
index c468c3c6dfc525e0c413eae9f90a8b77d77887f0..cfc5295f1e82bc10b86b6ec6fc861b9fe4bd5ec1 100644 (file)
@@ -96,7 +96,7 @@ static int sixty = 60;
 static int neg_one = -1;
 #endif
 
-#ifdef CONFIG_MMU
+#if defined(CONFIG_MMU) && defined(CONFIG_FILE_LOCKING)
 static int two = 2;
 #endif
 
@@ -1248,6 +1248,7 @@ static struct ctl_table fs_table[] = {
                .extra1         = &minolduid,
                .extra2         = &maxolduid,
        },
+#ifdef CONFIG_FILE_LOCKING
        {
                .ctl_name       = FS_LEASES,
                .procname       = "leases-enable",
@@ -1256,6 +1257,7 @@ static struct ctl_table fs_table[] = {
                .mode           = 0644,
                .proc_handler   = &proc_dointvec,
        },
+#endif
 #ifdef CONFIG_DNOTIFY
        {
                .ctl_name       = FS_DIR_NOTIFY,
@@ -1267,6 +1269,7 @@ static struct ctl_table fs_table[] = {
        },
 #endif
 #ifdef CONFIG_MMU
+#ifdef CONFIG_FILE_LOCKING
        {
                .ctl_name       = FS_LEASE_TIME,
                .procname       = "lease-break-time",
@@ -1278,6 +1281,7 @@ static struct ctl_table fs_table[] = {
                .extra1         = &zero,
                .extra2         = &two,
        },
+#endif
        {
                .procname       = "aio-nr",
                .data           = &aio_nr,
index a4d2193981675aa7b5e2048bf229d5dbdbeccdcd..b711ffcb106c906be19eab01140ec1e8cafd0063 100644 (file)
@@ -270,7 +270,7 @@ void tick_nohz_stop_sched_tick(int inidle)
        next_jiffies = get_next_timer_interrupt(last_jiffies);
        delta_jiffies = next_jiffies - last_jiffies;
 
-       if (rcu_needs_cpu(cpu))
+       if (rcu_needs_cpu(cpu) || printk_needs_cpu(cpu))
                delta_jiffies = 1;
        /*
         * Do not stop the tick, if we are only one off
index 03bc7f1f159350d2ad29ca45d3b3a28fc275f296..510fe69351ca2ec19700802b59ac3dbe9d142019 100644 (file)
@@ -978,6 +978,7 @@ void update_process_times(int user_tick)
        run_local_timers();
        if (rcu_pending(cpu))
                rcu_check_callbacks(cpu, user_tick);
+       printk_tick();
        scheduler_tick();
        run_posix_cpu_timers(p);
 }
index 0bd9c2dbb2a0be8d184b4b9f1d75dc03ec2a14ba..91ee3922510aef5d27d91ad49a01cae866ccf390 100644 (file)
@@ -187,6 +187,9 @@ config RESOURCES_64BIT
        help
          This option allows memory and IO resources to be 64 bit.
 
+config PHYS_ADDR_T_64BIT
+       def_bool 64BIT || ARCH_PHYS_ADDR_T_64BIT
+
 config ZONE_DMA_FLAG
        int
        default "0" if !ZONE_DMA
index b3cfe5a14fcaee0cfb56401286574d1a2e94a74b..70980baeb68297bdc629b27f8ea63f68b0a9e04a 100644 (file)
@@ -1,7 +1,7 @@
 /*
  *     SNAP data link layer. Derived from 802.2
  *
- *             Alan Cox <Alan.Cox@linux.org>,
+ *             Alan Cox <alan@lxorguk.ukuu.org.uk>,
  *             from the 802.2 layer by Greg Page.
  *             Merged in additions from Greg Page's psnap.c.
  *
index 0c850427a85b437e4e82e80b6c5f7f5bc52e06d0..d3134e7e6ee8e3a6960806259ce4266fabdb6a82 100644 (file)
@@ -2,7 +2,7 @@
  *     DDP:    An implementation of the AppleTalk DDP protocol for
  *             Ethernet 'ELAP'.
  *
- *             Alan Cox  <Alan.Cox@linux.org>
+ *             Alan Cox  <alan@lxorguk.ukuu.org.uk>
  *
  *             With more than a little assistance from
  *
@@ -1934,6 +1934,6 @@ static void __exit atalk_exit(void)
 module_exit(atalk_exit);
 
 MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Alan Cox <Alan.Cox@linux.org>");
+MODULE_AUTHOR("Alan Cox <alan@lxorguk.ukuu.org.uk>");
 MODULE_DESCRIPTION("AppleTalk 0.20\n");
 MODULE_ALIAS_NETPROTO(PF_APPLETALK);
index 96434d774c842ff79b84e2d171cdd1e01dd03aaa..acdeab3d980707fbd7740344abd4f7ffe335c75d 100644 (file)
@@ -578,7 +578,7 @@ static int hidp_session(void *arg)
        if (session->hid) {
                if (session->hid->claimed & HID_CLAIMED_INPUT)
                        hidinput_disconnect(session->hid);
-               hid_free_device(session->hid);
+               hid_destroy_device(session->hid);
        }
 
        /* Wakeup user-space polling for socket errors */
@@ -623,9 +623,15 @@ static struct device *hidp_get_device(struct hidp_session *session)
 static int hidp_setup_input(struct hidp_session *session,
                                struct hidp_connadd_req *req)
 {
-       struct input_dev *input = session->input;
+       struct input_dev *input;
        int i;
 
+       input = input_allocate_device();
+       if (!input)
+               return -ENOMEM;
+
+       session->input = input;
+
        input_set_drvdata(input, session);
 
        input->name = "Bluetooth HID Boot Protocol Device";
@@ -677,67 +683,114 @@ static void hidp_close(struct hid_device *hid)
 {
 }
 
-static const struct {
-       __u16 idVendor;
-       __u16 idProduct;
-       unsigned quirks;
-} hidp_blacklist[] = {
-       /* Apple wireless Mighty Mouse */
-       { 0x05ac, 0x030c, HID_QUIRK_MIGHTYMOUSE | HID_QUIRK_INVERT_HWHEEL },
+static int hidp_parse(struct hid_device *hid)
+{
+       struct hidp_session *session = hid->driver_data;
+       struct hidp_connadd_req *req = session->req;
+       unsigned char *buf;
+       int ret;
 
-       { }     /* Terminating entry */
-};
+       buf = kmalloc(req->rd_size, GFP_KERNEL);
+       if (!buf)
+               return -ENOMEM;
+
+       if (copy_from_user(buf, req->rd_data, req->rd_size)) {
+               kfree(buf);
+               return -EFAULT;
+       }
+
+       ret = hid_parse_report(session->hid, buf, req->rd_size);
+
+       kfree(buf);
+
+       if (ret)
+               return ret;
+
+       session->req = NULL;
+
+       return 0;
+}
+
+static int hidp_start(struct hid_device *hid)
+{
+       struct hidp_session *session = hid->driver_data;
+       struct hid_report *report;
 
-static void hidp_setup_quirks(struct hid_device *hid)
+       list_for_each_entry(report, &hid->report_enum[HID_INPUT_REPORT].
+                       report_list, list)
+               hidp_send_report(session, report);
+
+       list_for_each_entry(report, &hid->report_enum[HID_FEATURE_REPORT].
+                       report_list, list)
+               hidp_send_report(session, report);
+
+       return 0;
+}
+
+static void hidp_stop(struct hid_device *hid)
 {
-       unsigned int n;
+       struct hidp_session *session = hid->driver_data;
+
+       skb_queue_purge(&session->ctrl_transmit);
+       skb_queue_purge(&session->intr_transmit);
 
-       for (n = 0; hidp_blacklist[n].idVendor; n++)
-               if (hidp_blacklist[n].idVendor == le16_to_cpu(hid->vendor) &&
-                               hidp_blacklist[n].idProduct == le16_to_cpu(hid->product))
-                       hid->quirks = hidp_blacklist[n].quirks;
+       if (hid->claimed & HID_CLAIMED_INPUT)
+               hidinput_disconnect(hid);
+       hid->claimed = 0;
 }
 
-static void hidp_setup_hid(struct hidp_session *session,
+static struct hid_ll_driver hidp_hid_driver = {
+       .parse = hidp_parse,
+       .start = hidp_start,
+       .stop = hidp_stop,
+       .open  = hidp_open,
+       .close = hidp_close,
+       .hidinput_input_event = hidp_hidinput_event,
+};
+
+static int hidp_setup_hid(struct hidp_session *session,
                                struct hidp_connadd_req *req)
 {
-       struct hid_device *hid = session->hid;
-       struct hid_report *report;
+       struct hid_device *hid;
        bdaddr_t src, dst;
+       int ret;
 
-       baswap(&src, &bt_sk(session->ctrl_sock->sk)->src);
-       baswap(&dst, &bt_sk(session->ctrl_sock->sk)->dst);
+       hid = hid_allocate_device();
+       if (IS_ERR(hid)) {
+               ret = PTR_ERR(session->hid);
+               goto err;
+       }
 
+       session->hid = hid;
+       session->req = req;
        hid->driver_data = session;
 
-       hid->country = req->country;
+       baswap(&src, &bt_sk(session->ctrl_sock->sk)->src);
+       baswap(&dst, &bt_sk(session->ctrl_sock->sk)->dst);
 
        hid->bus     = BUS_BLUETOOTH;
        hid->vendor  = req->vendor;
        hid->product = req->product;
        hid->version = req->version;
+       hid->country = req->country;
 
        strncpy(hid->name, req->name, 128);
        strncpy(hid->phys, batostr(&src), 64);
        strncpy(hid->uniq, batostr(&dst), 64);
 
-       hid->dev = hidp_get_device(session);
-
-       hid->hid_open  = hidp_open;
-       hid->hid_close = hidp_close;
-
-       hid->hidinput_input_event = hidp_hidinput_event;
+       hid->dev.parent = hidp_get_device(session);
+       hid->ll_driver = &hidp_hid_driver;
 
-       hidp_setup_quirks(hid);
+       ret = hid_add_device(hid);
+       if (ret)
+               goto err_hid;
 
-       list_for_each_entry(report, &hid->report_enum[HID_INPUT_REPORT].report_list, list)
-               hidp_send_report(session, report);
-
-       list_for_each_entry(report, &hid->report_enum[HID_FEATURE_REPORT].report_list, list)
-               hidp_send_report(session, report);
-
-       if (hidinput_connect(hid) == 0)
-               hid->claimed |= HID_CLAIMED_INPUT;
+       return 0;
+err_hid:
+       hid_destroy_device(hid);
+       session->hid = NULL;
+err:
+       return ret;
 }
 
 int hidp_add_connection(struct hidp_connadd_req *req, struct socket *ctrl_sock, struct socket *intr_sock)
@@ -757,38 +810,6 @@ int hidp_add_connection(struct hidp_connadd_req *req, struct socket *ctrl_sock,
 
        BT_DBG("rd_data %p rd_size %d", req->rd_data, req->rd_size);
 
-       if (req->rd_size > 0) {
-               unsigned char *buf = kmalloc(req->rd_size, GFP_KERNEL);
-
-               if (!buf) {
-                       kfree(session);
-                       return -ENOMEM;
-               }
-
-               if (copy_from_user(buf, req->rd_data, req->rd_size)) {
-                       kfree(buf);
-                       kfree(session);
-                       return -EFAULT;
-               }
-
-               session->hid = hid_parse_report(buf, req->rd_size);
-
-               kfree(buf);
-
-               if (!session->hid) {
-                       kfree(session);
-                       return -EINVAL;
-               }
-       }
-
-       if (!session->hid) {
-               session->input = input_allocate_device();
-               if (!session->input) {
-                       kfree(session);
-                       return -ENOMEM;
-               }
-       }
-
        down_write(&hidp_session_sem);
 
        s = __hidp_get_session(&bt_sk(ctrl_sock->sk)->dst);
@@ -816,15 +837,18 @@ int hidp_add_connection(struct hidp_connadd_req *req, struct socket *ctrl_sock,
        session->flags   = req->flags & (1 << HIDP_BLUETOOTH_VENDOR_ID);
        session->idle_to = req->idle_to;
 
-       if (session->input) {
+       if (req->rd_size > 0) {
+               err = hidp_setup_hid(session, req);
+               if (err && err != -ENODEV)
+                       goto err_skb;
+       }
+
+       if (!session->hid) {
                err = hidp_setup_input(session, req);
                if (err < 0)
-                       goto failed;
+                       goto err_skb;
        }
 
-       if (session->hid)
-               hidp_setup_hid(session, req);
-
        __hidp_link_session(session);
 
        hidp_set_timer(session);
@@ -850,17 +874,16 @@ unlink:
 
        __hidp_unlink_session(session);
 
-       if (session->input) {
+       if (session->input)
                input_unregister_device(session->input);
-               session->input = NULL; /* don't try to free it here */
-       }
-
+       if (session->hid)
+               hid_destroy_device(session->hid);
+err_skb:
+       skb_queue_purge(&session->ctrl_transmit);
+       skb_queue_purge(&session->intr_transmit);
 failed:
        up_write(&hidp_session_sem);
 
-       if (session->hid)
-               hid_free_device(session->hid);
-
        input_free_device(session->input);
        kfree(session);
        return err;
@@ -950,18 +973,43 @@ int hidp_get_conninfo(struct hidp_conninfo *ci)
        return err;
 }
 
+static const struct hid_device_id hidp_table[] = {
+       { HID_BLUETOOTH_DEVICE(HID_ANY_ID, HID_ANY_ID) },
+       { }
+};
+
+static struct hid_driver hidp_driver = {
+       .name = "generic-bluetooth",
+       .id_table = hidp_table,
+};
+
 static int __init hidp_init(void)
 {
+       int ret;
+
        l2cap_load();
 
        BT_INFO("HIDP (Human Interface Emulation) ver %s", VERSION);
 
-       return hidp_init_sockets();
+       ret = hid_register_driver(&hidp_driver);
+       if (ret)
+               goto err;
+
+       ret = hidp_init_sockets();
+       if (ret)
+               goto err_drv;
+
+       return 0;
+err_drv:
+       hid_unregister_driver(&hidp_driver);
+err:
+       return ret;
 }
 
 static void __exit hidp_exit(void)
 {
        hidp_cleanup_sockets();
+       hid_unregister_driver(&hidp_driver);
 }
 
 module_init(hidp_init);
index 343fb0566b3ee73a244a9ce269e482c8578603fe..e503c89057ad4d2bf4e1a3449a63663553c4393e 100644 (file)
@@ -151,6 +151,8 @@ struct hidp_session {
 
        struct sk_buff_head ctrl_transmit;
        struct sk_buff_head intr_transmit;
+
+       struct hidp_connadd_req *req;
 };
 
 static inline void hidp_schedule(struct hidp_session *session)
index 52f577a0f5440c32b17488c630cfc619ede5c49a..ee631843c2f533de37101e92d05d447f85cf4d1f 100644 (file)
@@ -9,7 +9,7 @@
  *     identical recvmsg() code. So we share it here. The poll was
  *     shared before but buried in udp.c so I moved it.
  *
- *     Authors:        Alan Cox <alan@redhat.com>. (datagram_poll() from old
+ *     Authors:        Alan Cox <alan@lxorguk.ukuu.org.uk>. (datagram_poll() from old
  *                                                  udp.c code)
  *
  *     Fixes:
index 5402b3b38e0d230f9858adb0889b11746d90e2f5..9e2fa39f22a300e1fc46502aac39f305751e69c5 100644 (file)
@@ -6,7 +6,7 @@
  *             Richard Underwood <richard@wuzz.demon.co.uk>
  *
  *     Stir fried together from the IP multicast and CAP patches above
- *             Alan Cox <Alan.Cox@linux.org>
+ *             Alan Cox <alan@lxorguk.ukuu.org.uk>
  *
  *     Fixes:
  *             Alan Cox        :       Update the device on a real delete
index a756847e3814e5fc9e425f105acfd2984eccb33a..99f656d35b4f5556be314cbb3ec4c732228f5709 100644 (file)
@@ -2474,7 +2474,7 @@ static inline int process_ipsec(struct pktgen_dev *pkt_dev,
                                if (ret < 0) {
                                        printk(KERN_ERR "Error expanding "
                                               "ipsec packet %d\n",ret);
-                                       return 0;
+                                       goto err;
                                }
                        }
 
@@ -2484,8 +2484,7 @@ static inline int process_ipsec(struct pktgen_dev *pkt_dev,
                        if (ret) {
                                printk(KERN_ERR "Error creating ipsec "
                                       "packet %d\n",ret);
-                               kfree_skb(skb);
-                               return 0;
+                               goto err;
                        }
                        /* restore ll */
                        eth = (__u8 *) skb_push(skb, ETH_HLEN);
@@ -2494,6 +2493,9 @@ static inline int process_ipsec(struct pktgen_dev *pkt_dev,
                }
        }
        return 1;
+err:
+       kfree_skb(skb);
+       return 0;
 }
 #endif
 
index 7f7bb1a636d935eb4a825d440605e1d35fde3994..4e22e3a35359169f172320337b91bf25ee7e3da7 100644 (file)
@@ -1,7 +1,7 @@
 /*
  *     Routines having to do with the 'struct sk_buff' memory handlers.
  *
- *     Authors:        Alan Cox <iiitac@pyr.swan.ac.uk>
+ *     Authors:        Alan Cox <alan@lxorguk.ukuu.org.uk>
  *                     Florian La Roche <rzsfl@rz.uni-sb.de>
  *
  *     Fixes:
index a6b3437ff082d9cf98b59bb754b409817f3b4597..8727cead64ad5f55bd4980d4964ede930d3242a9 100644 (file)
@@ -9,7 +9,7 @@
  *
  *     Authors:        Arnaldo Carvalho de Melo <acme@conectiva.com.br>
  *                     (from old tcp.c code)
- *                     Alan Cox <alan@redhat.com> (Borrowed comments 8-))
+ *                     Alan Cox <alan@lxorguk.ukuu.org.uk> (Borrowed comments 8-))
  */
 
 #include <linux/module.h>
index cdce4c6c672a03d884ad597fb3b20a4b0af17eb5..49211b35725ba74c5661717522075e7b968f303f 100644 (file)
@@ -1,7 +1,7 @@
 menuconfig NET_DSA
        bool "Distributed Switch Architecture support"
        default n
-       depends on EXPERIMENTAL
+       depends on EXPERIMENTAL && !S390
        select PHYLIB
        ---help---
          This allows you to use hardware switch chips that use
index 55c355e632345c9d3b07b263beea4332e2d36904..72b2de76f1cd1683b9dc33c957597a4556d9b36b 100644 (file)
@@ -1,7 +1,7 @@
 /*
  *     NET3:   Implementation of the ICMP protocol layer.
  *
- *             Alan Cox, <alan@redhat.com>
+ *             Alan Cox, <alan@lxorguk.ukuu.org.uk>
  *
  *     This program is free software; you can redistribute it and/or
  *     modify it under the terms of the GNU General Public License
index 7f9e337e3908c9f643d00f020484f142f52bb6ef..a0d86455c53e3a69fc24adc90fcfcc2e1d293571 100644 (file)
@@ -9,7 +9,7 @@
  *     seems to fall out with gcc 2.6.2.
  *
  *     Authors:
- *             Alan Cox <Alan.Cox@linux.org>
+ *             Alan Cox <alan@lxorguk.ukuu.org.uk>
  *
  *     This program is free software; you can redistribute it and/or
  *     modify it under the terms of the GNU General Public License
index 2152d222b954b3eca22bba260e30a5a551316da0..e4f81f54befee04ad398f6e5a6e3877d388d65bf 100644 (file)
@@ -6,7 +6,7 @@
  *             The IP fragmentation functionality.
  *
  * Authors:    Fred N. van Kempen <waltje@uWalt.NL.Mugnet.ORG>
- *             Alan Cox <Alan.Cox@linux.org>
+ *             Alan Cox <alan@lxorguk.ukuu.org.uk>
  *
  * Fixes:
  *             Alan Cox        :       Split from ip.c , see ip_input.c for history.
index e0bed56c51f1952d9d20d75729f01e83e31e2eed..861978a4f1a8b2f92a79baf34318beb808589a64 100644 (file)
@@ -8,7 +8,7 @@
  * Authors:    Ross Biro
  *             Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
  *             Donald Becker, <becker@super.org>
- *             Alan Cox, <Alan.Cox@linux.org>
+ *             Alan Cox, <alan@lxorguk.ukuu.org.uk>
  *             Richard Underwood
  *             Stefan Becker, <stefanb@yello.ping.de>
  *             Jorge Cwik, <jorge@laser.satlink.net>
index 4c6d2caf92032c4fef47b2cb042e531eae0aa193..29609d29df769350e9d1683209b372a422e9cd3d 100644 (file)
@@ -41,7 +41,7 @@
                Made the tunnels use dev->name not tunnel: when error reporting.
                Added tx_dropped stat
 
-               -Alan Cox       (Alan.Cox@linux.org) 21 March 95
+               -Alan Cox       (alan@lxorguk.ukuu.org.uk) 21 March 95
 
        Reworked:
                Changed to tunnel to destination gateway in addition to the
index c519b8d30eee5c46f4547c3a014b3832d5a090ab..b42e082cc17048ddcdee7103f933c6123c663111 100644 (file)
@@ -1,7 +1,7 @@
 /*
  *     IP multicast routing support for mrouted 3.6/3.8
  *
- *             (c) 1995 Alan Cox, <alan@redhat.com>
+ *             (c) 1995 Alan Cox, <alan@lxorguk.ukuu.org.uk>
  *       Linux Consultancy and Custom Driver Development
  *
  *     This program is free software; you can redistribute it and/or
index eacf4cfef146dc445eef300b852c70545c584574..2095abc3caba90e2883febab41a9b792d405ba5f 100644 (file)
@@ -8,7 +8,7 @@
  * Authors:    Ross Biro
  *             Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
  *             Arnt Gulbrandsen, <agulbra@nvg.unit.no>
- *             Alan Cox, <Alan.Cox@linux.org>
+ *             Alan Cox, <alan@lxorguk.ukuu.org.uk>
  *             Hirokazu Takahashi, <taka@valinux.co.jp>
  *
  * Fixes:
index 050e14b7f7014a2f61854af31defd1483a7eb234..01edac888510cb7f4efb0c1ff994c4978a11bfaf 100644 (file)
@@ -834,7 +834,7 @@ static void __net_exit ipv6_cleanup_mibs(struct net *net)
        snmp_mib_free((void **)net->mib.icmpv6msg_statistics);
 }
 
-static int inet6_net_init(struct net *net)
+static int __net_init inet6_net_init(struct net *net)
 {
        int err = 0;
 
index b0eacc0007cc39b1c19ccf0c38800e0e50a8d608..2fd8afac5f7126955e70152b1f4c4315583ecba1 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * NETLINK      Kernel-user communication protocol.
  *
- *             Authors:        Alan Cox <alan@redhat.com>
+ *             Authors:        Alan Cox <alan@lxorguk.ukuu.org.uk>
  *                             Alexey Kuznetsov <kuznet@ms2.inr.ac.ru>
  *
  *             This program is free software; you can redistribute it and/or
index e5b69556bb5b270f5d0b1250d5c01825ba27ea43..21124ec0a73d64d8b4f937b6a7d93d37a6f81a69 100644 (file)
@@ -16,6 +16,7 @@
 #include <linux/workqueue.h>
 #include <linux/init.h>
 #include <linux/rfkill.h>
+#include <linux/sched.h>
 
 #include "rfkill-input.h"
 
index 76739e928d0da5ff99b748ac61538e2bfc03fbf4..da0789fa1b88876e785e2f8c233fd8f2f3950dbd 100644 (file)
@@ -174,7 +174,7 @@ static struct rpc_clnt * rpc_new_client(const struct rpc_create_args *args, stru
        clnt->cl_procinfo = version->procs;
        clnt->cl_maxproc  = version->nrprocs;
        clnt->cl_protname = program->name;
-       clnt->cl_prog     = program->number;
+       clnt->cl_prog     = args->prognumber ? : program->number;
        clnt->cl_vers     = version->number;
        clnt->cl_stats    = program->stats;
        clnt->cl_metrics  = rpc_alloc_iostats(clnt);
index 24db2b4d12d3e43b3faf944262a10105ecaca898..34abc91058d84ebe5dd89ed481d9542b548c24a7 100644 (file)
@@ -20,6 +20,7 @@
 #include <linux/in6.h>
 #include <linux/kernel.h>
 #include <linux/errno.h>
+#include <net/ipv6.h>
 
 #include <linux/sunrpc/clnt.h>
 #include <linux/sunrpc/sched.h>
@@ -176,13 +177,12 @@ static struct rpc_clnt *rpcb_create(char *hostname, struct sockaddr *srvaddr,
 }
 
 static int rpcb_register_call(struct sockaddr *addr, size_t addrlen,
-                             u32 version, struct rpc_message *msg,
-                             int *result)
+                             u32 version, struct rpc_message *msg)
 {
        struct rpc_clnt *rpcb_clnt;
-       int error = 0;
+       int result, error = 0;
 
-       *result = 0;
+       msg->rpc_resp = &result;
 
        rpcb_clnt = rpcb_create_local(addr, addrlen, version);
        if (!IS_ERR(rpcb_clnt)) {
@@ -191,12 +191,15 @@ static int rpcb_register_call(struct sockaddr *addr, size_t addrlen,
        } else
                error = PTR_ERR(rpcb_clnt);
 
-       if (error < 0)
+       if (error < 0) {
                printk(KERN_WARNING "RPC: failed to contact local rpcbind "
                                "server (errno %d).\n", -error);
-       dprintk("RPC:       registration status %d/%d\n", error, *result);
+               return error;
+       }
 
-       return error;
+       if (!result)
+               return -EACCES;
+       return 0;
 }
 
 /**
@@ -205,7 +208,11 @@ static int rpcb_register_call(struct sockaddr *addr, size_t addrlen,
  * @vers: RPC version number to bind
  * @prot: transport protocol to register
  * @port: port value to register
- * @okay: OUT: result code
+ *
+ * Returns zero if the registration request was dispatched successfully
+ * and the rpcbind daemon returned success.  Otherwise, returns an errno
+ * value that reflects the nature of the error (request could not be
+ * dispatched, timed out, or rpcbind returned an error).
  *
  * RPC services invoke this function to advertise their contact
  * information via the system's rpcbind daemon.  RPC services
@@ -217,15 +224,6 @@ static int rpcb_register_call(struct sockaddr *addr, size_t addrlen,
  * all registered transports for [program, version] from the local
  * rpcbind database.
  *
- * Returns zero if the registration request was dispatched
- * successfully and a reply was received.  The rpcbind daemon's
- * boolean result code is stored in *okay.
- *
- * Returns an errno value and sets *result to zero if there was
- * some problem that prevented the rpcbind request from being
- * dispatched, or if the rpcbind daemon did not respond within
- * the timeout.
- *
  * This function uses rpcbind protocol version 2 to contact the
  * local rpcbind daemon.
  *
@@ -236,7 +234,7 @@ static int rpcb_register_call(struct sockaddr *addr, size_t addrlen,
  * IN6ADDR_ANY (ie available for all AF_INET and AF_INET6
  * addresses).
  */
-int rpcb_register(u32 prog, u32 vers, int prot, unsigned short port, int *okay)
+int rpcb_register(u32 prog, u32 vers, int prot, unsigned short port)
 {
        struct rpcbind_args map = {
                .r_prog         = prog,
@@ -246,7 +244,6 @@ int rpcb_register(u32 prog, u32 vers, int prot, unsigned short port, int *okay)
        };
        struct rpc_message msg = {
                .rpc_argp       = &map,
-               .rpc_resp       = okay,
        };
 
        dprintk("RPC:       %sregistering (%u, %u, %d, %u) with local "
@@ -259,7 +256,7 @@ int rpcb_register(u32 prog, u32 vers, int prot, unsigned short port, int *okay)
 
        return rpcb_register_call((struct sockaddr *)&rpcb_inaddr_loopback,
                                        sizeof(rpcb_inaddr_loopback),
-                                       RPCBVERS_2, &msg, okay);
+                                       RPCBVERS_2, &msg);
 }
 
 /*
@@ -290,7 +287,7 @@ static int rpcb_register_netid4(struct sockaddr_in *address_to_register,
 
        return rpcb_register_call((struct sockaddr *)&rpcb_inaddr_loopback,
                                        sizeof(rpcb_inaddr_loopback),
-                                       RPCBVERS_4, msg, msg->rpc_resp);
+                                       RPCBVERS_4, msg);
 }
 
 /*
@@ -304,10 +301,13 @@ static int rpcb_register_netid6(struct sockaddr_in6 *address_to_register,
        char buf[64];
 
        /* Construct AF_INET6 universal address */
-       snprintf(buf, sizeof(buf),
-                       NIP6_FMT".%u.%u",
-                       NIP6(address_to_register->sin6_addr),
-                       port >> 8, port & 0xff);
+       if (ipv6_addr_any(&address_to_register->sin6_addr))
+               snprintf(buf, sizeof(buf), "::.%u.%u",
+                               port >> 8, port & 0xff);
+       else
+               snprintf(buf, sizeof(buf), NIP6_FMT".%u.%u",
+                               NIP6(address_to_register->sin6_addr),
+                               port >> 8, port & 0xff);
        map->r_addr = buf;
 
        dprintk("RPC:       %sregistering [%u, %u, %s, '%s'] with "
@@ -321,7 +321,7 @@ static int rpcb_register_netid6(struct sockaddr_in6 *address_to_register,
 
        return rpcb_register_call((struct sockaddr *)&rpcb_in6addr_loopback,
                                        sizeof(rpcb_in6addr_loopback),
-                                       RPCBVERS_4, msg, msg->rpc_resp);
+                                       RPCBVERS_4, msg);
 }
 
 /**
@@ -330,7 +330,11 @@ static int rpcb_register_netid6(struct sockaddr_in6 *address_to_register,
  * @version: RPC version number of service to (un)register
  * @address: address family, IP address, and port to (un)register
  * @netid: netid of transport protocol to (un)register
- * @result: result code from rpcbind RPC call
+ *
+ * Returns zero if the registration request was dispatched successfully
+ * and the rpcbind daemon returned success.  Otherwise, returns an errno
+ * value that reflects the nature of the error (request could not be
+ * dispatched, timed out, or rpcbind returned an error).
  *
  * RPC services invoke this function to advertise their contact
  * information via the system's rpcbind daemon.  RPC services
@@ -342,15 +346,6 @@ static int rpcb_register_netid6(struct sockaddr_in6 *address_to_register,
  * to zero.  Callers pass a netid of "" to unregister all
  * transport netids associated with [program, version, address].
  *
- * Returns zero if the registration request was dispatched
- * successfully and a reply was received.  The rpcbind daemon's
- * result code is stored in *result.
- *
- * Returns an errno value and sets *result to zero if there was
- * some problem that prevented the rpcbind request from being
- * dispatched, or if the rpcbind daemon did not respond within
- * the timeout.
- *
  * This function uses rpcbind protocol version 4 to contact the
  * local rpcbind daemon.  The local rpcbind daemon must support
  * version 4 of the rpcbind protocol in order for these functions
@@ -372,8 +367,7 @@ static int rpcb_register_netid6(struct sockaddr_in6 *address_to_register,
  * advertises the service on all IPv4 and IPv6 addresses.
  */
 int rpcb_v4_register(const u32 program, const u32 version,
-                    const struct sockaddr *address, const char *netid,
-                    int *result)
+                    const struct sockaddr *address, const char *netid)
 {
        struct rpcbind_args map = {
                .r_prog         = program,
@@ -383,11 +377,8 @@ int rpcb_v4_register(const u32 program, const u32 version,
        };
        struct rpc_message msg = {
                .rpc_argp       = &map,
-               .rpc_resp       = result,
        };
 
-       *result = 0;
-
        switch (address->sa_family) {
        case AF_INET:
                return rpcb_register_netid4((struct sockaddr_in *)address,
@@ -633,7 +624,7 @@ static void rpcb_getport_done(struct rpc_task *child, void *data)
 static int rpcb_encode_mapping(struct rpc_rqst *req, __be32 *p,
                               struct rpcbind_args *rpcb)
 {
-       dprintk("RPC:       rpcb_encode_mapping(%u, %u, %d, %u)\n",
+       dprintk("RPC:       encoding rpcb request (%u, %u, %d, %u)\n",
                        rpcb->r_prog, rpcb->r_vers, rpcb->r_prot, rpcb->r_port);
        *p++ = htonl(rpcb->r_prog);
        *p++ = htonl(rpcb->r_vers);
@@ -648,7 +639,7 @@ static int rpcb_decode_getport(struct rpc_rqst *req, __be32 *p,
                               unsigned short *portp)
 {
        *portp = (unsigned short) ntohl(*p++);
-       dprintk("RPC:       rpcb_decode_getport result %u\n",
+       dprintk("RPC:       rpcb getport result: %u\n",
                        *portp);
        return 0;
 }
@@ -657,7 +648,7 @@ static int rpcb_decode_set(struct rpc_rqst *req, __be32 *p,
                           unsigned int *boolp)
 {
        *boolp = (unsigned int) ntohl(*p++);
-       dprintk("RPC:       rpcb_decode_set: call %s\n",
+       dprintk("RPC:       rpcb set/unset call %s\n",
                        (*boolp ? "succeeded" : "failed"));
        return 0;
 }
@@ -665,7 +656,7 @@ static int rpcb_decode_set(struct rpc_rqst *req, __be32 *p,
 static int rpcb_encode_getaddr(struct rpc_rqst *req, __be32 *p,
                               struct rpcbind_args *rpcb)
 {
-       dprintk("RPC:       rpcb_encode_getaddr(%u, %u, %s)\n",
+       dprintk("RPC:       encoding rpcb request (%u, %u, %s)\n",
                        rpcb->r_prog, rpcb->r_vers, rpcb->r_addr);
        *p++ = htonl(rpcb->r_prog);
        *p++ = htonl(rpcb->r_vers);
index 5a32cb7c4bb486267a03d15892adc7ce5db93c93..54c98d8768472f8a8442b024d5ef47c913c99511 100644 (file)
@@ -28,6 +28,8 @@
 
 #define RPCDBG_FACILITY        RPCDBG_SVCDSP
 
+static void svc_unregister(const struct svc_serv *serv);
+
 #define svc_serv_is_pooled(serv)    ((serv)->sv_function)
 
 /*
@@ -357,7 +359,7 @@ svc_pool_for_cpu(struct svc_serv *serv, int cpu)
  */
 static struct svc_serv *
 __svc_create(struct svc_program *prog, unsigned int bufsize, int npools,
-          void (*shutdown)(struct svc_serv *serv))
+          sa_family_t family, void (*shutdown)(struct svc_serv *serv))
 {
        struct svc_serv *serv;
        unsigned int vers;
@@ -366,6 +368,7 @@ __svc_create(struct svc_program *prog, unsigned int bufsize, int npools,
 
        if (!(serv = kzalloc(sizeof(*serv), GFP_KERNEL)))
                return NULL;
+       serv->sv_family    = family;
        serv->sv_name      = prog->pg_name;
        serv->sv_program   = prog;
        serv->sv_nrthreads = 1;
@@ -416,30 +419,29 @@ __svc_create(struct svc_program *prog, unsigned int bufsize, int npools,
                spin_lock_init(&pool->sp_lock);
        }
 
-
        /* Remove any stale portmap registrations */
-       svc_register(serv, 0, 0);
+       svc_unregister(serv);
 
        return serv;
 }
 
 struct svc_serv *
 svc_create(struct svc_program *prog, unsigned int bufsize,
-               void (*shutdown)(struct svc_serv *serv))
+               sa_family_t family, void (*shutdown)(struct svc_serv *serv))
 {
-       return __svc_create(prog, bufsize, /*npools*/1, shutdown);
+       return __svc_create(prog, bufsize, /*npools*/1, family, shutdown);
 }
 EXPORT_SYMBOL(svc_create);
 
 struct svc_serv *
 svc_create_pooled(struct svc_program *prog, unsigned int bufsize,
-               void (*shutdown)(struct svc_serv *serv),
+                 sa_family_t family, void (*shutdown)(struct svc_serv *serv),
                  svc_thread_fn func, struct module *mod)
 {
        struct svc_serv *serv;
        unsigned int npools = svc_pool_map_get();
 
-       serv = __svc_create(prog, bufsize, npools, shutdown);
+       serv = __svc_create(prog, bufsize, npools, family, shutdown);
 
        if (serv != NULL) {
                serv->sv_function = func;
@@ -486,8 +488,7 @@ svc_destroy(struct svc_serv *serv)
        if (svc_serv_is_pooled(serv))
                svc_pool_map_put();
 
-       /* Unregister service with the portmapper */
-       svc_register(serv, 0, 0);
+       svc_unregister(serv);
        kfree(serv->sv_pools);
        kfree(serv);
 }
@@ -718,55 +719,245 @@ svc_exit_thread(struct svc_rqst *rqstp)
 }
 EXPORT_SYMBOL(svc_exit_thread);
 
+#ifdef CONFIG_SUNRPC_REGISTER_V4
+
 /*
- * Register an RPC service with the local portmapper.
- * To unregister a service, call this routine with
- * proto and port == 0.
+ * Register an "inet" protocol family netid with the local
+ * rpcbind daemon via an rpcbind v4 SET request.
+ *
+ * No netconfig infrastructure is available in the kernel, so
+ * we map IP_ protocol numbers to netids by hand.
+ *
+ * Returns zero on success; a negative errno value is returned
+ * if any error occurs.
  */
-int
-svc_register(struct svc_serv *serv, int proto, unsigned short port)
+static int __svc_rpcb_register4(const u32 program, const u32 version,
+                               const unsigned short protocol,
+                               const unsigned short port)
+{
+       struct sockaddr_in sin = {
+               .sin_family             = AF_INET,
+               .sin_addr.s_addr        = htonl(INADDR_ANY),
+               .sin_port               = htons(port),
+       };
+       char *netid;
+
+       switch (protocol) {
+       case IPPROTO_UDP:
+               netid = RPCBIND_NETID_UDP;
+               break;
+       case IPPROTO_TCP:
+               netid = RPCBIND_NETID_TCP;
+               break;
+       default:
+               return -EPROTONOSUPPORT;
+       }
+
+       return rpcb_v4_register(program, version,
+                               (struct sockaddr *)&sin, netid);
+}
+
+/*
+ * Register an "inet6" protocol family netid with the local
+ * rpcbind daemon via an rpcbind v4 SET request.
+ *
+ * No netconfig infrastructure is available in the kernel, so
+ * we map IP_ protocol numbers to netids by hand.
+ *
+ * Returns zero on success; a negative errno value is returned
+ * if any error occurs.
+ */
+static int __svc_rpcb_register6(const u32 program, const u32 version,
+                               const unsigned short protocol,
+                               const unsigned short port)
+{
+       struct sockaddr_in6 sin6 = {
+               .sin6_family            = AF_INET6,
+               .sin6_addr              = IN6ADDR_ANY_INIT,
+               .sin6_port              = htons(port),
+       };
+       char *netid;
+
+       switch (protocol) {
+       case IPPROTO_UDP:
+               netid = RPCBIND_NETID_UDP6;
+               break;
+       case IPPROTO_TCP:
+               netid = RPCBIND_NETID_TCP6;
+               break;
+       default:
+               return -EPROTONOSUPPORT;
+       }
+
+       return rpcb_v4_register(program, version,
+                               (struct sockaddr *)&sin6, netid);
+}
+
+/*
+ * Register a kernel RPC service via rpcbind version 4.
+ *
+ * Returns zero on success; a negative errno value is returned
+ * if any error occurs.
+ */
+static int __svc_register(const u32 program, const u32 version,
+                         const sa_family_t family,
+                         const unsigned short protocol,
+                         const unsigned short port)
+{
+       int error;
+
+       switch (family) {
+       case AF_INET:
+               return __svc_rpcb_register4(program, version,
+                                               protocol, port);
+       case AF_INET6:
+               error = __svc_rpcb_register6(program, version,
+                                               protocol, port);
+               if (error < 0)
+                       return error;
+
+               /*
+                * Work around bug in some versions of Linux rpcbind
+                * which don't allow registration of both inet and
+                * inet6 netids.
+                *
+                * Error return ignored for now.
+                */
+               __svc_rpcb_register4(program, version,
+                                               protocol, port);
+               return 0;
+       }
+
+       return -EAFNOSUPPORT;
+}
+
+#else  /* CONFIG_SUNRPC_REGISTER_V4 */
+
+/*
+ * Register a kernel RPC service via rpcbind version 2.
+ *
+ * Returns zero on success; a negative errno value is returned
+ * if any error occurs.
+ */
+static int __svc_register(const u32 program, const u32 version,
+                         sa_family_t family,
+                         const unsigned short protocol,
+                         const unsigned short port)
+{
+       if (family != AF_INET)
+               return -EAFNOSUPPORT;
+
+       return rpcb_register(program, version, protocol, port);
+}
+
+#endif /* CONFIG_SUNRPC_REGISTER_V4 */
+
+/**
+ * svc_register - register an RPC service with the local portmapper
+ * @serv: svc_serv struct for the service to register
+ * @proto: transport protocol number to advertise
+ * @port: port to advertise
+ *
+ * Service is registered for any address in serv's address family
+ */
+int svc_register(const struct svc_serv *serv, const unsigned short proto,
+                const unsigned short port)
 {
        struct svc_program      *progp;
-       unsigned long           flags;
        unsigned int            i;
-       int                     error = 0, dummy;
+       int                     error = 0;
 
-       if (!port)
-               clear_thread_flag(TIF_SIGPENDING);
+       BUG_ON(proto == 0 && port == 0);
 
        for (progp = serv->sv_program; progp; progp = progp->pg_next) {
                for (i = 0; i < progp->pg_nvers; i++) {
                        if (progp->pg_vers[i] == NULL)
                                continue;
 
-                       dprintk("svc: svc_register(%s, %s, %d, %d)%s\n",
+                       dprintk("svc: svc_register(%sv%d, %s, %u, %u)%s\n",
                                        progp->pg_name,
+                                       i,
                                        proto == IPPROTO_UDP?  "udp" : "tcp",
                                        port,
-                                       i,
+                                       serv->sv_family,
                                        progp->pg_vers[i]->vs_hidden?
                                                " (but not telling portmap)" : "");
 
                        if (progp->pg_vers[i]->vs_hidden)
                                continue;
 
-                       error = rpcb_register(progp->pg_prog, i, proto, port, &dummy);
+                       error = __svc_register(progp->pg_prog, i,
+                                               serv->sv_family, proto, port);
                        if (error < 0)
                                break;
-                       if (port && !dummy) {
-                               error = -EACCES;
-                               break;
-                       }
                }
        }
 
-       if (!port) {
-               spin_lock_irqsave(&current->sighand->siglock, flags);
-               recalc_sigpending();
-               spin_unlock_irqrestore(&current->sighand->siglock, flags);
+       return error;
+}
+
+#ifdef CONFIG_SUNRPC_REGISTER_V4
+
+static void __svc_unregister(const u32 program, const u32 version,
+                            const char *progname)
+{
+       struct sockaddr_in6 sin6 = {
+               .sin6_family            = AF_INET6,
+               .sin6_addr              = IN6ADDR_ANY_INIT,
+               .sin6_port              = 0,
+       };
+       int error;
+
+       error = rpcb_v4_register(program, version,
+                               (struct sockaddr *)&sin6, "");
+       dprintk("svc: %s(%sv%u), error %d\n",
+                       __func__, progname, version, error);
+}
+
+#else  /* CONFIG_SUNRPC_REGISTER_V4 */
+
+static void __svc_unregister(const u32 program, const u32 version,
+                            const char *progname)
+{
+       int error;
+
+       error = rpcb_register(program, version, 0, 0);
+       dprintk("svc: %s(%sv%u), error %d\n",
+                       __func__, progname, version, error);
+}
+
+#endif /* CONFIG_SUNRPC_REGISTER_V4 */
+
+/*
+ * All netids, bind addresses and ports registered for [program, version]
+ * are removed from the local rpcbind database (if the service is not
+ * hidden) to make way for a new instance of the service.
+ *
+ * The result of unregistration is reported via dprintk for those who want
+ * verification of the result, but is otherwise not important.
+ */
+static void svc_unregister(const struct svc_serv *serv)
+{
+       struct svc_program *progp;
+       unsigned long flags;
+       unsigned int i;
+
+       clear_thread_flag(TIF_SIGPENDING);
+
+       for (progp = serv->sv_program; progp; progp = progp->pg_next) {
+               for (i = 0; i < progp->pg_nvers; i++) {
+                       if (progp->pg_vers[i] == NULL)
+                               continue;
+                       if (progp->pg_vers[i]->vs_hidden)
+                               continue;
+
+                       __svc_unregister(progp->pg_prog, i, progp->pg_name);
+               }
        }
 
-       return error;
+       spin_lock_irqsave(&current->sighand->siglock, flags);
+       recalc_sigpending();
+       spin_unlock_irqrestore(&current->sighand->siglock, flags);
 }
 
 /*
index e46c825f49548923f79f1363d4a7712ef4ead513..bf5b5cdafebfcc82866369bdb3d895c83379a4e3 100644 (file)
@@ -159,15 +159,44 @@ void svc_xprt_init(struct svc_xprt_class *xcl, struct svc_xprt *xprt,
 }
 EXPORT_SYMBOL_GPL(svc_xprt_init);
 
-int svc_create_xprt(struct svc_serv *serv, char *xprt_name, unsigned short port,
-                   int flags)
+static struct svc_xprt *__svc_xpo_create(struct svc_xprt_class *xcl,
+                                        struct svc_serv *serv,
+                                        unsigned short port, int flags)
 {
-       struct svc_xprt_class *xcl;
        struct sockaddr_in sin = {
                .sin_family             = AF_INET,
                .sin_addr.s_addr        = htonl(INADDR_ANY),
                .sin_port               = htons(port),
        };
+       struct sockaddr_in6 sin6 = {
+               .sin6_family            = AF_INET6,
+               .sin6_addr              = IN6ADDR_ANY_INIT,
+               .sin6_port              = htons(port),
+       };
+       struct sockaddr *sap;
+       size_t len;
+
+       switch (serv->sv_family) {
+       case AF_INET:
+               sap = (struct sockaddr *)&sin;
+               len = sizeof(sin);
+               break;
+       case AF_INET6:
+               sap = (struct sockaddr *)&sin6;
+               len = sizeof(sin6);
+               break;
+       default:
+               return ERR_PTR(-EAFNOSUPPORT);
+       }
+
+       return xcl->xcl_ops->xpo_create(serv, sap, len, flags);
+}
+
+int svc_create_xprt(struct svc_serv *serv, char *xprt_name, unsigned short port,
+                   int flags)
+{
+       struct svc_xprt_class *xcl;
+
        dprintk("svc: creating transport %s[%d]\n", xprt_name, port);
        spin_lock(&svc_xprt_class_lock);
        list_for_each_entry(xcl, &svc_xprt_class_list, xcl_list) {
@@ -180,9 +209,7 @@ int svc_create_xprt(struct svc_serv *serv, char *xprt_name, unsigned short port,
                        goto err;
 
                spin_unlock(&svc_xprt_class_lock);
-               newxprt = xcl->xcl_ops->
-                       xpo_create(serv, (struct sockaddr *)&sin, sizeof(sin),
-                                  flags);
+               newxprt = __svc_xpo_create(xcl, serv, port, flags);
                if (IS_ERR(newxprt)) {
                        module_put(xcl->xcl_owner);
                        return PTR_ERR(newxprt);
index 3e65719f1ef698891d8e81ebea6c57e72b212717..95293f549e9c2b371b82ce32af4ca43be00b5ada 100644 (file)
@@ -1114,6 +1114,7 @@ static struct svc_sock *svc_setup_socket(struct svc_serv *serv,
        struct svc_sock *svsk;
        struct sock     *inet;
        int             pmap_register = !(flags & SVC_SOCK_ANONYMOUS);
+       int             val;
 
        dprintk("svc: svc_setup_socket %p\n", sock);
        if (!(svsk = kzalloc(sizeof(*svsk), GFP_KERNEL))) {
@@ -1146,6 +1147,18 @@ static struct svc_sock *svc_setup_socket(struct svc_serv *serv,
        else
                svc_tcp_init(svsk, serv);
 
+       /*
+        * We start one listener per sv_serv.  We want AF_INET
+        * requests to be automatically shunted to our AF_INET6
+        * listener using a mapped IPv4 address.  Make sure
+        * no-one starts an equivalent IPv4 listener, which
+        * would steal our incoming connections.
+        */
+       val = 0;
+       if (serv->sv_family == AF_INET6)
+               kernel_setsockopt(sock, SOL_IPV6, IPV6_V6ONLY,
+                                       (char *)&val, sizeof(val));
+
        dprintk("svc: svc_setup_socket created %p (inet %p)\n",
                                svsk, svsk->sk_sk);
 
@@ -1154,8 +1167,7 @@ static struct svc_sock *svc_setup_socket(struct svc_serv *serv,
 
 int svc_addsock(struct svc_serv *serv,
                int fd,
-               char *name_return,
-               int *proto)
+               char *name_return)
 {
        int err = 0;
        struct socket *so = sockfd_lookup(fd, &err);
@@ -1190,7 +1202,6 @@ int svc_addsock(struct svc_serv *serv,
                sockfd_put(so);
                return err;
        }
-       if (proto) *proto = so->sk->sk_protocol;
        return one_sock_name(name_return, svsk);
 }
 EXPORT_SYMBOL_GPL(svc_addsock);
index 74de31a066168509db7dba7ab559b29d17dae413..a4756576d68794a1106c4404b39ad9adf510a906 100644 (file)
@@ -116,7 +116,7 @@ static void rdma_build_arg_xdr(struct svc_rqst *rqstp,
  *
  * Assumptions:
  * - chunk[0]->position points to pages[0] at an offset of 0
- * - pages[] is not physically or virtually contigous and consists of
+ * - pages[] is not physically or virtually contiguous and consists of
  *   PAGE_SIZE elements.
  *
  * Output:
@@ -125,7 +125,7 @@ static void rdma_build_arg_xdr(struct svc_rqst *rqstp,
  *   chunk in the read list
  *
  */
-static int rdma_rcl_to_sge(struct svcxprt_rdma *xprt,
+static int map_read_chunks(struct svcxprt_rdma *xprt,
                           struct svc_rqst *rqstp,
                           struct svc_rdma_op_ctxt *head,
                           struct rpcrdma_msg *rmsgp,
@@ -211,26 +211,128 @@ static int rdma_rcl_to_sge(struct svcxprt_rdma *xprt,
        return sge_no;
 }
 
-static void rdma_set_ctxt_sge(struct svcxprt_rdma *xprt,
-                             struct svc_rdma_op_ctxt *ctxt,
-                             struct kvec *vec,
-                             u64 *sgl_offset,
-                             int count)
+/* Map a read-chunk-list to an XDR and fast register the page-list.
+ *
+ * Assumptions:
+ * - chunk[0]  position points to pages[0] at an offset of 0
+ * - pages[]   will be made physically contiguous by creating a one-off memory
+ *             region using the fastreg verb.
+ * - byte_count is # of bytes in read-chunk-list
+ * - ch_count  is # of chunks in read-chunk-list
+ *
+ * Output:
+ * - sge array pointing into pages[] array.
+ * - chunk_sge array specifying sge index and count for each
+ *   chunk in the read list
+ */
+static int fast_reg_read_chunks(struct svcxprt_rdma *xprt,
+                               struct svc_rqst *rqstp,
+                               struct svc_rdma_op_ctxt *head,
+                               struct rpcrdma_msg *rmsgp,
+                               struct svc_rdma_req_map *rpl_map,
+                               struct svc_rdma_req_map *chl_map,
+                               int ch_count,
+                               int byte_count)
+{
+       int page_no;
+       int ch_no;
+       u32 offset;
+       struct rpcrdma_read_chunk *ch;
+       struct svc_rdma_fastreg_mr *frmr;
+       int ret = 0;
+
+       frmr = svc_rdma_get_frmr(xprt);
+       if (IS_ERR(frmr))
+               return -ENOMEM;
+
+       head->frmr = frmr;
+       head->arg.head[0] = rqstp->rq_arg.head[0];
+       head->arg.tail[0] = rqstp->rq_arg.tail[0];
+       head->arg.pages = &head->pages[head->count];
+       head->hdr_count = head->count; /* save count of hdr pages */
+       head->arg.page_base = 0;
+       head->arg.page_len = byte_count;
+       head->arg.len = rqstp->rq_arg.len + byte_count;
+       head->arg.buflen = rqstp->rq_arg.buflen + byte_count;
+
+       /* Fast register the page list */
+       frmr->kva = page_address(rqstp->rq_arg.pages[0]);
+       frmr->direction = DMA_FROM_DEVICE;
+       frmr->access_flags = (IB_ACCESS_LOCAL_WRITE|IB_ACCESS_REMOTE_WRITE);
+       frmr->map_len = byte_count;
+       frmr->page_list_len = PAGE_ALIGN(byte_count) >> PAGE_SHIFT;
+       for (page_no = 0; page_no < frmr->page_list_len; page_no++) {
+               frmr->page_list->page_list[page_no] =
+                       ib_dma_map_single(xprt->sc_cm_id->device,
+                                         page_address(rqstp->rq_arg.pages[page_no]),
+                                         PAGE_SIZE, DMA_TO_DEVICE);
+               if (ib_dma_mapping_error(xprt->sc_cm_id->device,
+                                        frmr->page_list->page_list[page_no]))
+                       goto fatal_err;
+               atomic_inc(&xprt->sc_dma_used);
+               head->arg.pages[page_no] = rqstp->rq_arg.pages[page_no];
+       }
+       head->count += page_no;
+
+       /* rq_respages points one past arg pages */
+       rqstp->rq_respages = &rqstp->rq_arg.pages[page_no];
+
+       /* Create the reply and chunk maps */
+       offset = 0;
+       ch = (struct rpcrdma_read_chunk *)&rmsgp->rm_body.rm_chunks[0];
+       for (ch_no = 0; ch_no < ch_count; ch_no++) {
+               rpl_map->sge[ch_no].iov_base = frmr->kva + offset;
+               rpl_map->sge[ch_no].iov_len = ch->rc_target.rs_length;
+               chl_map->ch[ch_no].count = 1;
+               chl_map->ch[ch_no].start = ch_no;
+               offset += ch->rc_target.rs_length;
+               ch++;
+       }
+
+       ret = svc_rdma_fastreg(xprt, frmr);
+       if (ret)
+               goto fatal_err;
+
+       return ch_no;
+
+ fatal_err:
+       printk("svcrdma: error fast registering xdr for xprt %p", xprt);
+       svc_rdma_put_frmr(xprt, frmr);
+       return -EIO;
+}
+
+static int rdma_set_ctxt_sge(struct svcxprt_rdma *xprt,
+                            struct svc_rdma_op_ctxt *ctxt,
+                            struct svc_rdma_fastreg_mr *frmr,
+                            struct kvec *vec,
+                            u64 *sgl_offset,
+                            int count)
 {
        int i;
 
        ctxt->count = count;
        ctxt->direction = DMA_FROM_DEVICE;
        for (i = 0; i < count; i++) {
-               atomic_inc(&xprt->sc_dma_used);
-               ctxt->sge[i].addr =
-                       ib_dma_map_single(xprt->sc_cm_id->device,
-                                         vec[i].iov_base, vec[i].iov_len,
-                                         DMA_FROM_DEVICE);
+               ctxt->sge[i].length = 0; /* in case map fails */
+               if (!frmr) {
+                       ctxt->sge[i].addr =
+                               ib_dma_map_single(xprt->sc_cm_id->device,
+                                                 vec[i].iov_base,
+                                                 vec[i].iov_len,
+                                                 DMA_FROM_DEVICE);
+                       if (ib_dma_mapping_error(xprt->sc_cm_id->device,
+                                                ctxt->sge[i].addr))
+                               return -EINVAL;
+                       ctxt->sge[i].lkey = xprt->sc_dma_lkey;
+                       atomic_inc(&xprt->sc_dma_used);
+               } else {
+                       ctxt->sge[i].addr = (unsigned long)vec[i].iov_base;
+                       ctxt->sge[i].lkey = frmr->mr->lkey;
+               }
                ctxt->sge[i].length = vec[i].iov_len;
-               ctxt->sge[i].lkey = xprt->sc_phys_mr->lkey;
                *sgl_offset = *sgl_offset + vec[i].iov_len;
        }
+       return 0;
 }
 
 static int rdma_read_max_sge(struct svcxprt_rdma *xprt, int sge_count)
@@ -278,6 +380,7 @@ static int rdma_read_xdr(struct svcxprt_rdma *xprt,
                         struct svc_rdma_op_ctxt *hdr_ctxt)
 {
        struct ib_send_wr read_wr;
+       struct ib_send_wr inv_wr;
        int err = 0;
        int ch_no;
        int ch_count;
@@ -301,9 +404,20 @@ static int rdma_read_xdr(struct svcxprt_rdma *xprt,
        svc_rdma_rcl_chunk_counts(ch, &ch_count, &byte_count);
        if (ch_count > RPCSVC_MAXPAGES)
                return -EINVAL;
-       sge_count = rdma_rcl_to_sge(xprt, rqstp, hdr_ctxt, rmsgp,
-                                   rpl_map, chl_map,
-                                   ch_count, byte_count);
+
+       if (!xprt->sc_frmr_pg_list_len)
+               sge_count = map_read_chunks(xprt, rqstp, hdr_ctxt, rmsgp,
+                                           rpl_map, chl_map, ch_count,
+                                           byte_count);
+       else
+               sge_count = fast_reg_read_chunks(xprt, rqstp, hdr_ctxt, rmsgp,
+                                                rpl_map, chl_map, ch_count,
+                                                byte_count);
+       if (sge_count < 0) {
+               err = -EIO;
+               goto out;
+       }
+
        sgl_offset = 0;
        ch_no = 0;
 
@@ -312,13 +426,16 @@ static int rdma_read_xdr(struct svcxprt_rdma *xprt,
 next_sge:
                ctxt = svc_rdma_get_context(xprt);
                ctxt->direction = DMA_FROM_DEVICE;
+               ctxt->frmr = hdr_ctxt->frmr;
+               ctxt->read_hdr = NULL;
                clear_bit(RDMACTXT_F_LAST_CTXT, &ctxt->flags);
+               clear_bit(RDMACTXT_F_FAST_UNREG, &ctxt->flags);
 
                /* Prepare READ WR */
                memset(&read_wr, 0, sizeof read_wr);
-               ctxt->wr_op = IB_WR_RDMA_READ;
                read_wr.wr_id = (unsigned long)ctxt;
                read_wr.opcode = IB_WR_RDMA_READ;
+               ctxt->wr_op = read_wr.opcode;
                read_wr.send_flags = IB_SEND_SIGNALED;
                read_wr.wr.rdma.rkey = ch->rc_target.rs_handle;
                read_wr.wr.rdma.remote_addr =
@@ -327,10 +444,15 @@ next_sge:
                read_wr.sg_list = ctxt->sge;
                read_wr.num_sge =
                        rdma_read_max_sge(xprt, chl_map->ch[ch_no].count);
-               rdma_set_ctxt_sge(xprt, ctxt,
-                                 &rpl_map->sge[chl_map->ch[ch_no].start],
-                                 &sgl_offset,
-                                 read_wr.num_sge);
+               err = rdma_set_ctxt_sge(xprt, ctxt, hdr_ctxt->frmr,
+                                       &rpl_map->sge[chl_map->ch[ch_no].start],
+                                       &sgl_offset,
+                                       read_wr.num_sge);
+               if (err) {
+                       svc_rdma_unmap_dma(ctxt);
+                       svc_rdma_put_context(ctxt, 0);
+                       goto out;
+               }
                if (((ch+1)->rc_discrim == 0) &&
                    (read_wr.num_sge == chl_map->ch[ch_no].count)) {
                        /*
@@ -339,6 +461,29 @@ next_sge:
                         * the client and the RPC needs to be enqueued.
                         */
                        set_bit(RDMACTXT_F_LAST_CTXT, &ctxt->flags);
+                       if (hdr_ctxt->frmr) {
+                               set_bit(RDMACTXT_F_FAST_UNREG, &ctxt->flags);
+                               /*
+                                * Invalidate the local MR used to map the data
+                                * sink.
+                                */
+                               if (xprt->sc_dev_caps &
+                                   SVCRDMA_DEVCAP_READ_W_INV) {
+                                       read_wr.opcode =
+                                               IB_WR_RDMA_READ_WITH_INV;
+                                       ctxt->wr_op = read_wr.opcode;
+                                       read_wr.ex.invalidate_rkey =
+                                               ctxt->frmr->mr->lkey;
+                               } else {
+                                       /* Prepare INVALIDATE WR */
+                                       memset(&inv_wr, 0, sizeof inv_wr);
+                                       inv_wr.opcode = IB_WR_LOCAL_INV;
+                                       inv_wr.send_flags = IB_SEND_SIGNALED;
+                                       inv_wr.ex.invalidate_rkey =
+                                               hdr_ctxt->frmr->mr->lkey;
+                                       read_wr.next = &inv_wr;
+                               }
+                       }
                        ctxt->read_hdr = hdr_ctxt;
                }
                /* Post the read */
index 84d328329d98575d624557fa75a7e159436ece85..9a7a8e7ae038fbb255dd34880e7882feb8eff322 100644 (file)
  * array is only concerned with the reply we are assured that we have
  * on extra page for the RPCRMDA header.
  */
-static void xdr_to_sge(struct svcxprt_rdma *xprt,
-                      struct xdr_buf *xdr,
-                      struct svc_rdma_req_map *vec)
+int fast_reg_xdr(struct svcxprt_rdma *xprt,
+                struct xdr_buf *xdr,
+                struct svc_rdma_req_map *vec)
+{
+       int sge_no;
+       u32 sge_bytes;
+       u32 page_bytes;
+       u32 page_off;
+       int page_no = 0;
+       u8 *frva;
+       struct svc_rdma_fastreg_mr *frmr;
+
+       frmr = svc_rdma_get_frmr(xprt);
+       if (IS_ERR(frmr))
+               return -ENOMEM;
+       vec->frmr = frmr;
+
+       /* Skip the RPCRDMA header */
+       sge_no = 1;
+
+       /* Map the head. */
+       frva = (void *)((unsigned long)(xdr->head[0].iov_base) & PAGE_MASK);
+       vec->sge[sge_no].iov_base = xdr->head[0].iov_base;
+       vec->sge[sge_no].iov_len = xdr->head[0].iov_len;
+       vec->count = 2;
+       sge_no++;
+
+       /* Build the FRMR */
+       frmr->kva = frva;
+       frmr->direction = DMA_TO_DEVICE;
+       frmr->access_flags = 0;
+       frmr->map_len = PAGE_SIZE;
+       frmr->page_list_len = 1;
+       frmr->page_list->page_list[page_no] =
+               ib_dma_map_single(xprt->sc_cm_id->device,
+                                 (void *)xdr->head[0].iov_base,
+                                 PAGE_SIZE, DMA_TO_DEVICE);
+       if (ib_dma_mapping_error(xprt->sc_cm_id->device,
+                                frmr->page_list->page_list[page_no]))
+               goto fatal_err;
+       atomic_inc(&xprt->sc_dma_used);
+
+       page_off = xdr->page_base;
+       page_bytes = xdr->page_len + page_off;
+       if (!page_bytes)
+               goto encode_tail;
+
+       /* Map the pages */
+       vec->sge[sge_no].iov_base = frva + frmr->map_len + page_off;
+       vec->sge[sge_no].iov_len = page_bytes;
+       sge_no++;
+       while (page_bytes) {
+               struct page *page;
+
+               page = xdr->pages[page_no++];
+               sge_bytes = min_t(u32, page_bytes, (PAGE_SIZE - page_off));
+               page_bytes -= sge_bytes;
+
+               frmr->page_list->page_list[page_no] =
+                       ib_dma_map_page(xprt->sc_cm_id->device, page, 0,
+                                         PAGE_SIZE, DMA_TO_DEVICE);
+               if (ib_dma_mapping_error(xprt->sc_cm_id->device,
+                                        frmr->page_list->page_list[page_no]))
+                       goto fatal_err;
+
+               atomic_inc(&xprt->sc_dma_used);
+               page_off = 0; /* reset for next time through loop */
+               frmr->map_len += PAGE_SIZE;
+               frmr->page_list_len++;
+       }
+       vec->count++;
+
+ encode_tail:
+       /* Map tail */
+       if (0 == xdr->tail[0].iov_len)
+               goto done;
+
+       vec->count++;
+       vec->sge[sge_no].iov_len = xdr->tail[0].iov_len;
+
+       if (((unsigned long)xdr->tail[0].iov_base & PAGE_MASK) ==
+           ((unsigned long)xdr->head[0].iov_base & PAGE_MASK)) {
+               /*
+                * If head and tail use the same page, we don't need
+                * to map it again.
+                */
+               vec->sge[sge_no].iov_base = xdr->tail[0].iov_base;
+       } else {
+               void *va;
+
+               /* Map another page for the tail */
+               page_off = (unsigned long)xdr->tail[0].iov_base & ~PAGE_MASK;
+               va = (void *)((unsigned long)xdr->tail[0].iov_base & PAGE_MASK);
+               vec->sge[sge_no].iov_base = frva + frmr->map_len + page_off;
+
+               frmr->page_list->page_list[page_no] =
+                       ib_dma_map_single(xprt->sc_cm_id->device, va, PAGE_SIZE,
+                                         DMA_TO_DEVICE);
+               if (ib_dma_mapping_error(xprt->sc_cm_id->device,
+                                        frmr->page_list->page_list[page_no]))
+                       goto fatal_err;
+               atomic_inc(&xprt->sc_dma_used);
+               frmr->map_len += PAGE_SIZE;
+               frmr->page_list_len++;
+       }
+
+ done:
+       if (svc_rdma_fastreg(xprt, frmr))
+               goto fatal_err;
+
+       return 0;
+
+ fatal_err:
+       printk("svcrdma: Error fast registering memory for xprt %p\n", xprt);
+       svc_rdma_put_frmr(xprt, frmr);
+       return -EIO;
+}
+
+static int map_xdr(struct svcxprt_rdma *xprt,
+                  struct xdr_buf *xdr,
+                  struct svc_rdma_req_map *vec)
 {
        int sge_max = (xdr->len+PAGE_SIZE-1) / PAGE_SIZE + 3;
        int sge_no;
@@ -83,6 +201,9 @@ static void xdr_to_sge(struct svcxprt_rdma *xprt,
        BUG_ON(xdr->len !=
               (xdr->head[0].iov_len + xdr->page_len + xdr->tail[0].iov_len));
 
+       if (xprt->sc_frmr_pg_list_len)
+               return fast_reg_xdr(xprt, xdr, vec);
+
        /* Skip the first sge, this is for the RPCRDMA header */
        sge_no = 1;
 
@@ -116,9 +237,12 @@ static void xdr_to_sge(struct svcxprt_rdma *xprt,
 
        BUG_ON(sge_no > sge_max);
        vec->count = sge_no;
+       return 0;
 }
 
 /* Assumptions:
+ * - We are using FRMR
+ *     - or -
  * - The specified write_len can be represented in sc_max_sge * PAGE_SIZE
  */
 static int send_write(struct svcxprt_rdma *xprt, struct svc_rqst *rqstp,
@@ -158,30 +282,35 @@ static int send_write(struct svcxprt_rdma *xprt, struct svc_rqst *rqstp,
        sge_no = 0;
 
        /* Copy the remaining SGE */
-       while (bc != 0 && xdr_sge_no < vec->count) {
-               sge[sge_no].lkey = xprt->sc_phys_mr->lkey;
-               sge_bytes = min((size_t)bc,
-                               (size_t)(vec->sge[xdr_sge_no].iov_len-sge_off));
+       while (bc != 0) {
+               sge_bytes = min_t(size_t,
+                         bc, vec->sge[xdr_sge_no].iov_len-sge_off);
                sge[sge_no].length = sge_bytes;
-               atomic_inc(&xprt->sc_dma_used);
-               sge[sge_no].addr =
-                       ib_dma_map_single(xprt->sc_cm_id->device,
-                                         (void *)
-                                         vec->sge[xdr_sge_no].iov_base + sge_off,
-                                         sge_bytes, DMA_TO_DEVICE);
-               if (dma_mapping_error(xprt->sc_cm_id->device->dma_device,
-                                       sge[sge_no].addr))
-                       goto err;
+               if (!vec->frmr) {
+                       sge[sge_no].addr =
+                               ib_dma_map_single(xprt->sc_cm_id->device,
+                                                 (void *)
+                                                 vec->sge[xdr_sge_no].iov_base + sge_off,
+                                                 sge_bytes, DMA_TO_DEVICE);
+                       if (ib_dma_mapping_error(xprt->sc_cm_id->device,
+                                                sge[sge_no].addr))
+                               goto err;
+                       atomic_inc(&xprt->sc_dma_used);
+                       sge[sge_no].lkey = xprt->sc_dma_lkey;
+               } else {
+                       sge[sge_no].addr = (unsigned long)
+                               vec->sge[xdr_sge_no].iov_base + sge_off;
+                       sge[sge_no].lkey = vec->frmr->mr->lkey;
+               }
+               ctxt->count++;
+               ctxt->frmr = vec->frmr;
                sge_off = 0;
                sge_no++;
-               ctxt->count++;
                xdr_sge_no++;
+               BUG_ON(xdr_sge_no > vec->count);
                bc -= sge_bytes;
        }
 
-       BUG_ON(bc != 0);
-       BUG_ON(xdr_sge_no > vec->count);
-
        /* Prepare WRITE WR */
        memset(&write_wr, 0, sizeof write_wr);
        ctxt->wr_op = IB_WR_RDMA_WRITE;
@@ -226,7 +355,10 @@ static int send_write_chunks(struct svcxprt_rdma *xprt,
        res_ary = (struct rpcrdma_write_array *)
                &rdma_resp->rm_body.rm_chunks[1];
 
-       max_write = xprt->sc_max_sge * PAGE_SIZE;
+       if (vec->frmr)
+               max_write = vec->frmr->map_len;
+       else
+               max_write = xprt->sc_max_sge * PAGE_SIZE;
 
        /* Write chunks start at the pagelist */
        for (xdr_off = rqstp->rq_res.head[0].iov_len, chunk_no = 0;
@@ -297,7 +429,10 @@ static int send_reply_chunks(struct svcxprt_rdma *xprt,
        res_ary = (struct rpcrdma_write_array *)
                &rdma_resp->rm_body.rm_chunks[2];
 
-       max_write = xprt->sc_max_sge * PAGE_SIZE;
+       if (vec->frmr)
+               max_write = vec->frmr->map_len;
+       else
+               max_write = xprt->sc_max_sge * PAGE_SIZE;
 
        /* xdr offset starts at RPC message */
        for (xdr_off = 0, chunk_no = 0;
@@ -307,7 +442,6 @@ static int send_reply_chunks(struct svcxprt_rdma *xprt,
                ch = &arg_ary->wc_array[chunk_no].wc_target;
                write_len = min(xfer_len, ch->rs_length);
 
-
                /* Prepare the reply chunk given the length actually
                 * written */
                rs_offset = get_unaligned(&(ch->rs_offset));
@@ -366,6 +500,7 @@ static int send_reply(struct svcxprt_rdma *rdma,
                      int byte_count)
 {
        struct ib_send_wr send_wr;
+       struct ib_send_wr inv_wr;
        int sge_no;
        int sge_bytes;
        int page_no;
@@ -385,27 +520,45 @@ static int send_reply(struct svcxprt_rdma *rdma,
        /* Prepare the context */
        ctxt->pages[0] = page;
        ctxt->count = 1;
+       ctxt->frmr = vec->frmr;
+       if (vec->frmr)
+               set_bit(RDMACTXT_F_FAST_UNREG, &ctxt->flags);
+       else
+               clear_bit(RDMACTXT_F_FAST_UNREG, &ctxt->flags);
 
        /* Prepare the SGE for the RPCRDMA Header */
-       atomic_inc(&rdma->sc_dma_used);
        ctxt->sge[0].addr =
                ib_dma_map_page(rdma->sc_cm_id->device,
                                page, 0, PAGE_SIZE, DMA_TO_DEVICE);
+       if (ib_dma_mapping_error(rdma->sc_cm_id->device, ctxt->sge[0].addr))
+               goto err;
+       atomic_inc(&rdma->sc_dma_used);
+
        ctxt->direction = DMA_TO_DEVICE;
+
        ctxt->sge[0].length = svc_rdma_xdr_get_reply_hdr_len(rdma_resp);
-       ctxt->sge[0].lkey = rdma->sc_phys_mr->lkey;
+       ctxt->sge[0].lkey = rdma->sc_dma_lkey;
 
        /* Determine how many of our SGE are to be transmitted */
        for (sge_no = 1; byte_count && sge_no < vec->count; sge_no++) {
                sge_bytes = min_t(size_t, vec->sge[sge_no].iov_len, byte_count);
                byte_count -= sge_bytes;
-               atomic_inc(&rdma->sc_dma_used);
-               ctxt->sge[sge_no].addr =
-                       ib_dma_map_single(rdma->sc_cm_id->device,
-                                         vec->sge[sge_no].iov_base,
-                                         sge_bytes, DMA_TO_DEVICE);
+               if (!vec->frmr) {
+                       ctxt->sge[sge_no].addr =
+                               ib_dma_map_single(rdma->sc_cm_id->device,
+                                                 vec->sge[sge_no].iov_base,
+                                                 sge_bytes, DMA_TO_DEVICE);
+                       if (ib_dma_mapping_error(rdma->sc_cm_id->device,
+                                                ctxt->sge[sge_no].addr))
+                               goto err;
+                       atomic_inc(&rdma->sc_dma_used);
+                       ctxt->sge[sge_no].lkey = rdma->sc_dma_lkey;
+               } else {
+                       ctxt->sge[sge_no].addr = (unsigned long)
+                               vec->sge[sge_no].iov_base;
+                       ctxt->sge[sge_no].lkey = vec->frmr->mr->lkey;
+               }
                ctxt->sge[sge_no].length = sge_bytes;
-               ctxt->sge[sge_no].lkey = rdma->sc_phys_mr->lkey;
        }
        BUG_ON(byte_count != 0);
 
@@ -417,11 +570,16 @@ static int send_reply(struct svcxprt_rdma *rdma,
                ctxt->pages[page_no+1] = rqstp->rq_respages[page_no];
                ctxt->count++;
                rqstp->rq_respages[page_no] = NULL;
-               /* If there are more pages than SGE, terminate SGE list */
+               /*
+                * If there are more pages than SGE, terminate SGE
+                * list so that svc_rdma_unmap_dma doesn't attempt to
+                * unmap garbage.
+                */
                if (page_no+1 >= sge_no)
                        ctxt->sge[page_no+1].length = 0;
        }
        BUG_ON(sge_no > rdma->sc_max_sge);
+       BUG_ON(sge_no > ctxt->count);
        memset(&send_wr, 0, sizeof send_wr);
        ctxt->wr_op = IB_WR_SEND;
        send_wr.wr_id = (unsigned long)ctxt;
@@ -429,12 +587,26 @@ static int send_reply(struct svcxprt_rdma *rdma,
        send_wr.num_sge = sge_no;
        send_wr.opcode = IB_WR_SEND;
        send_wr.send_flags =  IB_SEND_SIGNALED;
+       if (vec->frmr) {
+               /* Prepare INVALIDATE WR */
+               memset(&inv_wr, 0, sizeof inv_wr);
+               inv_wr.opcode = IB_WR_LOCAL_INV;
+               inv_wr.send_flags = IB_SEND_SIGNALED;
+               inv_wr.ex.invalidate_rkey =
+                       vec->frmr->mr->lkey;
+               send_wr.next = &inv_wr;
+       }
 
        ret = svc_rdma_send(rdma, &send_wr);
        if (ret)
-               svc_rdma_put_context(ctxt, 1);
+               goto err;
 
-       return ret;
+       return 0;
+
+ err:
+       svc_rdma_put_frmr(rdma, vec->frmr);
+       svc_rdma_put_context(ctxt, 1);
+       return -EIO;
 }
 
 void svc_rdma_prep_reply_hdr(struct svc_rqst *rqstp)
@@ -477,8 +649,9 @@ int svc_rdma_sendto(struct svc_rqst *rqstp)
        ctxt = svc_rdma_get_context(rdma);
        ctxt->direction = DMA_TO_DEVICE;
        vec = svc_rdma_get_req_map();
-       xdr_to_sge(rdma, &rqstp->rq_res, vec);
-
+       ret = map_xdr(rdma, &rqstp->rq_res, vec);
+       if (ret)
+               goto err0;
        inline_bytes = rqstp->rq_res.len;
 
        /* Create the RDMA response header */
@@ -498,7 +671,7 @@ int svc_rdma_sendto(struct svc_rqst *rqstp)
        if (ret < 0) {
                printk(KERN_ERR "svcrdma: failed to send write chunks, rc=%d\n",
                       ret);
-               goto error;
+               goto err1;
        }
        inline_bytes -= ret;
 
@@ -508,7 +681,7 @@ int svc_rdma_sendto(struct svc_rqst *rqstp)
        if (ret < 0) {
                printk(KERN_ERR "svcrdma: failed to send reply chunks, rc=%d\n",
                       ret);
-               goto error;
+               goto err1;
        }
        inline_bytes -= ret;
 
@@ -517,9 +690,11 @@ int svc_rdma_sendto(struct svc_rqst *rqstp)
        svc_rdma_put_req_map(vec);
        dprintk("svcrdma: send_reply returns %d\n", ret);
        return ret;
- error:
+
+ err1:
+       put_page(res_page);
+ err0:
        svc_rdma_put_req_map(vec);
        svc_rdma_put_context(ctxt, 0);
-       put_page(res_page);
        return ret;
 }
index 900cb69728c691537e14e85e6f4618c0b0f79cfe..6fb493cbd29fcd2786479965bb526ba343e1b70f 100644 (file)
@@ -100,20 +100,29 @@ struct svc_rdma_op_ctxt *svc_rdma_get_context(struct svcxprt_rdma *xprt)
        ctxt->xprt = xprt;
        INIT_LIST_HEAD(&ctxt->dto_q);
        ctxt->count = 0;
+       ctxt->frmr = NULL;
        atomic_inc(&xprt->sc_ctxt_used);
        return ctxt;
 }
 
-static void svc_rdma_unmap_dma(struct svc_rdma_op_ctxt *ctxt)
+void svc_rdma_unmap_dma(struct svc_rdma_op_ctxt *ctxt)
 {
        struct svcxprt_rdma *xprt = ctxt->xprt;
        int i;
        for (i = 0; i < ctxt->count && ctxt->sge[i].length; i++) {
-               atomic_dec(&xprt->sc_dma_used);
-               ib_dma_unmap_single(xprt->sc_cm_id->device,
-                                   ctxt->sge[i].addr,
-                                   ctxt->sge[i].length,
-                                   ctxt->direction);
+               /*
+                * Unmap the DMA addr in the SGE if the lkey matches
+                * the sc_dma_lkey, otherwise, ignore it since it is
+                * an FRMR lkey and will be unmapped later when the
+                * last WR that uses it completes.
+                */
+               if (ctxt->sge[i].lkey == xprt->sc_dma_lkey) {
+                       atomic_dec(&xprt->sc_dma_used);
+                       ib_dma_unmap_single(xprt->sc_cm_id->device,
+                                           ctxt->sge[i].addr,
+                                           ctxt->sge[i].length,
+                                           ctxt->direction);
+               }
        }
 }
 
@@ -150,6 +159,7 @@ struct svc_rdma_req_map *svc_rdma_get_req_map(void)
                schedule_timeout_uninterruptible(msecs_to_jiffies(500));
        }
        map->count = 0;
+       map->frmr = NULL;
        return map;
 }
 
@@ -315,6 +325,50 @@ static void rq_cq_reap(struct svcxprt_rdma *xprt)
                svc_xprt_enqueue(&xprt->sc_xprt);
 }
 
+/*
+ * Processs a completion context
+ */
+static void process_context(struct svcxprt_rdma *xprt,
+                           struct svc_rdma_op_ctxt *ctxt)
+{
+       svc_rdma_unmap_dma(ctxt);
+
+       switch (ctxt->wr_op) {
+       case IB_WR_SEND:
+               if (test_bit(RDMACTXT_F_FAST_UNREG, &ctxt->flags))
+                       svc_rdma_put_frmr(xprt, ctxt->frmr);
+               svc_rdma_put_context(ctxt, 1);
+               break;
+
+       case IB_WR_RDMA_WRITE:
+               svc_rdma_put_context(ctxt, 0);
+               break;
+
+       case IB_WR_RDMA_READ:
+       case IB_WR_RDMA_READ_WITH_INV:
+               if (test_bit(RDMACTXT_F_LAST_CTXT, &ctxt->flags)) {
+                       struct svc_rdma_op_ctxt *read_hdr = ctxt->read_hdr;
+                       BUG_ON(!read_hdr);
+                       if (test_bit(RDMACTXT_F_FAST_UNREG, &ctxt->flags))
+                               svc_rdma_put_frmr(xprt, ctxt->frmr);
+                       spin_lock_bh(&xprt->sc_rq_dto_lock);
+                       set_bit(XPT_DATA, &xprt->sc_xprt.xpt_flags);
+                       list_add_tail(&read_hdr->dto_q,
+                                     &xprt->sc_read_complete_q);
+                       spin_unlock_bh(&xprt->sc_rq_dto_lock);
+                       svc_xprt_enqueue(&xprt->sc_xprt);
+               }
+               svc_rdma_put_context(ctxt, 0);
+               break;
+
+       default:
+               printk(KERN_ERR "svcrdma: unexpected completion type, "
+                      "opcode=%d\n",
+                      ctxt->wr_op);
+               break;
+       }
+}
+
 /*
  * Send Queue Completion Handler - potentially called on interrupt context.
  *
@@ -327,17 +381,12 @@ static void sq_cq_reap(struct svcxprt_rdma *xprt)
        struct ib_cq *cq = xprt->sc_sq_cq;
        int ret;
 
-
        if (!test_and_clear_bit(RDMAXPRT_SQ_PENDING, &xprt->sc_flags))
                return;
 
        ib_req_notify_cq(xprt->sc_sq_cq, IB_CQ_NEXT_COMP);
        atomic_inc(&rdma_stat_sq_poll);
        while ((ret = ib_poll_cq(cq, 1, &wc)) > 0) {
-               ctxt = (struct svc_rdma_op_ctxt *)(unsigned long)wc.wr_id;
-               xprt = ctxt->xprt;
-
-               svc_rdma_unmap_dma(ctxt);
                if (wc.status != IB_WC_SUCCESS)
                        /* Close the transport */
                        set_bit(XPT_CLOSE, &xprt->sc_xprt.xpt_flags);
@@ -346,35 +395,10 @@ static void sq_cq_reap(struct svcxprt_rdma *xprt)
                atomic_dec(&xprt->sc_sq_count);
                wake_up(&xprt->sc_send_wait);
 
-               switch (ctxt->wr_op) {
-               case IB_WR_SEND:
-                       svc_rdma_put_context(ctxt, 1);
-                       break;
-
-               case IB_WR_RDMA_WRITE:
-                       svc_rdma_put_context(ctxt, 0);
-                       break;
-
-               case IB_WR_RDMA_READ:
-                       if (test_bit(RDMACTXT_F_LAST_CTXT, &ctxt->flags)) {
-                               struct svc_rdma_op_ctxt *read_hdr = ctxt->read_hdr;
-                               BUG_ON(!read_hdr);
-                               spin_lock_bh(&xprt->sc_rq_dto_lock);
-                               set_bit(XPT_DATA, &xprt->sc_xprt.xpt_flags);
-                               list_add_tail(&read_hdr->dto_q,
-                                             &xprt->sc_read_complete_q);
-                               spin_unlock_bh(&xprt->sc_rq_dto_lock);
-                               svc_xprt_enqueue(&xprt->sc_xprt);
-                       }
-                       svc_rdma_put_context(ctxt, 0);
-                       break;
+               ctxt = (struct svc_rdma_op_ctxt *)(unsigned long)wc.wr_id;
+               if (ctxt)
+                       process_context(xprt, ctxt);
 
-               default:
-                       printk(KERN_ERR "svcrdma: unexpected completion type, "
-                              "opcode=%d, status=%d\n",
-                              wc.opcode, wc.status);
-                       break;
-               }
                svc_xprt_put(&xprt->sc_xprt);
        }
 
@@ -425,10 +449,12 @@ static struct svcxprt_rdma *rdma_create_xprt(struct svc_serv *serv,
        INIT_LIST_HEAD(&cma_xprt->sc_dto_q);
        INIT_LIST_HEAD(&cma_xprt->sc_rq_dto_q);
        INIT_LIST_HEAD(&cma_xprt->sc_read_complete_q);
+       INIT_LIST_HEAD(&cma_xprt->sc_frmr_q);
        init_waitqueue_head(&cma_xprt->sc_send_wait);
 
        spin_lock_init(&cma_xprt->sc_lock);
        spin_lock_init(&cma_xprt->sc_rq_dto_lock);
+       spin_lock_init(&cma_xprt->sc_frmr_q_lock);
 
        cma_xprt->sc_ord = svcrdma_ord;
 
@@ -462,7 +488,7 @@ int svc_rdma_post_recv(struct svcxprt_rdma *xprt)
        struct ib_recv_wr recv_wr, *bad_recv_wr;
        struct svc_rdma_op_ctxt *ctxt;
        struct page *page;
-       unsigned long pa;
+       dma_addr_t pa;
        int sge_no;
        int buflen;
        int ret;
@@ -474,13 +500,15 @@ int svc_rdma_post_recv(struct svcxprt_rdma *xprt)
                BUG_ON(sge_no >= xprt->sc_max_sge);
                page = svc_rdma_get_page();
                ctxt->pages[sge_no] = page;
-               atomic_inc(&xprt->sc_dma_used);
                pa = ib_dma_map_page(xprt->sc_cm_id->device,
                                     page, 0, PAGE_SIZE,
                                     DMA_FROM_DEVICE);
+               if (ib_dma_mapping_error(xprt->sc_cm_id->device, pa))
+                       goto err_put_ctxt;
+               atomic_inc(&xprt->sc_dma_used);
                ctxt->sge[sge_no].addr = pa;
                ctxt->sge[sge_no].length = PAGE_SIZE;
-               ctxt->sge[sge_no].lkey = xprt->sc_phys_mr->lkey;
+               ctxt->sge[sge_no].lkey = xprt->sc_dma_lkey;
                buflen += PAGE_SIZE;
        }
        ctxt->count = sge_no;
@@ -496,6 +524,10 @@ int svc_rdma_post_recv(struct svcxprt_rdma *xprt)
                svc_rdma_put_context(ctxt, 1);
        }
        return ret;
+
+ err_put_ctxt:
+       svc_rdma_put_context(ctxt, 1);
+       return -ENOMEM;
 }
 
 /*
@@ -566,7 +598,7 @@ static int rdma_listen_handler(struct rdma_cm_id *cma_id,
                dprintk("svcrdma: Connect request on cma_id=%p, xprt = %p, "
                        "event=%d\n", cma_id, cma_id->context, event->event);
                handle_connect_req(cma_id,
-                                  event->param.conn.responder_resources);
+                                  event->param.conn.initiator_depth);
                break;
 
        case RDMA_CM_EVENT_ESTABLISHED:
@@ -686,6 +718,97 @@ static struct svc_xprt *svc_rdma_create(struct svc_serv *serv,
        return ERR_PTR(ret);
 }
 
+static struct svc_rdma_fastreg_mr *rdma_alloc_frmr(struct svcxprt_rdma *xprt)
+{
+       struct ib_mr *mr;
+       struct ib_fast_reg_page_list *pl;
+       struct svc_rdma_fastreg_mr *frmr;
+
+       frmr = kmalloc(sizeof(*frmr), GFP_KERNEL);
+       if (!frmr)
+               goto err;
+
+       mr = ib_alloc_fast_reg_mr(xprt->sc_pd, RPCSVC_MAXPAGES);
+       if (!mr)
+               goto err_free_frmr;
+
+       pl = ib_alloc_fast_reg_page_list(xprt->sc_cm_id->device,
+                                        RPCSVC_MAXPAGES);
+       if (!pl)
+               goto err_free_mr;
+
+       frmr->mr = mr;
+       frmr->page_list = pl;
+       INIT_LIST_HEAD(&frmr->frmr_list);
+       return frmr;
+
+ err_free_mr:
+       ib_dereg_mr(mr);
+ err_free_frmr:
+       kfree(frmr);
+ err:
+       return ERR_PTR(-ENOMEM);
+}
+
+static void rdma_dealloc_frmr_q(struct svcxprt_rdma *xprt)
+{
+       struct svc_rdma_fastreg_mr *frmr;
+
+       while (!list_empty(&xprt->sc_frmr_q)) {
+               frmr = list_entry(xprt->sc_frmr_q.next,
+                                 struct svc_rdma_fastreg_mr, frmr_list);
+               list_del_init(&frmr->frmr_list);
+               ib_dereg_mr(frmr->mr);
+               ib_free_fast_reg_page_list(frmr->page_list);
+               kfree(frmr);
+       }
+}
+
+struct svc_rdma_fastreg_mr *svc_rdma_get_frmr(struct svcxprt_rdma *rdma)
+{
+       struct svc_rdma_fastreg_mr *frmr = NULL;
+
+       spin_lock_bh(&rdma->sc_frmr_q_lock);
+       if (!list_empty(&rdma->sc_frmr_q)) {
+               frmr = list_entry(rdma->sc_frmr_q.next,
+                                 struct svc_rdma_fastreg_mr, frmr_list);
+               list_del_init(&frmr->frmr_list);
+               frmr->map_len = 0;
+               frmr->page_list_len = 0;
+       }
+       spin_unlock_bh(&rdma->sc_frmr_q_lock);
+       if (frmr)
+               return frmr;
+
+       return rdma_alloc_frmr(rdma);
+}
+
+static void frmr_unmap_dma(struct svcxprt_rdma *xprt,
+                          struct svc_rdma_fastreg_mr *frmr)
+{
+       int page_no;
+       for (page_no = 0; page_no < frmr->page_list_len; page_no++) {
+               dma_addr_t addr = frmr->page_list->page_list[page_no];
+               if (ib_dma_mapping_error(frmr->mr->device, addr))
+                       continue;
+               atomic_dec(&xprt->sc_dma_used);
+               ib_dma_unmap_single(frmr->mr->device, addr, PAGE_SIZE,
+                                   frmr->direction);
+       }
+}
+
+void svc_rdma_put_frmr(struct svcxprt_rdma *rdma,
+                      struct svc_rdma_fastreg_mr *frmr)
+{
+       if (frmr) {
+               frmr_unmap_dma(rdma, frmr);
+               spin_lock_bh(&rdma->sc_frmr_q_lock);
+               BUG_ON(!list_empty(&frmr->frmr_list));
+               list_add(&frmr->frmr_list, &rdma->sc_frmr_q);
+               spin_unlock_bh(&rdma->sc_frmr_q_lock);
+       }
+}
+
 /*
  * This is the xpo_recvfrom function for listening endpoints. Its
  * purpose is to accept incoming connections. The CMA callback handler
@@ -704,6 +827,8 @@ static struct svc_xprt *svc_rdma_accept(struct svc_xprt *xprt)
        struct rdma_conn_param conn_param;
        struct ib_qp_init_attr qp_attr;
        struct ib_device_attr devattr;
+       int dma_mr_acc;
+       int need_dma_mr;
        int ret;
        int i;
 
@@ -819,15 +944,77 @@ static struct svc_xprt *svc_rdma_accept(struct svc_xprt *xprt)
        }
        newxprt->sc_qp = newxprt->sc_cm_id->qp;
 
-       /* Register all of physical memory */
-       newxprt->sc_phys_mr = ib_get_dma_mr(newxprt->sc_pd,
-                                           IB_ACCESS_LOCAL_WRITE |
-                                           IB_ACCESS_REMOTE_WRITE);
-       if (IS_ERR(newxprt->sc_phys_mr)) {
-               dprintk("svcrdma: Failed to create DMA MR ret=%d\n", ret);
+       /*
+        * Use the most secure set of MR resources based on the
+        * transport type and available memory management features in
+        * the device. Here's the table implemented below:
+        *
+        *              Fast    Global  DMA     Remote WR
+        *              Reg     LKEY    MR      Access
+        *              Sup'd   Sup'd   Needed  Needed
+        *
+        * IWARP        N       N       Y       Y
+        *              N       Y       Y       Y
+        *              Y       N       Y       N
+        *              Y       Y       N       -
+        *
+        * IB           N       N       Y       N
+        *              N       Y       N       -
+        *              Y       N       Y       N
+        *              Y       Y       N       -
+        *
+        * NB:  iWARP requires remote write access for the data sink
+        *      of an RDMA_READ. IB does not.
+        */
+       if (devattr.device_cap_flags & IB_DEVICE_MEM_MGT_EXTENSIONS) {
+               newxprt->sc_frmr_pg_list_len =
+                       devattr.max_fast_reg_page_list_len;
+               newxprt->sc_dev_caps |= SVCRDMA_DEVCAP_FAST_REG;
+       }
+
+       /*
+        * Determine if a DMA MR is required and if so, what privs are required
+        */
+       switch (rdma_node_get_transport(newxprt->sc_cm_id->device->node_type)) {
+       case RDMA_TRANSPORT_IWARP:
+               newxprt->sc_dev_caps |= SVCRDMA_DEVCAP_READ_W_INV;
+               if (!(newxprt->sc_dev_caps & SVCRDMA_DEVCAP_FAST_REG)) {
+                       need_dma_mr = 1;
+                       dma_mr_acc =
+                               (IB_ACCESS_LOCAL_WRITE |
+                                IB_ACCESS_REMOTE_WRITE);
+               } else if (!(devattr.device_cap_flags & IB_DEVICE_LOCAL_DMA_LKEY)) {
+                       need_dma_mr = 1;
+                       dma_mr_acc = IB_ACCESS_LOCAL_WRITE;
+               } else
+                       need_dma_mr = 0;
+               break;
+       case RDMA_TRANSPORT_IB:
+               if (!(devattr.device_cap_flags & IB_DEVICE_LOCAL_DMA_LKEY)) {
+                       need_dma_mr = 1;
+                       dma_mr_acc = IB_ACCESS_LOCAL_WRITE;
+               } else
+                       need_dma_mr = 0;
+               break;
+       default:
                goto errout;
        }
 
+       /* Create the DMA MR if needed, otherwise, use the DMA LKEY */
+       if (need_dma_mr) {
+               /* Register all of physical memory */
+               newxprt->sc_phys_mr =
+                       ib_get_dma_mr(newxprt->sc_pd, dma_mr_acc);
+               if (IS_ERR(newxprt->sc_phys_mr)) {
+                       dprintk("svcrdma: Failed to create DMA MR ret=%d\n",
+                               ret);
+                       goto errout;
+               }
+               newxprt->sc_dma_lkey = newxprt->sc_phys_mr->lkey;
+       } else
+               newxprt->sc_dma_lkey =
+                       newxprt->sc_cm_id->device->local_dma_lkey;
+
        /* Post receive buffers */
        for (i = 0; i < newxprt->sc_max_requests; i++) {
                ret = svc_rdma_post_recv(newxprt);
@@ -961,6 +1148,9 @@ static void __svc_rdma_free(struct work_struct *work)
        WARN_ON(atomic_read(&rdma->sc_ctxt_used) != 0);
        WARN_ON(atomic_read(&rdma->sc_dma_used) != 0);
 
+       /* De-allocate fastreg mr */
+       rdma_dealloc_frmr_q(rdma);
+
        /* Destroy the QP if present (not a listener) */
        if (rdma->sc_qp && !IS_ERR(rdma->sc_qp))
                ib_destroy_qp(rdma->sc_qp);
@@ -1014,21 +1204,59 @@ static int svc_rdma_has_wspace(struct svc_xprt *xprt)
        return 1;
 }
 
+/*
+ * Attempt to register the kvec representing the RPC memory with the
+ * device.
+ *
+ * Returns:
+ *  NULL : The device does not support fastreg or there were no more
+ *         fastreg mr.
+ *  frmr : The kvec register request was successfully posted.
+ *    <0 : An error was encountered attempting to register the kvec.
+ */
+int svc_rdma_fastreg(struct svcxprt_rdma *xprt,
+                    struct svc_rdma_fastreg_mr *frmr)
+{
+       struct ib_send_wr fastreg_wr;
+       u8 key;
+
+       /* Bump the key */
+       key = (u8)(frmr->mr->lkey & 0x000000FF);
+       ib_update_fast_reg_key(frmr->mr, ++key);
+
+       /* Prepare FASTREG WR */
+       memset(&fastreg_wr, 0, sizeof fastreg_wr);
+       fastreg_wr.opcode = IB_WR_FAST_REG_MR;
+       fastreg_wr.send_flags = IB_SEND_SIGNALED;
+       fastreg_wr.wr.fast_reg.iova_start = (unsigned long)frmr->kva;
+       fastreg_wr.wr.fast_reg.page_list = frmr->page_list;
+       fastreg_wr.wr.fast_reg.page_list_len = frmr->page_list_len;
+       fastreg_wr.wr.fast_reg.page_shift = PAGE_SHIFT;
+       fastreg_wr.wr.fast_reg.length = frmr->map_len;
+       fastreg_wr.wr.fast_reg.access_flags = frmr->access_flags;
+       fastreg_wr.wr.fast_reg.rkey = frmr->mr->lkey;
+       return svc_rdma_send(xprt, &fastreg_wr);
+}
+
 int svc_rdma_send(struct svcxprt_rdma *xprt, struct ib_send_wr *wr)
 {
-       struct ib_send_wr *bad_wr;
+       struct ib_send_wr *bad_wr, *n_wr;
+       int wr_count;
+       int i;
        int ret;
 
        if (test_bit(XPT_CLOSE, &xprt->sc_xprt.xpt_flags))
                return -ENOTCONN;
 
        BUG_ON(wr->send_flags != IB_SEND_SIGNALED);
-       BUG_ON(((struct svc_rdma_op_ctxt *)(unsigned long)wr->wr_id)->wr_op !=
-               wr->opcode);
+       wr_count = 1;
+       for (n_wr = wr->next; n_wr; n_wr = n_wr->next)
+               wr_count++;
+
        /* If the SQ is full, wait until an SQ entry is available */
        while (1) {
                spin_lock_bh(&xprt->sc_lock);
-               if (xprt->sc_sq_depth == atomic_read(&xprt->sc_sq_count)) {
+               if (xprt->sc_sq_depth < atomic_read(&xprt->sc_sq_count) + wr_count) {
                        spin_unlock_bh(&xprt->sc_lock);
                        atomic_inc(&rdma_stat_sq_starve);
 
@@ -1043,19 +1271,26 @@ int svc_rdma_send(struct svcxprt_rdma *xprt, struct ib_send_wr *wr)
                                return 0;
                        continue;
                }
-               /* Bumped used SQ WR count and post */
-               svc_xprt_get(&xprt->sc_xprt);
+               /* Take a transport ref for each WR posted */
+               for (i = 0; i < wr_count; i++)
+                       svc_xprt_get(&xprt->sc_xprt);
+
+               /* Bump used SQ WR count and post */
+               atomic_add(wr_count, &xprt->sc_sq_count);
                ret = ib_post_send(xprt->sc_qp, wr, &bad_wr);
-               if (!ret)
-                       atomic_inc(&xprt->sc_sq_count);
-               else {
-                       svc_xprt_put(&xprt->sc_xprt);
+               if (ret) {
+                       set_bit(XPT_CLOSE, &xprt->sc_xprt.xpt_flags);
+                       atomic_sub(wr_count, &xprt->sc_sq_count);
+                       for (i = 0; i < wr_count; i ++)
+                               svc_xprt_put(&xprt->sc_xprt);
                        dprintk("svcrdma: failed to post SQ WR rc=%d, "
                               "sc_sq_count=%d, sc_sq_depth=%d\n",
                               ret, atomic_read(&xprt->sc_sq_count),
                               xprt->sc_sq_depth);
                }
                spin_unlock_bh(&xprt->sc_lock);
+               if (ret)
+                       wake_up(&xprt->sc_send_wait);
                break;
        }
        return ret;
@@ -1079,10 +1314,14 @@ void svc_rdma_send_error(struct svcxprt_rdma *xprt, struct rpcrdma_msg *rmsgp,
        length = svc_rdma_xdr_encode_error(xprt, rmsgp, err, va);
 
        /* Prepare SGE for local address */
-       atomic_inc(&xprt->sc_dma_used);
        sge.addr = ib_dma_map_page(xprt->sc_cm_id->device,
                                   p, 0, PAGE_SIZE, DMA_FROM_DEVICE);
-       sge.lkey = xprt->sc_phys_mr->lkey;
+       if (ib_dma_mapping_error(xprt->sc_cm_id->device, sge.addr)) {
+               put_page(p);
+               return;
+       }
+       atomic_inc(&xprt->sc_dma_used);
+       sge.lkey = xprt->sc_dma_lkey;
        sge.length = length;
 
        ctxt = svc_rdma_get_context(xprt);
@@ -1103,6 +1342,9 @@ void svc_rdma_send_error(struct svcxprt_rdma *xprt, struct rpcrdma_msg *rmsgp,
        if (ret) {
                dprintk("svcrdma: Error %d posting send for protocol error\n",
                        ret);
+               ib_dma_unmap_page(xprt->sc_cm_id->device,
+                                 sge.addr, PAGE_SIZE,
+                                 DMA_FROM_DEVICE);
                svc_rdma_put_context(ctxt, 1);
        }
 }
index 4486c59c3aca87d6102a372255246a326b9efdc6..9a288d5eea646e65953bf92b015f2e557d15239e 100644 (file)
@@ -3,8 +3,8 @@
  *
  * Client-side transport implementation for sockets.
  *
- * TCP callback races fixes (C) 1998 Red Hat Software <alan@redhat.com>
- * TCP send fixes (C) 1998 Red Hat Software <alan@redhat.com>
+ * TCP callback races fixes (C) 1998 Red Hat
+ * TCP send fixes (C) 1998 Red Hat
  * TCP NFS related read + write fixes
  *  (C) 1999 Dave Airlie, University of Limerick, Ireland <airlied@linux.ie>
  *
index 015606b54d9b28325aad62a71ea4fc160b686cd8..c647aab8d418c508c9088789c847fae59745c049 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * NET4:       Implementation of BSD Unix domain sockets.
  *
- * Authors:    Alan Cox, <alan.cox@linux.org>
+ * Authors:    Alan Cox, <alan@lxorguk.ukuu.org.uk>
  *
  *             This program is free software; you can redistribute it and/or
  *             modify it under the terms of the GNU General Public License
diff --git a/scripts/bootgraph.pl b/scripts/bootgraph.pl
new file mode 100644 (file)
index 0000000..2243353
--- /dev/null
@@ -0,0 +1,147 @@
+#!/usr/bin/perl
+
+# Copyright 2008, Intel Corporation
+#
+# This file is part of the Linux kernel
+#
+# This program file is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by the
+# Free Software Foundation; version 2 of the License.
+#
+# 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 in a file named COPYING; if not, write to the
+# Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor,
+# Boston, MA 02110-1301 USA
+#
+# Authors:
+#      Arjan van de Ven <arjan@linux.intel.com>
+
+
+#
+# This script turns a dmesg output into a SVG graphic that shows which
+# functions take how much time. You can view SVG graphics with various
+# programs, including Inkscape, The Gimp and Firefox.
+#
+#
+# For this script to work, the kernel needs to be compiled with the
+# CONFIG_PRINTK_TIME configuration option enabled, and with
+# "initcall_debug" passed on the kernel command line.
+#
+# usage:
+#      dmesg | perl scripts/bootgraph.pl > output.svg
+#
+
+my @rows;
+my %start, %end, %row;
+my $done = 0;
+my $rowcount = 0;
+my $maxtime = 0;
+my $firsttime = 100;
+my $count = 0;
+while (<>) {
+       my $line = $_;
+       if ($line =~ /([0-9\.]+)\] calling  ([a-zA-Z0-9\_]+)\+/) {
+               my $func = $2;
+               if ($done == 0) {
+                       $start{$func} = $1;
+                       if ($1 < $firsttime) {
+                               $firsttime = $1;
+                       }
+               }
+               $row{$func} = 1;
+               if ($line =~ /\@ ([0-9]+)/) {
+                       my $pid = $1;
+                       if (!defined($rows[$pid])) {
+                               $rowcount = $rowcount + 1;
+                               $rows[$pid] = $rowcount;
+                       }
+                       $row{$func} = $rows[$pid];
+               }
+               $count = $count + 1;
+       }
+
+       if ($line =~ /([0-9\.]+)\] initcall ([a-zA-Z0-9\_]+)\+.*returned/) {
+               if ($done == 0) {
+                       $end{$2} = $1;
+                       $maxtime = $1;
+               }
+       }
+       if ($line =~ /Write protecting the/) {
+               $done = 1;
+       }
+       if ($line =~ /Freeing unused kernel memory/) {
+               $done = 1;
+       }
+}
+
+if ($count == 0) {
+       print "No data found in the dmesg. Make sure that 'printk.time=1' and\n";
+       print "'initcall_debug' are passed on the kernel command line.\n\n";
+       print "Usage: \n";
+       print "      dmesg | perl scripts/bootgraph.pl > output.svg\n\n";
+       exit;
+}
+
+print "<?xml version=\"1.0\" standalone=\"no\"?> \n";
+print "<svg width=\"1000\" height=\"100%\" version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\">\n";
+
+my @styles;
+
+$styles[0] = "fill:rgb(0,0,255);fill-opacity:0.5;stroke-width:1;stroke:rgb(0,0,0)";
+$styles[1] = "fill:rgb(0,255,0);fill-opacity:0.5;stroke-width:1;stroke:rgb(0,0,0)";
+$styles[2] = "fill:rgb(255,0,20);fill-opacity:0.5;stroke-width:1;stroke:rgb(0,0,0)";
+$styles[3] = "fill:rgb(255,255,20);fill-opacity:0.5;stroke-width:1;stroke:rgb(0,0,0)";
+$styles[4] = "fill:rgb(255,0,255);fill-opacity:0.5;stroke-width:1;stroke:rgb(0,0,0)";
+$styles[5] = "fill:rgb(0,255,255);fill-opacity:0.5;stroke-width:1;stroke:rgb(0,0,0)";
+$styles[6] = "fill:rgb(0,128,255);fill-opacity:0.5;stroke-width:1;stroke:rgb(0,0,0)";
+$styles[7] = "fill:rgb(0,255,128);fill-opacity:0.5;stroke-width:1;stroke:rgb(0,0,0)";
+$styles[8] = "fill:rgb(255,0,128);fill-opacity:0.5;stroke-width:1;stroke:rgb(0,0,0)";
+$styles[9] = "fill:rgb(255,255,128);fill-opacity:0.5;stroke-width:1;stroke:rgb(0,0,0)";
+$styles[10] = "fill:rgb(255,128,255);fill-opacity:0.5;stroke-width:1;stroke:rgb(0,0,0)";
+$styles[11] = "fill:rgb(128,255,255);fill-opacity:0.5;stroke-width:1;stroke:rgb(0,0,0)";
+
+my $mult = 950.0 / ($maxtime - $firsttime);
+my $threshold = ($maxtime - $firsttime) / 60.0;
+my $stylecounter = 0;
+while (($key,$value) = each %start) {
+       my $duration = $end{$key} - $start{$key};
+
+       if ($duration >= $threshold) {
+               my $s, $s2, $e, $y;
+               $s = ($value - $firsttime) * $mult;
+               $s2 = $s + 6;
+               $e = ($end{$key} - $firsttime) * $mult;
+               $w = $e - $s;
+
+               $y = $row{$key} * 150;
+               $y2 = $y + 4;
+
+               $style = $styles[$stylecounter];
+               $stylecounter = $stylecounter + 1;
+               if ($stylecounter > 11) {
+                       $stylecounter = 0;
+               };
+
+               print "<rect x=\"$s\" width=\"$w\" y=\"$y\" height=\"145\" style=\"$style\"/>\n";
+               print "<text transform=\"translate($s2,$y2) rotate(90)\">$key</text>\n";
+       }
+}
+
+
+# print the time line on top
+my $time = $firsttime;
+my $step = ($maxtime - $firsttime) / 15;
+while ($time < $maxtime) {
+       my $s2 = ($time - $firsttime) * $mult;
+       my $tm = int($time * 100) / 100.0;
+       print "<text transform=\"translate($s2,89) rotate(90)\">$tm</text>\n";
+       $time = $time + $step;
+}
+
+print "</svg>\n";
index 473f94e56eade0f26195a6afadc90559a11cef2c..d4dc222a74f33c69fd17dbb7170c41731933aa2b 100644 (file)
@@ -206,6 +206,20 @@ static void do_usb_table(void *symval, unsigned long size,
                do_usb_entry_multi(symval + i, mod);
 }
 
+/* Looks like: hid:bNvNpN */
+static int do_hid_entry(const char *filename,
+                            struct hid_device_id *id, char *alias)
+{
+       id->vendor = TO_NATIVE(id->vendor);
+       id->product = TO_NATIVE(id->product);
+
+       sprintf(alias, "hid:b%04X", id->bus);
+       ADD(alias, "v", id->vendor != HID_ANY_ID, id->vendor);
+       ADD(alias, "p", id->product != HID_ANY_ID, id->product);
+
+       return 1;
+}
+
 /* Looks like: ieee1394:venNmoNspNverN */
 static int do_ieee1394_entry(const char *filename,
                             struct ieee1394_device_id *id, char *alias)
@@ -745,6 +759,10 @@ void handle_moddevtable(struct module *mod, struct elf_info *info,
        else if (sym_is(symname, "__mod_usb_device_table"))
                /* special case to handle bcdDevice ranges */
                do_usb_table(symval, sym->st_size, mod);
+       else if (sym_is(symname, "__mod_hid_device_table"))
+               do_table(symval, sym->st_size,
+                        sizeof(struct hid_device_id), "hid",
+                        do_hid_entry, mod);
        else if (sym_is(symname, "__mod_ieee1394_device_table"))
                do_table(symval, sym->st_size,
                         sizeof(struct ieee1394_device_id), "ieee1394",