Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/rostedt...
authorLinus Torvalds <torvalds@linux-foundation.org>
Sun, 30 May 2010 16:13:43 +0000 (09:13 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Sun, 30 May 2010 16:13:43 +0000 (09:13 -0700)
* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/rostedt/linux-2.6-kconfig:
  kconfig: Hide error output in find command in streamline_config.pl
  kconfig: Fix typo in comment in streamline_config.pl
  kconfig: Make a variable local in streamline_config.pl

579 files changed:
Documentation/acpi/apei/einj.txt [new file with mode: 0644]
Documentation/arm/Samsung-S3C24XX/GPIO.txt
Documentation/arm/Samsung-S3C24XX/Overview.txt
Documentation/arm/Samsung/GPIO.txt [new file with mode: 0644]
Documentation/arm/Samsung/Overview.txt
Documentation/filesystems/Locking
Documentation/filesystems/vfs.txt
Documentation/hwmon/dme1737
Documentation/hwmon/lm63
Documentation/hwmon/ltc4245
Documentation/hwmon/sysfs-interface
Documentation/hwmon/tmp102 [new file with mode: 0644]
Documentation/kernel-parameters.txt
MAINTAINERS
arch/arm/configs/s3c2410_defconfig
arch/arm/configs/s3c6400_defconfig
arch/arm/mach-davinci/board-da850-evm.c
arch/arm/mach-mx3/mach-mx31moboard.c
arch/arm/mach-orion5x/dns323-setup.c
arch/arm/mach-s3c2440/mach-gta02.c
arch/arm/mach-s3c64xx/clock.c
arch/arm/mach-u300/i2c.c
arch/arm/mach-u300/include/mach/irqs.h
arch/arm/mach-ux500/board-mop500.c
arch/arm/mach-ux500/clock.c
arch/arm/mach-ux500/cpu-db8500.c
arch/arm/mach-ux500/devices-db8500.c
arch/arm/mach-ux500/include/mach/db8500-regs.h
arch/arm/mach-ux500/include/mach/devices.h
arch/arm/mach-ux500/ste-dma40-db8500.h [new file with mode: 0644]
arch/arm/plat-s5p/clock.c
arch/arm/plat-samsung/include/plat/gpio-cfg.h
arch/frv/include/asm/cache.h
arch/frv/include/asm/mem-layout.h
arch/ia64/include/asm/acpi.h
arch/ia64/kernel/smpboot.c
arch/ia64/mm/numa.c
arch/ia64/pci/pci.c
arch/parisc/include/asm/cacheflush.h
arch/parisc/kernel/asm-offsets.c
arch/parisc/kernel/entry.S
arch/parisc/kernel/syscall.S
arch/parisc/math-emu/decode_exc.c
arch/parisc/mm/fault.c
arch/powerpc/platforms/cell/spufs/file.c
arch/powerpc/platforms/cell/spufs/inode.c
arch/powerpc/platforms/pseries/hvCall_inst.c
arch/sparc/kernel/perf_event.c
arch/x86/include/asm/acpi.h
arch/x86/include/asm/cpufeature.h
arch/x86/include/asm/mce.h
arch/x86/include/asm/perf_event_p4.h
arch/x86/include/asm/rdc321x_defs.h [deleted file]
arch/x86/include/asm/thread_info.h
arch/x86/kernel/acpi/boot.c
arch/x86/kernel/acpi/sleep.c
arch/x86/kernel/apic/apic.c
arch/x86/kernel/cpu/cpufreq/powernow-k8.c
arch/x86/kernel/cpu/mcheck/Makefile
arch/x86/kernel/cpu/mcheck/mce-apei.c [new file with mode: 0644]
arch/x86/kernel/cpu/mcheck/mce-internal.h
arch/x86/kernel/cpu/mcheck/mce.c
arch/x86/kernel/cpu/perf_event.c
arch/x86/kernel/cpu/perf_event_p4.c
arch/x86/kernel/setup.c
arch/x86/kernel/smpboot.c
arch/x86/lguest/boot.c
arch/x86/mm/numa.c
arch/x86/mm/pat.c
arch/x86/mm/pat_internal.h
arch/x86/mm/pat_rbtree.c
arch/x86/mm/pf_in.c
arch/x86/mm/pgtable_32.c
arch/x86/pci/acpi.c
drivers/Makefile
drivers/acpi/Kconfig
drivers/acpi/Makefile
drivers/acpi/acpi_pad.c
drivers/acpi/acpica/evxfevnt.c
drivers/acpi/acpica/hwacpi.c
drivers/acpi/apei/Kconfig [new file with mode: 0644]
drivers/acpi/apei/Makefile [new file with mode: 0644]
drivers/acpi/apei/apei-base.c [new file with mode: 0644]
drivers/acpi/apei/apei-internal.h [new file with mode: 0644]
drivers/acpi/apei/cper.c [new file with mode: 0644]
drivers/acpi/apei/einj.c [new file with mode: 0644]
drivers/acpi/apei/erst.c [new file with mode: 0644]
drivers/acpi/apei/ghes.c [new file with mode: 0644]
drivers/acpi/apei/hest.c [new file with mode: 0644]
drivers/acpi/atomicio.c [new file with mode: 0644]
drivers/acpi/ec.c
drivers/acpi/hed.c [new file with mode: 0644]
drivers/acpi/hest.c [deleted file]
drivers/acpi/osl.c
drivers/acpi/pci_root.c
drivers/acpi/processor_driver.c
drivers/acpi/processor_idle.c
drivers/acpi/sleep.c
drivers/acpi/sleep.h
drivers/acpi/tables.c
drivers/acpi/video.c
drivers/acpi/video_detect.c
drivers/ata/Kconfig
drivers/ata/Makefile
drivers/ata/ata_generic.c
drivers/ata/ata_piix.c
drivers/ata/libata-core.c
drivers/ata/libata-sff.c
drivers/ata/pata_acpi.c
drivers/ata/pata_ali.c
drivers/ata/pata_amd.c
drivers/ata/pata_artop.c
drivers/ata/pata_atiixp.c
drivers/ata/pata_atp867x.c
drivers/ata/pata_bf54x.c
drivers/ata/pata_cmd64x.c
drivers/ata/pata_cs5520.c
drivers/ata/pata_cs5530.c
drivers/ata/pata_cs5535.c
drivers/ata/pata_cs5536.c
drivers/ata/pata_cypress.c
drivers/ata/pata_efar.c
drivers/ata/pata_hpt366.c
drivers/ata/pata_hpt37x.c
drivers/ata/pata_hpt3x2n.c
drivers/ata/pata_hpt3x3.c
drivers/ata/pata_icside.c
drivers/ata/pata_it8213.c
drivers/ata/pata_it821x.c
drivers/ata/pata_jmicron.c
drivers/ata/pata_macio.c
drivers/ata/pata_marvell.c
drivers/ata/pata_mpc52xx.c
drivers/ata/pata_netcell.c
drivers/ata/pata_ninja32.c
drivers/ata/pata_ns87415.c
drivers/ata/pata_octeon_cf.c
drivers/ata/pata_oldpiix.c
drivers/ata/pata_optidma.c
drivers/ata/pata_pdc2027x.c
drivers/ata/pata_pdc202xx_old.c
drivers/ata/pata_piccolo.c
drivers/ata/pata_radisys.c
drivers/ata/pata_rdc.c
drivers/ata/pata_sc1200.c
drivers/ata/pata_scc.c
drivers/ata/pata_sch.c
drivers/ata/pata_serverworks.c
drivers/ata/pata_sil680.c
drivers/ata/pata_sis.c
drivers/ata/pata_sl82c105.c
drivers/ata/pata_triflex.c
drivers/ata/pata_via.c
drivers/ata/sata_mv.c
drivers/ata/sata_nv.c
drivers/ata/sata_qstor.c
drivers/ata/sata_sil.c
drivers/ata/sata_sis.c
drivers/ata/sata_svw.c
drivers/ata/sata_uli.c
drivers/ata/sata_via.c
drivers/ata/sata_vsc.c
drivers/char/ps3flash.c
drivers/cpuidle/cpuidle.c
drivers/cpuidle/cpuidle.h
drivers/cpuidle/driver.c
drivers/cpuidle/sysfs.c
drivers/dma/Kconfig
drivers/dma/Makefile
drivers/dma/pl330.c [new file with mode: 0644]
drivers/gpio/Kconfig
drivers/gpio/Makefile
drivers/gpio/janz-ttl.c [new file with mode: 0644]
drivers/gpio/rdc321x-gpio.c [new file with mode: 0644]
drivers/gpio/tc35892-gpio.c [new file with mode: 0644]
drivers/gpu/drm/radeon/radeon_state.c
drivers/hwmon/Kconfig
drivers/hwmon/Makefile
drivers/hwmon/adm1031.c
drivers/hwmon/applesmc.c
drivers/hwmon/asus_atk0110.c
drivers/hwmon/dme1737.c
drivers/hwmon/emc1403.c [new file with mode: 0644]
drivers/hwmon/f71882fg.c
drivers/hwmon/lm63.c
drivers/hwmon/lm75.c
drivers/hwmon/lm90.c
drivers/hwmon/ltc4245.c
drivers/hwmon/tmp102.c [new file with mode: 0644]
drivers/hwmon/tmp401.c
drivers/idle/Kconfig
drivers/idle/Makefile
drivers/idle/intel_idle.c [new file with mode: 0755]
drivers/infiniband/core/ucm.c
drivers/infiniband/hw/qib/qib_fs.c
drivers/infiniband/hw/qib/qib_iba6120.c
drivers/infiniband/hw/qib/qib_iba7322.c
drivers/infiniband/hw/qib/qib_init.c
drivers/input/touchscreen/Kconfig
drivers/input/touchscreen/Makefile
drivers/input/touchscreen/tps6507x-ts.c [new file with mode: 0644]
drivers/leds/Kconfig
drivers/leds/Makefile
drivers/leds/led-class.c
drivers/leds/leds-88pm860x.c
drivers/leds/leds-gpio.c
drivers/leds/leds-lp3944.c
drivers/leds/leds-mc13783.c [new file with mode: 0644]
drivers/leds/leds-net5501.c [new file with mode: 0644]
drivers/leds/leds-ss4200.c
drivers/mfd/88pm860x-core.c
drivers/mfd/88pm860x-i2c.c
drivers/mfd/Kconfig
drivers/mfd/Makefile
drivers/mfd/ab3100-core.c
drivers/mfd/ab3100-otp.c
drivers/mfd/ab3550-core.c [new file with mode: 0644]
drivers/mfd/ab4500-core.c [deleted file]
drivers/mfd/ab8500-core.c [new file with mode: 0644]
drivers/mfd/ab8500-spi.c [new file with mode: 0644]
drivers/mfd/abx500-core.c [new file with mode: 0644]
drivers/mfd/da903x.c
drivers/mfd/janz-cmodio.c [new file with mode: 0644]
drivers/mfd/max8925-core.c
drivers/mfd/max8925-i2c.c
drivers/mfd/mc13783-core.c
drivers/mfd/menelaus.c
drivers/mfd/mfd-core.c
drivers/mfd/pcf50633-adc.c
drivers/mfd/pcf50633-core.c
drivers/mfd/pcf50633-irq.c [new file with mode: 0644]
drivers/mfd/rdc321x-southbridge.c [new file with mode: 0644]
drivers/mfd/t7l66xb.c
drivers/mfd/tc35892.c [new file with mode: 0644]
drivers/mfd/timberdale.c
drivers/mfd/timberdale.h
drivers/mfd/tps65010.c
drivers/mfd/tps6507x.c [new file with mode: 0644]
drivers/mfd/twl4030-irq.c
drivers/mfd/wm831x-core.c
drivers/mfd/wm831x-irq.c
drivers/mfd/wm8350-i2c.c
drivers/mfd/wm8400-core.c
drivers/mtd/ubi/cdev.c
drivers/net/3c507.c
drivers/net/benet/be_cmds.c
drivers/net/benet/be_main.c
drivers/net/can/Kconfig
drivers/net/can/Makefile
drivers/net/can/janz-ican3.c [new file with mode: 0644]
drivers/net/cnic.c
drivers/net/cnic_if.h
drivers/net/fec.c
drivers/net/hamradio/yam.c
drivers/net/ll_temac.h
drivers/net/ll_temac_main.c
drivers/pci/pcie/aer/aerdrv.h
drivers/pci/pcie/aer/aerdrv_acpi.c
drivers/pci/pcie/aer/aerdrv_core.c
drivers/pci/probe.c
drivers/rapidio/rio-scan.c
drivers/rapidio/rio.c
drivers/regulator/ab3100.c
drivers/regulator/tps6507x-regulator.c
drivers/rtc/rtc-ab3100.c
drivers/serial/s5pv210.c
drivers/staging/pohmelfs/inode.c
drivers/usb/gadget/printer.c
drivers/vhost/net.c
drivers/vhost/vhost.c
drivers/video/backlight/88pm860x_bl.c
drivers/video/backlight/Kconfig
drivers/video/backlight/Makefile
drivers/video/backlight/adp8860_bl.c [new file with mode: 0644]
drivers/video/backlight/adx_bl.c
drivers/video/backlight/ep93xx_bl.c [new file with mode: 0644]
drivers/video/backlight/l4f00242t03.c
drivers/video/backlight/max8925_bl.c
drivers/video/backlight/mbp_nvidia_bl.c
drivers/video/backlight/pcf50633-backlight.c [new file with mode: 0644]
drivers/video/backlight/s6e63m0.c [new file with mode: 0644]
drivers/video/backlight/s6e63m0_gamma.h [new file with mode: 0644]
drivers/video/fb_defio.c
drivers/watchdog/rdc321x_wdt.c
fs/9p/vfs_file.c
fs/adfs/dir.c
fs/adfs/file.c
fs/adfs/inode.c
fs/affs/affs.h
fs/affs/file.c
fs/afs/internal.h
fs/afs/write.c
fs/aio.c
fs/anon_inodes.c
fs/attr.c
fs/bad_inode.c
fs/bfs/dir.c
fs/block_dev.c
fs/btrfs/ctree.h
fs/btrfs/file.c
fs/buffer.c
fs/ceph/auth.c
fs/ceph/auth.h
fs/ceph/auth_none.c
fs/ceph/auth_x.c
fs/ceph/caps.c
fs/ceph/ceph_fs.h
fs/ceph/dir.c
fs/ceph/export.c
fs/ceph/file.c
fs/ceph/inode.c
fs/ceph/mds_client.c
fs/ceph/messenger.c
fs/ceph/messenger.h
fs/ceph/mon_client.c
fs/ceph/osd_client.c
fs/ceph/osdmap.c
fs/ceph/super.c
fs/ceph/super.h
fs/cifs/cifsfs.h
fs/cifs/file.c
fs/coda/coda_int.h
fs/coda/file.c
fs/configfs/inode.c
fs/debugfs/file.c
fs/direct-io.c
fs/ecryptfs/file.c
fs/ecryptfs/inode.c
fs/exofs/file.c
fs/ext2/ext2.h
fs/ext2/file.c
fs/ext2/inode.c
fs/ext2/super.c
fs/ext3/dir.c
fs/ext3/fsync.c
fs/ext3/super.c
fs/ext4/ext4.h
fs/ext4/fsync.c
fs/ext4/super.c
fs/fat/fat.h
fs/fat/file.c
fs/fat/inode.c
fs/file_table.c
fs/fuse/dir.c
fs/fuse/file.c
fs/fuse/fuse_i.h
fs/gfs2/aops.c
fs/gfs2/file.c
fs/gfs2/ops_inode.c
fs/hostfs/hostfs_kern.c
fs/hpfs/file.c
fs/hpfs/hpfs_fn.h
fs/hppfs/hppfs.c
fs/hugetlbfs/inode.c
fs/jffs2/file.c
fs/jffs2/fs.c
fs/jffs2/os-linux.h
fs/jfs/file.c
fs/jfs/jfs_inode.h
fs/jfs/super.c
fs/libfs.c
fs/logfs/file.c
fs/logfs/logfs.h
fs/minix/dir.c
fs/minix/file.c
fs/minix/itree_v2.c
fs/namei.c
fs/ncpfs/file.c
fs/nfs/dir.c
fs/nfs/file.c
fs/nilfs2/file.c
fs/nilfs2/nilfs.h
fs/ntfs/dir.c
fs/ntfs/file.c
fs/ocfs2/file.c
fs/ocfs2/super.c
fs/omfs/file.c
fs/pipe.c
fs/qnx4/dir.c
fs/quota/dquot.c
fs/quota/quota.c
fs/ramfs/file-mmu.c
fs/ramfs/file-nommu.c
fs/reiserfs/dir.c
fs/reiserfs/file.c
fs/reiserfs/super.c
fs/smbfs/file.c
fs/smbfs/inode.c
fs/super.c
fs/sync.c
fs/sysfs/inode.c
fs/sysv/dir.c
fs/sysv/file.c
fs/sysv/inode.c
fs/ubifs/file.c
fs/ubifs/ubifs.h
fs/udf/balloc.c
fs/udf/dir.c
fs/udf/file.c
fs/udf/ialloc.c
fs/udf/inode.c
fs/udf/namei.c
fs/udf/super.c
fs/udf/udfdecl.h
fs/ufs/balloc.c
fs/ufs/dir.c
fs/ufs/file.c
fs/ufs/ialloc.c
fs/ufs/inode.c
fs/ufs/namei.c
fs/ufs/super.c
fs/ufs/truncate.c
fs/xfs/linux-2.6/xfs_file.c
include/acpi/acpi_bus.h
include/acpi/acpi_drivers.h
include/acpi/acpi_hest.h [deleted file]
include/acpi/apei.h [new file with mode: 0644]
include/acpi/atomicio.h [new file with mode: 0644]
include/acpi/hed.h [new file with mode: 0644]
include/acpi/processor.h
include/acpi/video.h
include/linux/acpi.h
include/linux/amba/pl330.h [new file with mode: 0644]
include/linux/bitmap.h
include/linux/buffer_head.h
include/linux/completion.h
include/linux/cper.h [new file with mode: 0644]
include/linux/cpuidle.h
include/linux/debugfs.h
include/linux/ext3_fs.h
include/linux/fb.h
include/linux/file.h
include/linux/fs.h
include/linux/ftrace_event.h
include/linux/i2c/adp8860.h [new file with mode: 0644]
include/linux/input/tps6507x-ts.h [new file with mode: 0644]
include/linux/lcd.h
include/linux/leds.h
include/linux/libata.h
include/linux/mfd/88pm860x.h
include/linux/mfd/ab4500.h [deleted file]
include/linux/mfd/ab8500.h [new file with mode: 0644]
include/linux/mfd/abx500.h [moved from include/linux/mfd/ab3100.h with 51% similarity]
include/linux/mfd/janz.h [new file with mode: 0644]
include/linux/mfd/mc13783.h
include/linux/mfd/pcf50633/backlight.h [new file with mode: 0644]
include/linux/mfd/pcf50633/core.h
include/linux/mfd/rdc321x.h [new file with mode: 0644]
include/linux/mfd/tc35892.h [new file with mode: 0644]
include/linux/mfd/tps6507x.h [new file with mode: 0644]
include/linux/mfd/wm831x/core.h
include/linux/nodemask.h
include/linux/pci.h
include/linux/perf_event.h
include/linux/quota.h
include/linux/quotaops.h
include/linux/rio.h
include/linux/syscalls.h
include/linux/tracepoint.h
include/linux/usb/audio-v2.h
include/linux/uuid.h [new file with mode: 0644]
include/net/cls_cgroup.h
include/net/sctp/structs.h
include/net/sock.h
include/trace/events/ext4.h
include/trace/ftrace.h
include/trace/syscall.h
ipc/shm.c
kernel/cpu.c
kernel/fork.c
kernel/hrtimer.c
kernel/perf_event.c
kernel/posix-timers.c
kernel/sched.c
kernel/timer.c
kernel/trace/blktrace.c
kernel/trace/ftrace.c
kernel/trace/kmemtrace.c
kernel/trace/trace.c
kernel/trace/trace.h
kernel/trace/trace_branch.c
kernel/trace/trace_event_perf.c
kernel/trace/trace_events.c
kernel/trace/trace_events_filter.c
kernel/trace/trace_export.c
kernel/trace/trace_functions_graph.c
kernel/trace/trace_kprobe.c
kernel/trace/trace_output.c
kernel/trace/trace_output.h
kernel/trace/trace_sched_switch.c
kernel/trace/trace_sched_wakeup.c
kernel/trace/trace_syscalls.c
kernel/trace/trace_workqueue.c
kernel/tracepoint.c
lib/Makefile
lib/atomic64_test.c
lib/bitmap.c
lib/uuid.c [new file with mode: 0644]
mm/shmem.c
mm/truncate.c
net/core/datagram.c
net/core/drop_monitor.c
net/core/neighbour.c
net/core/rtnetlink.c
net/core/sock.c
net/ipv4/ipmr.c
net/ipv4/udp.c
net/ipv6/ip6_output.c
net/ipv6/ip6mr.c
net/ipv6/udp.c
net/iucv/af_iucv.c
net/netfilter/xt_TEE.c
samples/tracepoints/tp-samples-trace.h
samples/tracepoints/tracepoint-probe-sample.c
samples/tracepoints/tracepoint-probe-sample2.c
sound/mips/au1x00.c
sound/oss/dmasound/dmasound_atari.c
sound/pci/asihpi/hpi.h
sound/pci/asihpi/hpi6000.c
sound/pci/asihpi/hpi6205.c
sound/pci/asihpi/hpi_internal.h
sound/pci/asihpi/hpicmn.c
sound/pci/asihpi/hpifunc.c
sound/pci/asihpi/hpios.c
sound/pci/asihpi/hpios.h
sound/pci/hda/hda_intel.c
sound/pci/hda/patch_conexant.c
sound/soc/codecs/wm8350.c
sound/soc/codecs/wm8400.c
sound/soc/codecs/wm8990.c
sound/soc/imx/imx-pcm-dma-mx2.c
sound/soc/sh/siu_dai.c
sound/usb/caiaq/control.c
sound/usb/caiaq/device.c
sound/usb/endpoint.c
sound/usb/format.c
sound/usb/format.h
sound/usb/mixer.c
sound/usb/pcm.c
tools/perf/Documentation/perf-stat.txt
tools/perf/builtin-annotate.c
tools/perf/builtin-probe.c
tools/perf/builtin-record.c
tools/perf/builtin-report.c
tools/perf/builtin-stat.c
tools/perf/perf.c
tools/perf/util/abspath.c
tools/perf/util/build-id.c
tools/perf/util/build-id.h
tools/perf/util/cache.h
tools/perf/util/callchain.c
tools/perf/util/callchain.h
tools/perf/util/config.c
tools/perf/util/exec_cmd.c
tools/perf/util/exec_cmd.h
tools/perf/util/header.c
tools/perf/util/help.c
tools/perf/util/hist.c
tools/perf/util/hist.h
tools/perf/util/newt.c
tools/perf/util/path.c
tools/perf/util/probe-finder.c
tools/perf/util/probe-finder.h
tools/perf/util/quote.c
tools/perf/util/quote.h
tools/perf/util/run-command.c
tools/perf/util/run-command.h
tools/perf/util/session.c
tools/perf/util/session.h
tools/perf/util/sigchain.c
tools/perf/util/sigchain.h
tools/perf/util/strbuf.c
tools/perf/util/strbuf.h
tools/perf/util/symbol.c
tools/perf/util/symbol.h
tools/perf/util/trace-event-read.c
tools/perf/util/trace-event.h
tools/perf/util/util.h
tools/perf/util/wrapper.c

diff --git a/Documentation/acpi/apei/einj.txt b/Documentation/acpi/apei/einj.txt
new file mode 100644 (file)
index 0000000..dfab718
--- /dev/null
@@ -0,0 +1,59 @@
+                       APEI Error INJection
+                       ~~~~~~~~~~~~~~~~~~~~
+
+EINJ provides a hardware error injection mechanism
+It is very useful for debugging and testing of other APEI and RAS features.
+
+To use EINJ, make sure the following are enabled in your kernel
+configuration:
+
+CONFIG_DEBUG_FS
+CONFIG_ACPI_APEI
+CONFIG_ACPI_APEI_EINJ
+
+The user interface of EINJ is debug file system, under the
+directory apei/einj. The following files are provided.
+
+- available_error_type
+  Reading this file returns the error injection capability of the
+  platform, that is, which error types are supported. The error type
+  definition is as follow, the left field is the error type value, the
+  right field is error description.
+
+    0x00000001 Processor Correctable
+    0x00000002 Processor Uncorrectable non-fatal
+    0x00000004 Processor Uncorrectable fatal
+    0x00000008  Memory Correctable
+    0x00000010  Memory Uncorrectable non-fatal
+    0x00000020  Memory Uncorrectable fatal
+    0x00000040 PCI Express Correctable
+    0x00000080 PCI Express Uncorrectable fatal
+    0x00000100 PCI Express Uncorrectable non-fatal
+    0x00000200 Platform Correctable
+    0x00000400 Platform Uncorrectable non-fatal
+    0x00000800 Platform Uncorrectable fatal
+
+  The format of file contents are as above, except there are only the
+  available error type lines.
+
+- error_type
+  This file is used to set the error type value. The error type value
+  is defined in "available_error_type" description.
+
+- error_inject
+  Write any integer to this file to trigger the error
+  injection. Before this, please specify all necessary error
+  parameters.
+
+- param1
+  This file is used to set the first error parameter value. Effect of
+  parameter depends on error_type specified. For memory error, this is
+  physical memory address.
+
+- param2
+  This file is used to set the second error parameter value. Effect of
+  parameter depends on error_type specified. For memory error, this is
+  physical memory address mask.
+
+For more information about EINJ, please refer to ACPI specification
+version 4.0, section 17.5.
index 2af2cf3..816d607 100644 (file)
@@ -12,6 +12,8 @@ Introduction
   of the s3c2410 GPIO system, please read the Samsung provided
   data-sheet/users manual to find out the complete list.
 
+  See Documentation/arm/Samsung/GPIO.txt for the core implemetation.
+
 
 GPIOLIB
 -------
@@ -24,8 +26,60 @@ GPIOLIB
   listed below will be removed (they may be marked as __deprecated
   in the near future).
 
-  - s3c2410_gpio_getpin
-  - s3c2410_gpio_setpin
+  The following functions now either have a s3c_ specific variant
+  or are merged into gpiolib. See the definitions in
+  arch/arm/plat-samsung/include/plat/gpio-cfg.h:
+
+  s3c2410_gpio_setpin()                gpio_set_value() or gpio_direction_output()
+  s3c2410_gpio_getpin()                gpio_get_value() or gpio_direction_input()
+  s3c2410_gpio_getirq()                gpio_to_irq()
+  s3c2410_gpio_cfgpin()                s3c_gpio_cfgpin()
+  s3c2410_gpio_getcfg()                s3c_gpio_getcfg()
+  s3c2410_gpio_pullup()                s3c_gpio_setpull()
+
+
+GPIOLIB conversion
+------------------
+
+If you need to convert your board or driver to use gpiolib from the exiting
+s3c2410 api, then here are some notes on the process.
+
+1) If your board is exclusively using an GPIO, say to control peripheral
+   power, then it will require to claim the gpio with gpio_request() before
+   it can use it.
+
+   It is recommended to check the return value, with at least WARN_ON()
+   during initialisation.
+
+2) The s3c2410_gpio_cfgpin() can be directly replaced with s3c_gpio_cfgpin()
+   as they have the same arguments, and can either take the pin specific
+   values, or the more generic special-function-number arguments.
+
+3) s3c2410_gpio_pullup() changs have the problem that whilst the 
+   s3c2410_gpio_pullup(x, 1) can be easily translated to the
+   s3c_gpio_setpull(x, S3C_GPIO_PULL_NONE), the s3c2410_gpio_pullup(x, 0)
+   are not so easy.
+
+   The s3c2410_gpio_pullup(x, 0) case enables the pull-up (or in the case
+   of some of the devices, a pull-down) and as such the new API distinguishes
+   between the UP and DOWN case. There is currently no 'just turn on' setting
+   which may be required if this becomes a problem.
+
+4) s3c2410_gpio_setpin() can be replaced by gpio_set_value(), the old call
+   does not implicitly configure the relevant gpio to output. The gpio
+   direction should be changed before using gpio_set_value().
+
+5) s3c2410_gpio_getpin() is replaceable by gpio_get_value() if the pin
+   has been set to input. It is currently unknown what the behaviour is
+   when using gpio_get_value() on an output pin (s3c2410_gpio_getpin
+   would return the value the pin is supposed to be outputting).
+
+6) s3c2410_gpio_getirq() should be directly replacable with the
+   gpio_to_irq() call.
+
+The s3c2410_gpio and gpio_ calls have always operated on the same gpio
+numberspace, so there is no problem with converting the gpio numbering
+between the calls.
 
 
 Headers
@@ -54,6 +108,11 @@ PIN Numbers
   eg S3C2410_GPA(0) or S3C2410_GPF(1). These defines are used to tell
   the GPIO functions which pin is to be used.
 
+  With the conversion to gpiolib, there is no longer a direct conversion
+  from gpio pin number to register base address as in earlier kernels. This
+  is due to the number space required for newer SoCs where the later
+  GPIOs are not contiguous.
+
 
 Configuring a pin
 -----------------
@@ -71,6 +130,8 @@ Configuring a pin
    which would turn GPA(0) into the lowest Address line A0, and set
    GPE(8) to be connected to the SDIO/MMC controller's SDDAT1 line.
 
+   The s3c_gpio_cfgpin() call is a functional replacement for this call.
+
 
 Reading the current configuration
 ---------------------------------
@@ -82,6 +143,9 @@ Reading the current configuration
   The return value will be from the same set of values which can be
   passed to s3c2410_gpio_cfgpin().
 
+  The s3c_gpio_getcfg() call should be a functional replacement for
+  this call.
+
 
 Configuring a pull-up resistor
 ------------------------------
@@ -95,6 +159,10 @@ Configuring a pull-up resistor
   Where the to value is zero to set the pull-up off, and 1 to enable
   the specified pull-up. Any other values are currently undefined.
 
+  The s3c_gpio_setpull() offers similar functionality, but with the
+  ability to encode whether the pull is up or down. Currently there
+  is no 'just on' state, so up or down must be selected.
+
 
 Getting the state of a PIN
 --------------------------
@@ -106,6 +174,9 @@ Getting the state of a PIN
   This will return either zero or non-zero. Do not count on this
   function returning 1 if the pin is set.
 
+  This call is now implemented by the relevant gpiolib calls, convert
+  your board or driver to use gpiolib.
+
 
 Setting the state of a PIN
 --------------------------
@@ -117,6 +188,9 @@ Setting the state of a PIN
   Which sets the given pin to the value. Use 0 to write 0, and 1 to
   set the output to 1.
 
+  This call is now implemented by the relevant gpiolib calls, convert
+  your board or driver to use gpiolib.
+
 
 Getting the IRQ number associated with a PIN
 --------------------------------------------
@@ -128,6 +202,9 @@ Getting the IRQ number associated with a PIN
 
   Note, not all pins have an IRQ.
 
+  This call is now implemented by the relevant gpiolib calls, convert
+  your board or driver to use gpiolib.
+
 
 Authour
 -------
index 081892d..c12bfc1 100644 (file)
@@ -8,10 +8,16 @@ Introduction
 
   The Samsung S3C24XX range of ARM9 System-on-Chip CPUs are supported
   by the 's3c2410' architecture of ARM Linux. Currently the S3C2410,
-  S3C2412, S3C2413, S3C2440, S3C2442 and S3C2443 devices are supported.
+  S3C2412, S3C2413, S3C2416 S3C2440, S3C2442, S3C2443 and S3C2450 devices
+  are supported.
 
   Support for the S3C2400 and S3C24A0 series are in progress.
 
+  The S3C2416 and S3C2450 devices are very similar and S3C2450 support is
+  included under the arch/arm/mach-s3c2416 directory. Note, whilst core
+  support for these SoCs is in, work on some of the extra peripherals
+  and extra interrupts is still ongoing.
+
 
 Configuration
 -------------
@@ -209,6 +215,13 @@ GPIO
   Newer kernels carry GPIOLIB, and support is being moved towards
   this with some of the older support in line to be removed.
 
+  As of v2.6.34, the move towards using gpiolib support is almost
+  complete, and very little of the old calls are left.
+
+  See Documentation/arm/Samsung-S3C24XX/GPIO.txt for the S3C24XX specific
+  support and Documentation/arm/Samsung/GPIO.txt for the core Samsung
+  implementation.
+
 
 Clock Management
 ----------------
diff --git a/Documentation/arm/Samsung/GPIO.txt b/Documentation/arm/Samsung/GPIO.txt
new file mode 100644 (file)
index 0000000..05850c6
--- /dev/null
@@ -0,0 +1,42 @@
+               Samsung GPIO implementation
+               ===========================
+
+Introduction
+------------
+
+This outlines the Samsung GPIO implementation and the architecture
+specfic calls provided alongisde the drivers/gpio core.
+
+
+S3C24XX (Legacy)
+----------------
+
+See Documentation/arm/Samsung-S3C24XX/GPIO.txt for more information
+about these devices. Their implementation is being brought into line
+with the core samsung implementation described in this document.
+
+
+GPIOLIB integration
+-------------------
+
+The gpio implementation uses gpiolib as much as possible, only providing
+specific calls for the items that require Samsung specific handling, such
+as pin special-function or pull resistor control.
+
+GPIO numbering is synchronised between the Samsung and gpiolib system.
+
+
+PIN configuration
+-----------------
+
+Pin configuration is specific to the Samsung architecutre, with each SoC
+registering the necessary information for the core gpio configuration
+implementation to configure pins as necessary.
+
+The s3c_gpio_cfgpin() and s3c_gpio_setpull() provide the means for a
+driver or machine to change gpio configuration.
+
+See arch/arm/plat-samsung/include/plat/gpio-cfg.h for more information
+on these functions.
+
+
index 7cced1f..c3094ea 100644 (file)
@@ -13,9 +13,10 @@ Introduction
 
   - S3C24XX: See Documentation/arm/Samsung-S3C24XX/Overview.txt for full list
   - S3C64XX: S3C6400 and S3C6410
-  - S5PC6440
-
-  S5PC100 and S5PC110 support is currently being merged
+  - S5P6440
+  - S5P6442
+  - S5PC100
+  - S5PC110 / S5PV210
 
 
 S3C24XX Systems
@@ -35,7 +36,10 @@ Configuration
   unifying all the SoCs into one kernel.
 
   s5p6440_defconfig - S5P6440 specific default configuration
+  s5p6442_defconfig - S5P6442 specific default configuration
   s5pc100_defconfig - S5PC100 specific default configuration
+  s5pc110_defconfig - S5PC110 specific default configuration
+  s5pv210_defconfig - S5PV210 specific default configuration
 
 
 Layout
@@ -50,18 +54,27 @@ Layout
   specific information. It contains the base clock, GPIO and device definitions
   to get the system running.
 
-  plat-s3c is the s3c24xx/s3c64xx platform directory, although it is currently
-  involved in other builds this will be phased out once the relevant code is
-  moved elsewhere.
-
   plat-s3c24xx is for s3c24xx specific builds, see the S3C24XX docs.
 
-  plat-s3c64xx is for the s3c64xx specific bits, see the S3C24XX docs.
+  plat-s5p is for s5p specific builds, and contains common support for the
+  S5P specific systems. Not all S5Ps use all the features in this directory
+  due to differences in the hardware.
+
+
+Layout changes
+--------------
+
+  The old plat-s3c and plat-s5pc1xx directories have been removed, with
+  support moved to either plat-samsung or plat-s5p as necessary. These moves
+  where to simplify the include and dependency issues involved with having
+  so many different platform directories.
 
-  plat-s5p is for s5p specific builds, more to be added.
+  It was decided to remove plat-s5pc1xx as some of the support was already
+  in plat-s5p or plat-samsung, with the S5PC110 support added with S5PV210
+  the only user was the S5PC100. The S5PC100 specific items where moved to
+  arch/arm/mach-s5pc100.
 
 
-  [ to finish ]
 
 
 Port Contributors
index 61c98f0..96d4293 100644 (file)
@@ -380,7 +380,7 @@ prototypes:
        int (*open) (struct inode *, struct file *);
        int (*flush) (struct file *);
        int (*release) (struct inode *, struct file *);
-       int (*fsync) (struct file *, struct dentry *, int datasync);
+       int (*fsync) (struct file *, int datasync);
        int (*aio_fsync) (struct kiocb *, int datasync);
        int (*fasync) (int, struct file *, int);
        int (*lock) (struct file *, int, struct file_lock *);
index b668585..94677e7 100644 (file)
@@ -401,11 +401,16 @@ otherwise noted.
        started might not be in the page cache at the end of the
        walk).
 
-  truncate: called by the VFS to change the size of a file.  The
+  truncate: Deprecated. This will not be called if ->setsize is defined.
+       Called by the VFS to change the size of a file.  The
        i_size field of the inode is set to the desired size by the
        VFS before this method is called.  This method is called by
        the truncate(2) system call and related functionality.
 
+       Note: ->truncate and vmtruncate are deprecated. Do not add new
+       instances/calls of these. Filesystems should be converted to do their
+       truncate sequence via ->setattr().
+
   permission: called by the VFS to check for access rights on a POSIX-like
        filesystem.
 
@@ -729,7 +734,7 @@ struct file_operations {
        int (*open) (struct inode *, struct file *);
        int (*flush) (struct file *);
        int (*release) (struct inode *, struct file *);
-       int (*fsync) (struct file *, struct dentry *, int datasync);
+       int (*fsync) (struct file *, int datasync);
        int (*aio_fsync) (struct kiocb *, int datasync);
        int (*fasync) (int, struct file *, int);
        int (*lock) (struct file *, int, struct file_lock *);
index 001d2e7..fc5df76 100644 (file)
@@ -9,11 +9,15 @@ Supported chips:
   * SMSC SCH3112, SCH3114, SCH3116
     Prefix: 'sch311x'
     Addresses scanned: none, address read from Super-I/O config space
-    Datasheet: http://www.nuhorizons.com/FeaturedProducts/Volume1/SMSC/311x.pdf
+    Datasheet: Available on the Internet
   * SMSC SCH5027
     Prefix: 'sch5027'
     Addresses scanned: I2C 0x2c, 0x2d, 0x2e
     Datasheet: Provided by SMSC upon request and under NDA
+  * SMSC SCH5127
+    Prefix: 'sch5127'
+    Addresses scanned: none, address read from Super-I/O config space
+    Datasheet: Provided by SMSC upon request and under NDA
 
 Authors:
     Juerg Haefliger <juergh@gmail.com>
@@ -36,8 +40,8 @@ Description
 -----------
 
 This driver implements support for the hardware monitoring capabilities of the
-SMSC DME1737 and Asus A8000 (which are the same), SMSC SCH5027, and SMSC
-SCH311x Super-I/O chips. These chips feature monitoring of 3 temp sensors
+SMSC DME1737 and Asus A8000 (which are the same), SMSC SCH5027, SCH311x,
+and SCH5127 Super-I/O chips. These chips feature monitoring of 3 temp sensors
 temp[1-3] (2 remote diodes and 1 internal), 7 voltages in[0-6] (6 external and
 1 internal) and up to 6 fan speeds fan[1-6]. Additionally, the chips implement
 up to 5 PWM outputs pwm[1-3,5-6] for controlling fan speeds both manually and
@@ -48,14 +52,14 @@ Fan[3-6] and pwm[3,5-6] are optional features and their availability depends on
 the configuration of the chip. The driver will detect which features are
 present during initialization and create the sysfs attributes accordingly.
 
-For the SCH311x, fan[1-3] and pwm[1-3] are always present and fan[4-6] and
-pwm[5-6] don't exist.
+For the SCH311x and SCH5127, fan[1-3] and pwm[1-3] are always present and
+fan[4-6] and pwm[5-6] don't exist.
 
 The hardware monitoring features of the DME1737, A8000, and SCH5027 are only
-accessible via SMBus, while the SCH311x only provides access via the ISA bus.
-The driver will therefore register itself as an I2C client driver if it detects
-a DME1737, A8000, or SCH5027 and as a platform driver if it detects a SCH311x
-chip.
+accessible via SMBus, while the SCH311x and SCH5127 only provide access via
+the ISA bus. The driver will therefore register itself as an I2C client driver
+if it detects a DME1737, A8000, or SCH5027 and as a platform driver if it
+detects a SCH311x or SCH5127 chip.
 
 
 Voltage Monitoring
@@ -76,7 +80,7 @@ DME1737, A8000:
        in6: Vbat       (+3.0V)                 0V - 4.38V
 
 SCH311x:
-       in0: +2.5V                              0V - 6.64V
+       in0: +2.5V                              0V - 3.32V
        in1: Vccp       (processor core)        0V - 2V
        in2: VCC        (internal +3.3V)        0V - 4.38V
        in3: +5V                                0V - 6.64V
@@ -93,6 +97,15 @@ SCH5027:
        in5: VTR        (+3.3V standby)         0V - 4.38V
        in6: Vbat       (+3.0V)                 0V - 4.38V
 
+SCH5127:
+       in0: +2.5                               0V - 3.32V
+       in1: Vccp       (processor core)        0V - 3V
+       in2: VCC        (internal +3.3V)        0V - 4.38V
+       in3: V2_IN                              0V - 1.5V
+       in4: V1_IN                              0V - 1.5V
+       in5: VTR        (+3.3V standby)         0V - 4.38V
+       in6: Vbat       (+3.0V)                 0V - 4.38V
+
 Each voltage input has associated min and max limits which trigger an alarm
 when crossed.
 
@@ -293,3 +306,21 @@ pwm[1-3]_auto_point1_pwm   RW      Auto PWM pwm point. Auto_point1 is the
 pwm[1-3]_auto_point2_pwm       RO      Auto PWM pwm point. Auto_point2 is the
                                        full-speed duty-cycle which is hard-
                                        wired to 255 (100% duty-cycle).
+
+Chip Differences
+----------------
+
+Feature                        dme1737 sch311x sch5027 sch5127
+-------------------------------------------------------
+temp[1-3]_offset       yes     yes
+vid                    yes
+zone3                  yes     yes     yes
+zone[1-3]_hyst         yes     yes
+pwm min/off            yes     yes
+fan3                   opt     yes     opt     yes
+pwm3                   opt     yes     opt     yes
+fan4                   opt             opt
+fan5                   opt             opt
+pwm5                   opt             opt
+fan6                   opt             opt
+pwm6                   opt             opt
index 31660bf..b9843ea 100644 (file)
@@ -7,6 +7,11 @@ Supported chips:
     Addresses scanned: I2C 0x4c
     Datasheet: Publicly available at the National Semiconductor website
                http://www.national.com/pf/LM/LM63.html
+  * National Semiconductor LM64
+    Prefix: 'lm64'
+    Addresses scanned: I2C 0x18 and 0x4e
+    Datasheet: Publicly available at the National Semiconductor website
+               http://www.national.com/pf/LM/LM64.html
 
 Author: Jean Delvare <khali@linux-fr.org>
 
@@ -55,3 +60,5 @@ The lm63 driver will not update its values more frequently than every
 second; reading them more often will do no harm, but will return 'old'
 values.
 
+The LM64 is effectively an LM63 with GPIO lines. The driver does not
+support these GPIO lines at present.
index 02838a4..86b5880 100644 (file)
@@ -72,9 +72,7 @@ in6_min_alarm         5v  output undervoltage alarm
 in7_min_alarm          3v  output undervoltage alarm
 in8_min_alarm          Vee (-12v) output undervoltage alarm
 
-in9_input              GPIO #1 voltage data
-in10_input             GPIO #2 voltage data
-in11_input             GPIO #3 voltage data
+in9_input              GPIO voltage data
 
 power1_input           12v power usage (mW)
 power2_input           5v  power usage (mW)
index 3de6b0b..d4e2917 100644 (file)
@@ -80,9 +80,9 @@ All entries (except name) are optional, and should only be created in a
 given driver if the chip has the feature.
 
 
-********
-* Name *
-********
+*********************
+* Global attributes *
+*********************
 
 name           The chip name.
                This should be a short, lowercase string, not containing
@@ -91,6 +91,13 @@ name         The chip name.
                I2C devices get this attribute created automatically.
                RO
 
+update_rate    The rate at which the chip will update readings.
+               Unit: millisecond
+               RW
+               Some devices have a variable update rate. This attribute
+               can be used to change the update rate to the desired
+               frequency.
+
 
 ************
 * Voltages *
diff --git a/Documentation/hwmon/tmp102 b/Documentation/hwmon/tmp102
new file mode 100644 (file)
index 0000000..8454a77
--- /dev/null
@@ -0,0 +1,26 @@
+Kernel driver tmp102
+====================
+
+Supported chips:
+  * Texas Instruments TMP102
+    Prefix: 'tmp102'
+    Addresses scanned: none
+    Datasheet: http://focus.ti.com/docs/prod/folders/print/tmp102.html
+
+Author:
+       Steven King <sfking@fdwdc.com>
+
+Description
+-----------
+
+The Texas Instruments TMP102 implements one temperature sensor.  Limits can be
+set through the Overtemperature Shutdown register and Hysteresis register.  The
+sensor is accurate to 0.5 degree over the range of -25 to +85 C, and to 1.0
+degree from -40 to +125 C. Resolution of the sensor is 0.0625 degree.  The
+operating temperature has a minimum of -55 C and a maximum of +150 C.
+
+The TMP102 has a programmable update rate that can select between 8, 4, 1, and
+0.5 Hz. (Currently the driver only supports the default of 4 Hz).
+
+The driver provides the common sysfs-interface for temperatures (see
+Documentation/hwmon/sysfs-interface under Temperatures).
index b56ea86..1808f11 100644 (file)
@@ -145,11 +145,10 @@ and is between 256 and 4096 characters. It is defined in the file
 
        acpi=           [HW,ACPI,X86]
                        Advanced Configuration and Power Interface
-                       Format: { force | off | ht | strict | noirq | rsdt }
+                       Format: { force | off | strict | noirq | rsdt }
                        force -- enable ACPI if default was off
                        off -- disable ACPI if default was on
                        noirq -- do not use ACPI for IRQ routing
-                       ht -- run only enough ACPI to enable Hyper Threading
                        strict -- Be less tolerant of platforms that are not
                                strictly ACPI specification compliant.
                        rsdt -- prefer RSDT over (default) XSDT
@@ -758,6 +757,10 @@ and is between 256 and 4096 characters. It is defined in the file
                        Default value is 0.
                        Value can be changed at runtime via /selinux/enforce.
 
+       erst_disable    [ACPI]
+                       Disable Error Record Serialization Table (ERST)
+                       support.
+
        ether=          [HW,NET] Ethernet cards parameters
                        This option is obsoleted by the "netdev=" option, which
                        has equivalent usage. See its documentation for details.
@@ -852,6 +855,11 @@ and is between 256 and 4096 characters. It is defined in the file
        hd=             [EIDE] (E)IDE hard drive subsystem geometry
                        Format: <cyl>,<head>,<sect>
 
+       hest_disable    [ACPI]
+                       Disable Hardware Error Source Table (HEST) support;
+                       corresponding firmware-first mode error processing
+                       logic will be disabled.
+
        highmem=nn[KMG] [KNL,BOOT] forces the highmem zone to have an exact
                        size of <nn>. This works even on boxes that have no
                        highmem otherwise. This also works to reduce highmem
@@ -1252,6 +1260,8 @@ and is between 256 and 4096 characters. It is defined in the file
                        * nohrst, nosrst, norst: suppress hard, soft
                           and both resets.
 
+                       * dump_id: dump IDENTIFY data.
+
                        If there are multiple matching configurations changing
                        the same attribute, the last one is used.
 
index 33047a6..13608bd 100644 (file)
@@ -2887,6 +2887,13 @@ T:       git git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input.git
 S:     Maintained
 F:     drivers/input/
 
+INTEL IDLE DRIVER
+M:     Len Brown <lenb@kernel.org>
+L:     linux-pm@lists.linux-foundation.org
+T:     git git://git.kernel.org/pub/scm/linux/kernel/git/lenb/linux-idle-2.6.git
+S:     Supported
+F:     drivers/idle/intel_idle.c
+
 INTEL FRAMEBUFFER DRIVER (excluding 810 and 815)
 M:     Maik Broemme <mbroemme@plusserver.de>
 L:     linux-fbdev@vger.kernel.org
index 43af89c..44cea2d 100644 (file)
@@ -1,7 +1,7 @@
 #
 # Automatically generated make config: don't edit
 # Linux kernel version: 2.6.34
-# Wed May 26 19:04:29 2010
+# Fri May 28 19:15:48 2010
 #
 CONFIG_ARM=y
 CONFIG_HAVE_PWM=y
@@ -250,12 +250,15 @@ CONFIG_S3C_BOOT_UART_FORCE_FIFO=y
 CONFIG_S3C_LOWLEVEL_UART_PORT=0
 CONFIG_SAMSUNG_CLKSRC=y
 CONFIG_S3C_GPIO_CFG_S3C24XX=y
+CONFIG_S3C_GPIO_PULL_UPDOWN=y
 CONFIG_S3C_GPIO_PULL_UP=y
 CONFIG_SAMSUNG_GPIO_EXTRA=0
 CONFIG_S3C_GPIO_SPACE=0
 CONFIG_S3C_ADC=y
 CONFIG_S3C_DEV_HSMMC=y
+CONFIG_S3C_DEV_HSMMC1=y
 CONFIG_S3C_DEV_HWMON=y
+CONFIG_S3C_DEV_FB=y
 CONFIG_S3C_DEV_USB_HOST=y
 CONFIG_S3C_DEV_WDT=y
 CONFIG_S3C_DEV_NAND=y
@@ -322,11 +325,13 @@ CONFIG_MACH_SMDK2413=y
 CONFIG_MACH_S3C2413=y
 CONFIG_MACH_SMDK2412=y
 CONFIG_MACH_VSTMS=y
+CONFIG_CPU_S3C2416=y
+CONFIG_S3C2416_DMA=y
 
 #
 # S3C2416 Machines
 #
-# CONFIG_MACH_SMDK2416 is not set
+CONFIG_MACH_SMDK2416=y
 CONFIG_CPU_S3C2440=y
 CONFIG_CPU_S3C2442=y
 CONFIG_CPU_S3C244X=y
@@ -338,9 +343,9 @@ CONFIG_S3C2440_DMA=y
 # S3C2440 and S3C2442 Machines
 #
 CONFIG_MACH_ANUBIS=y
-# CONFIG_MACH_NEO1973_GTA02 is not set
+CONFIG_MACH_NEO1973_GTA02=y
 CONFIG_MACH_OSIRIS=y
-# CONFIG_MACH_OSIRIS_DVS is not set
+CONFIG_MACH_OSIRIS_DVS=m
 CONFIG_MACH_RX3715=y
 CONFIG_ARCH_S3C2440=y
 CONFIG_MACH_NEXCODER_2440=y
@@ -348,7 +353,7 @@ CONFIG_SMDK2440_CPU2440=y
 CONFIG_SMDK2440_CPU2442=y
 CONFIG_MACH_AT2440EVB=y
 CONFIG_MACH_MINI2440=y
-# CONFIG_MACH_RX1950 is not set
+CONFIG_MACH_RX1950=y
 CONFIG_CPU_S3C2443=y
 CONFIG_S3C2443_DMA=y
 
@@ -1302,6 +1307,7 @@ CONFIG_INPUT_POWERMATE=m
 CONFIG_INPUT_YEALINK=m
 CONFIG_INPUT_CM109=m
 CONFIG_INPUT_UINPUT=m
+# CONFIG_INPUT_PCF50633_PMU is not set
 # CONFIG_INPUT_PCF8574 is not set
 CONFIG_INPUT_GPIO_ROTARY_ENCODER=m
 
@@ -1490,7 +1496,16 @@ CONFIG_GPIOLIB=y
 # AC97 GPIO expanders:
 #
 # CONFIG_W1 is not set
-# CONFIG_POWER_SUPPLY is not set
+CONFIG_POWER_SUPPLY=y
+# CONFIG_POWER_SUPPLY_DEBUG is not set
+# CONFIG_PDA_POWER is not set
+# CONFIG_APM_POWER is not set
+# CONFIG_TEST_POWER is not set
+# CONFIG_BATTERY_DS2760 is not set
+# CONFIG_BATTERY_DS2782 is not set
+# CONFIG_BATTERY_BQ27x00 is not set
+# CONFIG_BATTERY_MAX17040 is not set
+# CONFIG_CHARGER_PCF50633 is not set
 CONFIG_HWMON=y
 CONFIG_HWMON_VID=m
 # CONFIG_HWMON_DEBUG_CHIP is not set
@@ -1607,7 +1622,7 @@ CONFIG_MFD_SM501=y
 # CONFIG_HTC_PASIC3 is not set
 # CONFIG_HTC_I2CPLD is not set
 # CONFIG_UCB1400_CORE is not set
-# CONFIG_TPS65010 is not set
+CONFIG_TPS65010=m
 # CONFIG_TWL4030_CORE is not set
 # CONFIG_MFD_TMIO is not set
 # CONFIG_MFD_T7L66XB is not set
@@ -1620,8 +1635,10 @@ CONFIG_MFD_SM501=y
 # CONFIG_MFD_WM831X is not set
 # CONFIG_MFD_WM8350_I2C is not set
 # CONFIG_MFD_WM8994 is not set
-# CONFIG_MFD_PCF50633 is not set
+CONFIG_MFD_PCF50633=y
 # CONFIG_MFD_MC13783 is not set
+# CONFIG_PCF50633_ADC is not set
+CONFIG_PCF50633_GPIO=y
 # CONFIG_AB3100_CORE is not set
 # CONFIG_EZX_PCAP is not set
 # CONFIG_AB4500_CORE is not set
@@ -1737,6 +1754,7 @@ CONFIG_SND_S3C24XX_SOC_I2S=y
 CONFIG_SND_S3C_I2SV2_SOC=m
 CONFIG_SND_S3C2412_SOC_I2S=m
 CONFIG_SND_S3C_SOC_AC97=m
+# CONFIG_SND_S3C24XX_SOC_NEO1973_GTA02_WM8753 is not set
 CONFIG_SND_S3C24XX_SOC_JIVE_WM8750=m
 CONFIG_SND_S3C24XX_SOC_SMDK2443_WM9710=m
 CONFIG_SND_S3C24XX_SOC_LN2440SBC_ALC650=m
@@ -2045,6 +2063,7 @@ CONFIG_RTC_INTF_DEV=y
 # CONFIG_RTC_DRV_BQ4802 is not set
 # CONFIG_RTC_DRV_RP5C01 is not set
 # CONFIG_RTC_DRV_V3020 is not set
+# CONFIG_RTC_DRV_PCF50633 is not set
 
 #
 # on-CPU RTC drivers
index 7d8b4cf..2b64238 100644 (file)
@@ -1,9 +1,10 @@
 #
 # Automatically generated make config: don't edit
 # Linux kernel version: 2.6.34
-# Wed May 26 19:04:30 2010
+# Fri May 28 19:05:39 2010
 #
 CONFIG_ARM=y
+CONFIG_HAVE_PWM=y
 CONFIG_SYS_SUPPORTS_APM_EMULATION=y
 CONFIG_GENERIC_GPIO=y
 CONFIG_GENERIC_TIME=y
@@ -253,12 +254,15 @@ CONFIG_S3C_GPIO_TRACK=y
 # CONFIG_S3C_ADC is not set
 CONFIG_S3C_DEV_HSMMC=y
 CONFIG_S3C_DEV_HSMMC1=y
+CONFIG_S3C_DEV_HSMMC2=y
+CONFIG_S3C_DEV_HWMON=y
 CONFIG_S3C_DEV_I2C1=y
 CONFIG_S3C_DEV_FB=y
 CONFIG_S3C_DEV_USB_HOST=y
 CONFIG_S3C_DEV_USB_HSOTG=y
 CONFIG_S3C_DEV_WDT=y
 CONFIG_S3C_DEV_NAND=y
+CONFIG_S3C_DEV_RTC=y
 CONFIG_SAMSUNG_DEV_ADC=y
 CONFIG_SAMSUNG_DEV_TS=y
 CONFIG_S3C_DMA=y
@@ -271,6 +275,7 @@ CONFIG_S3C_DMA=y
 # CONFIG_SAMSUNG_PM_CHECK is not set
 CONFIG_SAMSUNG_WAKEMASK=y
 CONFIG_PLAT_S3C64XX=y
+CONFIG_CPU_S3C6400=y
 CONFIG_CPU_S3C6410=y
 CONFIG_S3C64XX_DMA=y
 CONFIG_S3C64XX_SETUP_SDHCI=y
@@ -278,17 +283,18 @@ CONFIG_S3C64XX_SETUP_I2C0=y
 CONFIG_S3C64XX_SETUP_I2C1=y
 CONFIG_S3C64XX_SETUP_FB_24BPP=y
 CONFIG_S3C64XX_SETUP_SDHCI_GPIO=y
-# CONFIG_MACH_SMDK6400 is not set
-# CONFIG_MACH_ANW6410 is not set
+CONFIG_MACH_SMDK6400=y
+CONFIG_MACH_ANW6410=y
 CONFIG_MACH_SMDK6410=y
 CONFIG_SMDK6410_SD_CH0=y
 # CONFIG_SMDK6410_SD_CH1 is not set
 # CONFIG_SMDK6410_WM1190_EV1 is not set
 # CONFIG_SMDK6410_WM1192_EV1 is not set
-# CONFIG_MACH_NCP is not set
-# CONFIG_MACH_HMT is not set
-# CONFIG_MACH_SMARTQ5 is not set
-# CONFIG_MACH_SMARTQ7 is not set
+CONFIG_MACH_NCP=y
+CONFIG_MACH_HMT=y
+CONFIG_MACH_SMARTQ=y
+CONFIG_MACH_SMARTQ5=y
+CONFIG_MACH_SMARTQ7=y
 
 #
 # Processor Type
@@ -475,6 +481,9 @@ CONFIG_MTD_CFI_I2=y
 #
 # Self-contained MTD device drivers
 #
+# CONFIG_MTD_DATAFLASH is not set
+# CONFIG_MTD_M25P80 is not set
+# CONFIG_MTD_SST25L is not set
 # CONFIG_MTD_SLRAM is not set
 # CONFIG_MTD_PHRAM is not set
 # CONFIG_MTD_MTDRAM is not set
@@ -501,6 +510,7 @@ CONFIG_MTD_NAND_S3C2410=y
 # CONFIG_MTD_NAND_S3C2410_CLKSTOP is not set
 # CONFIG_MTD_NAND_DISKONCHIP is not set
 # CONFIG_MTD_NAND_PLATFORM is not set
+# CONFIG_MTD_ALAUDA is not set
 # CONFIG_MTD_ONENAND is not set
 
 #
@@ -521,6 +531,7 @@ CONFIG_BLK_DEV_LOOP=y
 #
 # DRBD disabled because PROC_FS, INET or CONNECTOR not selected
 #
+# CONFIG_BLK_DEV_UB is not set
 CONFIG_BLK_DEV_RAM=y
 CONFIG_BLK_DEV_RAM_COUNT=16
 CONFIG_BLK_DEV_RAM_SIZE=4096
@@ -534,12 +545,14 @@ CONFIG_MISC_DEVICES=y
 # CONFIG_ISL29003 is not set
 # CONFIG_SENSORS_TSL2550 is not set
 # CONFIG_DS1682 is not set
+# CONFIG_TI_DAC7512 is not set
 # CONFIG_C2PORT is not set
 
 #
 # EEPROM support
 #
 CONFIG_EEPROM_AT24=y
+# CONFIG_EEPROM_AT25 is not set
 # CONFIG_EEPROM_LEGACY is not set
 # CONFIG_EEPROM_MAX6875 is not set
 # CONFIG_EEPROM_93CX6 is not set
@@ -654,6 +667,7 @@ CONFIG_SERIAL_SAMSUNG_UARTS=4
 # CONFIG_SERIAL_SAMSUNG_DEBUG is not set
 CONFIG_SERIAL_SAMSUNG_CONSOLE=y
 CONFIG_SERIAL_S3C6400=y
+# CONFIG_SERIAL_MAX3100 is not set
 CONFIG_SERIAL_CORE=y
 CONFIG_SERIAL_CORE_CONSOLE=y
 # CONFIG_SERIAL_TIMBERDALE is not set
@@ -694,6 +708,7 @@ CONFIG_I2C_S3C2410=y
 #
 # CONFIG_I2C_PARPORT_LIGHT is not set
 # CONFIG_I2C_TAOS_EVM is not set
+# CONFIG_I2C_TINY_USB is not set
 
 #
 # Other I2C/SMBus bus drivers
@@ -703,7 +718,24 @@ CONFIG_I2C_S3C2410=y
 # CONFIG_I2C_DEBUG_CORE is not set
 # CONFIG_I2C_DEBUG_ALGO is not set
 # CONFIG_I2C_DEBUG_BUS is not set
-# CONFIG_SPI is not set
+CONFIG_SPI=y
+# CONFIG_SPI_DEBUG is not set
+CONFIG_SPI_MASTER=y
+
+#
+# SPI Master Controller Drivers
+#
+CONFIG_SPI_BITBANG=m
+CONFIG_SPI_GPIO=m
+CONFIG_SPI_S3C64XX=m
+# CONFIG_SPI_XILINX is not set
+# CONFIG_SPI_DESIGNWARE is not set
+
+#
+# SPI Protocol Masters
+#
+# CONFIG_SPI_SPIDEV is not set
+# CONFIG_SPI_TLE62X0 is not set
 
 #
 # PPS support
@@ -735,6 +767,9 @@ CONFIG_GPIOLIB=y
 #
 # SPI GPIO expanders:
 #
+# CONFIG_GPIO_MAX7301 is not set
+# CONFIG_GPIO_MCP23S08 is not set
+# CONFIG_GPIO_MC33880 is not set
 
 #
 # AC97 GPIO expanders:
@@ -750,6 +785,7 @@ CONFIG_HWMON=y
 #
 # CONFIG_SENSORS_AD7414 is not set
 # CONFIG_SENSORS_AD7418 is not set
+# CONFIG_SENSORS_ADCXX is not set
 # CONFIG_SENSORS_ADM1021 is not set
 # CONFIG_SENSORS_ADM1025 is not set
 # CONFIG_SENSORS_ADM1026 is not set
@@ -771,6 +807,7 @@ CONFIG_HWMON=y
 # CONFIG_SENSORS_GL520SM is not set
 # CONFIG_SENSORS_IT87 is not set
 # CONFIG_SENSORS_LM63 is not set
+# CONFIG_SENSORS_LM70 is not set
 # CONFIG_SENSORS_LM73 is not set
 # CONFIG_SENSORS_LM75 is not set
 # CONFIG_SENSORS_LM77 is not set
@@ -785,6 +822,7 @@ CONFIG_HWMON=y
 # CONFIG_SENSORS_LTC4215 is not set
 # CONFIG_SENSORS_LTC4245 is not set
 # CONFIG_SENSORS_LM95241 is not set
+# CONFIG_SENSORS_MAX1111 is not set
 # CONFIG_SENSORS_MAX1619 is not set
 # CONFIG_SENSORS_MAX6650 is not set
 # CONFIG_SENSORS_PC87360 is not set
@@ -796,6 +834,7 @@ CONFIG_HWMON=y
 # CONFIG_SENSORS_SMSC47M192 is not set
 # CONFIG_SENSORS_SMSC47B397 is not set
 # CONFIG_SENSORS_ADS7828 is not set
+# CONFIG_SENSORS_ADS7871 is not set
 # CONFIG_SENSORS_AMC6821 is not set
 # CONFIG_SENSORS_THMC50 is not set
 # CONFIG_SENSORS_TMP401 is not set
@@ -809,6 +848,7 @@ CONFIG_HWMON=y
 # CONFIG_SENSORS_W83L786NG is not set
 # CONFIG_SENSORS_W83627HF is not set
 # CONFIG_SENSORS_W83627EHF is not set
+# CONFIG_SENSORS_LIS3_SPI is not set
 # CONFIG_SENSORS_LIS3_I2C is not set
 # CONFIG_THERMAL is not set
 # CONFIG_WATCHDOG is not set
@@ -845,7 +885,10 @@ CONFIG_SSB_POSSIBLE=y
 # CONFIG_MFD_WM8350_I2C is not set
 # CONFIG_MFD_WM8994 is not set
 # CONFIG_MFD_PCF50633 is not set
+# CONFIG_MFD_MC13783 is not set
 # CONFIG_AB3100_CORE is not set
+# CONFIG_EZX_PCAP is not set
+# CONFIG_AB4500_CORE is not set
 # CONFIG_REGULATOR is not set
 # CONFIG_MEDIA_SUPPORT is not set
 
@@ -854,8 +897,47 @@ CONFIG_SSB_POSSIBLE=y
 #
 # CONFIG_VGASTATE is not set
 # CONFIG_VIDEO_OUTPUT_CONTROL is not set
-# CONFIG_FB is not set
-# CONFIG_BACKLIGHT_LCD_SUPPORT is not set
+CONFIG_FB=y
+# CONFIG_FIRMWARE_EDID is not set
+# CONFIG_FB_DDC is not set
+# CONFIG_FB_BOOT_VESA_SUPPORT is not set
+CONFIG_FB_CFB_FILLRECT=y
+CONFIG_FB_CFB_COPYAREA=y
+CONFIG_FB_CFB_IMAGEBLIT=y
+# CONFIG_FB_CFB_REV_PIXELS_IN_BYTE is not set
+# CONFIG_FB_SYS_FILLRECT is not set
+# CONFIG_FB_SYS_COPYAREA is not set
+# CONFIG_FB_SYS_IMAGEBLIT is not set
+# CONFIG_FB_FOREIGN_ENDIAN is not set
+# CONFIG_FB_SYS_FOPS is not set
+# CONFIG_FB_SVGALIB is not set
+# CONFIG_FB_MACMODES is not set
+# CONFIG_FB_BACKLIGHT is not set
+# CONFIG_FB_MODE_HELPERS is not set
+# CONFIG_FB_TILEBLITTING is not set
+
+#
+# Frame buffer hardware drivers
+#
+# CONFIG_FB_S1D13XXX is not set
+CONFIG_FB_S3C=y
+# CONFIG_FB_S3C_DEBUG_REGWRITE is not set
+# CONFIG_FB_VIRTUAL is not set
+# CONFIG_FB_METRONOME is not set
+# CONFIG_FB_MB862XX is not set
+# CONFIG_FB_BROADSHEET is not set
+CONFIG_BACKLIGHT_LCD_SUPPORT=y
+CONFIG_LCD_CLASS_DEVICE=y
+# CONFIG_LCD_L4F00242T03 is not set
+# CONFIG_LCD_LMS283GF05 is not set
+CONFIG_LCD_LTV350QV=y
+# CONFIG_LCD_ILI9320 is not set
+# CONFIG_LCD_TDO24M is not set
+# CONFIG_LCD_VGG2432A4 is not set
+# CONFIG_LCD_PLATFORM is not set
+CONFIG_BACKLIGHT_CLASS_DEVICE=y
+CONFIG_BACKLIGHT_GENERIC=y
+CONFIG_BACKLIGHT_PWM=y
 
 #
 # Display device support
@@ -867,6 +949,8 @@ CONFIG_SSB_POSSIBLE=y
 #
 # CONFIG_VGA_CONSOLE is not set
 CONFIG_DUMMY_CONSOLE=y
+# CONFIG_FRAMEBUFFER_CONSOLE is not set
+# CONFIG_LOGO is not set
 CONFIG_SOUND=y
 CONFIG_SOUND_OSS_CORE=y
 CONFIG_SOUND_OSS_CORE_PRECLAIM=y
@@ -895,6 +979,11 @@ CONFIG_SND_DRIVERS=y
 # CONFIG_SND_SERIAL_U16550 is not set
 # CONFIG_SND_MPU401 is not set
 CONFIG_SND_ARM=y
+CONFIG_SND_SPI=y
+CONFIG_SND_USB=y
+# CONFIG_SND_USB_AUDIO is not set
+# CONFIG_SND_USB_UA101 is not set
+# CONFIG_SND_USB_CAIAQ is not set
 CONFIG_SND_SOC=m
 CONFIG_SND_SOC_AC97_BUS=y
 CONFIG_SND_S3C24XX_SOC=m
@@ -909,29 +998,197 @@ CONFIG_AC97_BUS=m
 CONFIG_HID_SUPPORT=y
 CONFIG_HID=y
 # CONFIG_HIDRAW is not set
+
+#
+# USB Input Devices
+#
+CONFIG_USB_HID=y
 # CONFIG_HID_PID is not set
+# CONFIG_USB_HIDDEV is not set
 
 #
 # Special HID drivers
 #
+# CONFIG_HID_3M_PCT is not set
+CONFIG_HID_A4TECH=y
+CONFIG_HID_APPLE=y
+CONFIG_HID_BELKIN=y
+# CONFIG_HID_CANDO is not set
+CONFIG_HID_CHERRY=y
+CONFIG_HID_CHICONY=y
+# CONFIG_HID_PRODIKEYS is not set
+CONFIG_HID_CYPRESS=y
+# CONFIG_HID_DRAGONRISE is not set
+# CONFIG_HID_EGALAX is not set
+CONFIG_HID_EZKEY=y
+CONFIG_HID_KYE=y
+# CONFIG_HID_GYRATION is not set
+# CONFIG_HID_TWINHAN is not set
+CONFIG_HID_KENSINGTON=y
+CONFIG_HID_LOGITECH=y
+# CONFIG_LOGITECH_FF is not set
+# CONFIG_LOGIRUMBLEPAD2_FF is not set
+# CONFIG_LOGIG940_FF is not set
+CONFIG_HID_MICROSOFT=y
+# CONFIG_HID_MOSART is not set
+CONFIG_HID_MONTEREY=y
+# CONFIG_HID_NTRIG is not set
+# CONFIG_HID_ORTEK is not set
+# CONFIG_HID_PANTHERLORD is not set
+# CONFIG_HID_PETALYNX is not set
+# CONFIG_HID_PICOLCD is not set
+# CONFIG_HID_QUANTA is not set
+# CONFIG_HID_ROCCAT_KONE is not set
+# CONFIG_HID_SAMSUNG is not set
+# CONFIG_HID_SONY is not set
+# CONFIG_HID_STANTUM is not set
+# CONFIG_HID_SUNPLUS is not set
+# CONFIG_HID_GREENASIA is not set
+# CONFIG_HID_SMARTJOYPLUS is not set
+# CONFIG_HID_TOPSEED is not set
+# CONFIG_HID_THRUSTMASTER is not set
+# CONFIG_HID_ZEROPLUS is not set
+# CONFIG_HID_ZYDACRON is not set
 CONFIG_USB_SUPPORT=y
 CONFIG_USB_ARCH_HAS_HCD=y
 CONFIG_USB_ARCH_HAS_OHCI=y
 # CONFIG_USB_ARCH_HAS_EHCI is not set
-# CONFIG_USB is not set
+CONFIG_USB=y
+# CONFIG_USB_DEBUG is not set
+CONFIG_USB_ANNOUNCE_NEW_DEVICES=y
 
 #
-# Enable Host or Gadget support to see Inventra options
+# Miscellaneous USB options
 #
+CONFIG_USB_DEVICEFS=y
+CONFIG_USB_DEVICE_CLASS=y
+# CONFIG_USB_DYNAMIC_MINORS is not set
+# CONFIG_USB_MON is not set
+# CONFIG_USB_WUSB is not set
+# CONFIG_USB_WUSB_CBAF is not set
+
+#
+# USB Host Controller Drivers
+#
+# CONFIG_USB_C67X00_HCD is not set
+# CONFIG_USB_OXU210HP_HCD is not set
+# CONFIG_USB_ISP116X_HCD is not set
+# CONFIG_USB_ISP1760_HCD is not set
+# CONFIG_USB_ISP1362_HCD is not set
+CONFIG_USB_OHCI_HCD=y
+# CONFIG_USB_OHCI_BIG_ENDIAN_DESC is not set
+# CONFIG_USB_OHCI_BIG_ENDIAN_MMIO is not set
+CONFIG_USB_OHCI_LITTLE_ENDIAN=y
+# CONFIG_USB_SL811_HCD is not set
+# CONFIG_USB_R8A66597_HCD is not set
+# CONFIG_USB_HWA_HCD is not set
+# CONFIG_USB_MUSB_HDRC is not set
+
+#
+# USB Device Class drivers
+#
+CONFIG_USB_ACM=m
+CONFIG_USB_PRINTER=m
+# CONFIG_USB_WDM is not set
+# CONFIG_USB_TMC is not set
 
 #
 # NOTE: USB_STORAGE depends on SCSI but BLK_DEV_SD may
 #
+
+#
+# also be needed; see USB_STORAGE Help for more info
+#
+# CONFIG_USB_LIBUSUAL is not set
+
+#
+# USB Imaging devices
+#
+# CONFIG_USB_MDC800 is not set
+
+#
+# USB port drivers
+#
+CONFIG_USB_SERIAL=m
+# CONFIG_USB_EZUSB is not set
+CONFIG_USB_SERIAL_GENERIC=y
+# CONFIG_USB_SERIAL_AIRCABLE is not set
+# CONFIG_USB_SERIAL_ARK3116 is not set
+# CONFIG_USB_SERIAL_BELKIN is not set
+# CONFIG_USB_SERIAL_CH341 is not set
+# CONFIG_USB_SERIAL_WHITEHEAT is not set
+# CONFIG_USB_SERIAL_DIGI_ACCELEPORT is not set
+# CONFIG_USB_SERIAL_CP210X is not set
+# CONFIG_USB_SERIAL_CYPRESS_M8 is not set
+CONFIG_USB_SERIAL_EMPEG=m
+CONFIG_USB_SERIAL_FTDI_SIO=m
+# CONFIG_USB_SERIAL_FUNSOFT is not set
+# CONFIG_USB_SERIAL_VISOR is not set
+# CONFIG_USB_SERIAL_IPAQ is not set
+# CONFIG_USB_SERIAL_IR is not set
+# CONFIG_USB_SERIAL_EDGEPORT is not set
+# CONFIG_USB_SERIAL_EDGEPORT_TI is not set
+# CONFIG_USB_SERIAL_GARMIN is not set
+# CONFIG_USB_SERIAL_IPW is not set
+# CONFIG_USB_SERIAL_IUU is not set
+# CONFIG_USB_SERIAL_KEYSPAN_PDA is not set
+# CONFIG_USB_SERIAL_KEYSPAN is not set
+# CONFIG_USB_SERIAL_KLSI is not set
+# CONFIG_USB_SERIAL_KOBIL_SCT is not set
+# CONFIG_USB_SERIAL_MCT_U232 is not set
+# CONFIG_USB_SERIAL_MOS7720 is not set
+# CONFIG_USB_SERIAL_MOS7840 is not set
+# CONFIG_USB_SERIAL_MOTOROLA is not set
+# CONFIG_USB_SERIAL_NAVMAN is not set
+CONFIG_USB_SERIAL_PL2303=m
+# CONFIG_USB_SERIAL_OTI6858 is not set
+# CONFIG_USB_SERIAL_QCAUX is not set
+# CONFIG_USB_SERIAL_QUALCOMM is not set
+# CONFIG_USB_SERIAL_SPCP8X5 is not set
+# CONFIG_USB_SERIAL_HP4X is not set
+# CONFIG_USB_SERIAL_SAFE is not set
+# CONFIG_USB_SERIAL_SIEMENS_MPI is not set
+# CONFIG_USB_SERIAL_SIERRAWIRELESS is not set
+# CONFIG_USB_SERIAL_SYMBOL is not set
+# CONFIG_USB_SERIAL_TI is not set
+# CONFIG_USB_SERIAL_CYBERJACK is not set
+# CONFIG_USB_SERIAL_XIRCOM is not set
+# CONFIG_USB_SERIAL_OPTION is not set
+# CONFIG_USB_SERIAL_OMNINET is not set
+# CONFIG_USB_SERIAL_OPTICON is not set
+# CONFIG_USB_SERIAL_VIVOPAY_SERIAL is not set
+# CONFIG_USB_SERIAL_ZIO is not set
+# CONFIG_USB_SERIAL_DEBUG is not set
+
+#
+# USB Miscellaneous drivers
+#
+# CONFIG_USB_EMI62 is not set
+# CONFIG_USB_EMI26 is not set
+# CONFIG_USB_ADUTUX is not set
+# CONFIG_USB_SEVSEG is not set
+# CONFIG_USB_RIO500 is not set
+# CONFIG_USB_LEGOTOWER is not set
+# CONFIG_USB_LCD is not set
+# CONFIG_USB_LED is not set
+# CONFIG_USB_CYPRESS_CY7C63 is not set
+# CONFIG_USB_CYTHERM is not set
+# CONFIG_USB_IDMOUSE is not set
+# CONFIG_USB_FTDI_ELAN is not set
+# CONFIG_USB_APPLEDISPLAY is not set
+# CONFIG_USB_LD is not set
+# CONFIG_USB_TRANCEVIBRATOR is not set
+# CONFIG_USB_IOWARRIOR is not set
+# CONFIG_USB_TEST is not set
+# CONFIG_USB_ISIGHTFW is not set
 # CONFIG_USB_GADGET is not set
 
 #
 # OTG and related infrastructure
 #
+# CONFIG_USB_GPIO_VBUS is not set
+# CONFIG_USB_ULPI is not set
+# CONFIG_NOP_USB_XCEIV is not set
 CONFIG_MMC=y
 CONFIG_MMC_DEBUG=y
 CONFIG_MMC_UNSAFE_RESUME=y
@@ -951,11 +1208,77 @@ CONFIG_MMC_SDHCI=y
 # CONFIG_MMC_SDHCI_PLTFM is not set
 CONFIG_MMC_SDHCI_S3C=y
 # CONFIG_MMC_SDHCI_S3C_DMA is not set
+# CONFIG_MMC_SPI is not set
 # CONFIG_MEMSTICK is not set
 # CONFIG_NEW_LEDS is not set
 # CONFIG_ACCESSIBILITY is not set
 CONFIG_RTC_LIB=y
-# CONFIG_RTC_CLASS is not set
+CONFIG_RTC_CLASS=y
+CONFIG_RTC_HCTOSYS=y
+CONFIG_RTC_HCTOSYS_DEVICE="rtc0"
+# CONFIG_RTC_DEBUG is not set
+
+#
+# RTC interfaces
+#
+CONFIG_RTC_INTF_SYSFS=y
+CONFIG_RTC_INTF_PROC=y
+CONFIG_RTC_INTF_DEV=y
+# CONFIG_RTC_INTF_DEV_UIE_EMUL is not set
+# CONFIG_RTC_DRV_TEST is not set
+
+#
+# I2C RTC drivers
+#
+# CONFIG_RTC_DRV_DS1307 is not set
+# CONFIG_RTC_DRV_DS1374 is not set
+# CONFIG_RTC_DRV_DS1672 is not set
+# CONFIG_RTC_DRV_MAX6900 is not set
+# CONFIG_RTC_DRV_RS5C372 is not set
+# CONFIG_RTC_DRV_ISL1208 is not set
+# CONFIG_RTC_DRV_X1205 is not set
+# CONFIG_RTC_DRV_PCF8563 is not set
+# CONFIG_RTC_DRV_PCF8583 is not set
+# CONFIG_RTC_DRV_M41T80 is not set
+# CONFIG_RTC_DRV_BQ32K is not set
+# CONFIG_RTC_DRV_S35390A is not set
+# CONFIG_RTC_DRV_FM3130 is not set
+# CONFIG_RTC_DRV_RX8581 is not set
+# CONFIG_RTC_DRV_RX8025 is not set
+
+#
+# SPI RTC drivers
+#
+# CONFIG_RTC_DRV_M41T94 is not set
+# CONFIG_RTC_DRV_DS1305 is not set
+# CONFIG_RTC_DRV_DS1390 is not set
+# CONFIG_RTC_DRV_MAX6902 is not set
+# CONFIG_RTC_DRV_R9701 is not set
+# CONFIG_RTC_DRV_RS5C348 is not set
+# CONFIG_RTC_DRV_DS3234 is not set
+# CONFIG_RTC_DRV_PCF2123 is not set
+
+#
+# Platform RTC drivers
+#
+# CONFIG_RTC_DRV_CMOS is not set
+# CONFIG_RTC_DRV_DS1286 is not set
+# CONFIG_RTC_DRV_DS1511 is not set
+# CONFIG_RTC_DRV_DS1553 is not set
+# CONFIG_RTC_DRV_DS1742 is not set
+# CONFIG_RTC_DRV_STK17TA8 is not set
+# CONFIG_RTC_DRV_M48T86 is not set
+# CONFIG_RTC_DRV_M48T35 is not set
+# CONFIG_RTC_DRV_M48T59 is not set
+# CONFIG_RTC_DRV_MSM6242 is not set
+# CONFIG_RTC_DRV_BQ4802 is not set
+# CONFIG_RTC_DRV_RP5C01 is not set
+# CONFIG_RTC_DRV_V3020 is not set
+
+#
+# on-CPU RTC drivers
+#
+CONFIG_RTC_DRV_S3C=y
 # CONFIG_DMADEVICES is not set
 # CONFIG_AUXDISPLAY is not set
 # CONFIG_UIO is not set
@@ -1052,7 +1375,46 @@ CONFIG_ROMFS_ON_BLOCK=y
 #
 # CONFIG_PARTITION_ADVANCED is not set
 CONFIG_MSDOS_PARTITION=y
-# CONFIG_NLS is not set
+CONFIG_NLS=y
+CONFIG_NLS_DEFAULT="iso8859-1"
+# CONFIG_NLS_CODEPAGE_437 is not set
+# CONFIG_NLS_CODEPAGE_737 is not set
+# CONFIG_NLS_CODEPAGE_775 is not set
+# CONFIG_NLS_CODEPAGE_850 is not set
+# CONFIG_NLS_CODEPAGE_852 is not set
+# CONFIG_NLS_CODEPAGE_855 is not set
+# CONFIG_NLS_CODEPAGE_857 is not set
+# CONFIG_NLS_CODEPAGE_860 is not set
+# CONFIG_NLS_CODEPAGE_861 is not set
+# CONFIG_NLS_CODEPAGE_862 is not set
+# CONFIG_NLS_CODEPAGE_863 is not set
+# CONFIG_NLS_CODEPAGE_864 is not set
+# CONFIG_NLS_CODEPAGE_865 is not set
+# CONFIG_NLS_CODEPAGE_866 is not set
+# CONFIG_NLS_CODEPAGE_869 is not set
+# CONFIG_NLS_CODEPAGE_936 is not set
+# CONFIG_NLS_CODEPAGE_950 is not set
+# CONFIG_NLS_CODEPAGE_932 is not set
+# CONFIG_NLS_CODEPAGE_949 is not set
+# CONFIG_NLS_CODEPAGE_874 is not set
+# CONFIG_NLS_ISO8859_8 is not set
+# CONFIG_NLS_CODEPAGE_1250 is not set
+# CONFIG_NLS_CODEPAGE_1251 is not set
+# CONFIG_NLS_ASCII is not set
+# CONFIG_NLS_ISO8859_1 is not set
+# CONFIG_NLS_ISO8859_2 is not set
+# CONFIG_NLS_ISO8859_3 is not set
+# CONFIG_NLS_ISO8859_4 is not set
+# CONFIG_NLS_ISO8859_5 is not set
+# CONFIG_NLS_ISO8859_6 is not set
+# CONFIG_NLS_ISO8859_7 is not set
+# CONFIG_NLS_ISO8859_9 is not set
+# CONFIG_NLS_ISO8859_13 is not set
+# CONFIG_NLS_ISO8859_14 is not set
+# CONFIG_NLS_ISO8859_15 is not set
+# CONFIG_NLS_KOI8_R is not set
+# CONFIG_NLS_KOI8_U is not set
+# CONFIG_NLS_UTF8 is not set
 
 #
 # Kernel hacking
index abd0493..2ec3095 100644 (file)
@@ -17,6 +17,7 @@
 #include <linux/i2c.h>
 #include <linux/i2c/at24.h>
 #include <linux/i2c/pca953x.h>
+#include <linux/mfd/tps6507x.h>
 #include <linux/gpio.h>
 #include <linux/platform_device.h>
 #include <linux/mtd/mtd.h>
@@ -24,6 +25,8 @@
 #include <linux/mtd/partitions.h>
 #include <linux/mtd/physmap.h>
 #include <linux/regulator/machine.h>
+#include <linux/mfd/tps6507x.h>
+#include <linux/input/tps6507x-ts.h>
 
 #include <asm/mach-types.h>
 #include <asm/mach/arch.h>
@@ -533,10 +536,24 @@ struct regulator_init_data tps65070_regulator_data[] = {
        },
 };
 
+static struct touchscreen_init_data tps6507x_touchscreen_data = {
+       .poll_period =  30,     /* ms between touch samples */
+       .min_pressure = 0x30,   /* minimum pressure to trigger touch */
+       .vref = 0,              /* turn off vref when not using A/D */
+       .vendor = 0,            /* /sys/class/input/input?/id/vendor */
+       .product = 65070,       /* /sys/class/input/input?/id/product */
+       .version = 0x100,       /* /sys/class/input/input?/id/version */
+};
+
+static struct tps6507x_board tps_board = {
+       .tps6507x_pmic_init_data = &tps65070_regulator_data[0],
+       .tps6507x_ts_init_data = &tps6507x_touchscreen_data,
+};
+
 static struct i2c_board_info __initdata da850evm_tps65070_info[] = {
        {
                I2C_BOARD_INFO("tps6507x", 0x48),
-               .platform_data = &tps65070_regulator_data[0],
+               .platform_data = &tps_board,
        },
 };
 
index 33a8d35..62b5e40 100644 (file)
@@ -220,11 +220,54 @@ static struct mc13783_regulator_init_data moboard_regulators[] = {
        },
 };
 
+static struct mc13783_led_platform_data moboard_led[] = {
+       {
+               .id = MC13783_LED_R1,
+               .name = "coreboard-led-4:red",
+               .max_current = 2,
+       },
+       {
+               .id = MC13783_LED_G1,
+               .name = "coreboard-led-4:green",
+               .max_current = 2,
+       },
+       {
+               .id = MC13783_LED_B1,
+               .name = "coreboard-led-4:blue",
+               .max_current = 2,
+       },
+       {
+               .id = MC13783_LED_R2,
+               .name = "coreboard-led-5:red",
+               .max_current = 3,
+       },
+       {
+               .id = MC13783_LED_G2,
+               .name = "coreboard-led-5:green",
+               .max_current = 3,
+       },
+       {
+               .id = MC13783_LED_B2,
+               .name = "coreboard-led-5:blue",
+               .max_current = 3,
+       },
+};
+
+static struct mc13783_leds_platform_data moboard_leds = {
+       .num_leds = ARRAY_SIZE(moboard_led),
+       .led = moboard_led,
+       .flags = MC13783_LED_SLEWLIMTC,
+       .abmode = MC13783_LED_AB_DISABLED,
+       .tc1_period = MC13783_LED_PERIOD_10MS,
+       .tc2_period = MC13783_LED_PERIOD_10MS,
+};
+
 static struct mc13783_platform_data moboard_pmic = {
        .regulators = moboard_regulators,
        .num_regulators = ARRAY_SIZE(moboard_regulators),
+       .leds = &moboard_leds,
        .flags = MC13783_USE_REGULATOR | MC13783_USE_RTC |
-               MC13783_USE_ADC,
+               MC13783_USE_ADC | MC13783_USE_LED,
 };
 
 static struct spi_board_info moboard_spi_board_info[] __initdata = {
index 685f34a..fe0de16 100644 (file)
@@ -240,22 +240,23 @@ error_fail:
 
 #define ORION_BLINK_HALF_PERIOD 100 /* ms */
 
-static int dns323_gpio_blink_set(unsigned gpio,
+static int dns323_gpio_blink_set(unsigned gpio, int state,
        unsigned long *delay_on, unsigned long *delay_off)
 {
-       static int value = 0;
 
-       if (!*delay_on && !*delay_off)
+       if (delay_on && delay_off && !*delay_on && !*delay_off)
                *delay_on = *delay_off = ORION_BLINK_HALF_PERIOD;
 
-       if (ORION_BLINK_HALF_PERIOD == *delay_on
-           && ORION_BLINK_HALF_PERIOD == *delay_off) {
-               value = !value;
-               orion_gpio_set_blink(gpio, value);
-               return 0;
+       switch(state) {
+       case GPIO_LED_NO_BLINK_LOW:
+       case GPIO_LED_NO_BLINK_HIGH:
+               orion_gpio_set_blink(gpio, 0);
+               gpio_set_value(gpio, state);
+               break;
+       case GPIO_LED_BLINK:
+               orion_gpio_set_blink(gpio, 1);
        }
-
-       return -EINVAL;
+       return 0;
 }
 
 static struct gpio_led dns323_leds[] = {
@@ -263,6 +264,7 @@ static struct gpio_led dns323_leds[] = {
                .name = "power:blue",
                .gpio = DNS323_GPIO_LED_POWER2,
                .default_trigger = "timer",
+               .active_low = 1,
        }, {
                .name = "right:amber",
                .gpio = DNS323_GPIO_LED_RIGHT_AMBER,
index 45799c6..9e39faa 100644 (file)
@@ -49,7 +49,6 @@
 #include <linux/io.h>
 
 #include <linux/i2c.h>
-#include <linux/backlight.h>
 #include <linux/regulator/machine.h>
 
 #include <linux/mfd/pcf50633/core.h>
@@ -57,6 +56,7 @@
 #include <linux/mfd/pcf50633/adc.h>
 #include <linux/mfd/pcf50633/gpio.h>
 #include <linux/mfd/pcf50633/pmic.h>
+#include <linux/mfd/pcf50633/backlight.h>
 
 #include <asm/mach/arch.h>
 #include <asm/mach/map.h>
@@ -254,6 +254,12 @@ static char *gta02_batteries[] = {
        "battery",
 };
 
+static struct pcf50633_bl_platform_data gta02_backlight_data = {
+       .default_brightness = 0x3f,
+       .default_brightness_limit = 0,
+       .ramp_time = 5,
+};
+
 struct pcf50633_platform_data gta02_pcf_pdata = {
        .resumers = {
                [0] =   PCF50633_INT1_USBINS |
@@ -271,6 +277,8 @@ struct pcf50633_platform_data gta02_pcf_pdata = {
 
        .charger_reference_current_ma = 1000,
 
+       .backlight_data = &gta02_backlight_data,
+
        .reg_init_data = {
                [PCF50633_REGULATOR_AUTO] = {
                        .constraints = {
@@ -478,71 +486,6 @@ static struct s3c2410_udc_mach_info gta02_udc_cfg = {
 
 };
 
-
-
-static void gta02_bl_set_intensity(int intensity)
-{
-       struct pcf50633 *pcf = gta02_pcf;
-       int old_intensity = pcf50633_reg_read(pcf, PCF50633_REG_LEDOUT);
-
-       /* We map 8-bit intensity to 6-bit intensity in hardware. */
-       intensity >>= 2;
-
-       /*
-        * This can happen during, eg, print of panic on blanked console,
-        * but we can't service i2c without interrupts active, so abort.
-        */
-       if (in_atomic()) {
-               printk(KERN_ERR "gta02_bl_set_intensity called while atomic\n");
-               return;
-       }
-
-       old_intensity = pcf50633_reg_read(pcf, PCF50633_REG_LEDOUT);
-       if (intensity == old_intensity)
-               return;
-
-       /* We can't do this anywhere else. */
-       pcf50633_reg_write(pcf, PCF50633_REG_LEDDIM, 5);
-
-       if (!(pcf50633_reg_read(pcf, PCF50633_REG_LEDENA) & 3))
-               old_intensity = 0;
-
-       /*
-        * The PCF50633 cannot handle LEDOUT = 0 (datasheet p60)
-        * if seen, you have to re-enable the LED unit.
-        */
-       if (!intensity || !old_intensity)
-               pcf50633_reg_write(pcf, PCF50633_REG_LEDENA, 0);
-
-       /* Illegal to set LEDOUT to 0. */
-       if (!intensity)
-               pcf50633_reg_set_bit_mask(pcf, PCF50633_REG_LEDOUT, 0x3f, 2);
-       else
-               pcf50633_reg_set_bit_mask(pcf, PCF50633_REG_LEDOUT, 0x3f,
-                                         intensity);
-
-       if (intensity)
-               pcf50633_reg_write(pcf, PCF50633_REG_LEDENA, 2);
-
-}
-
-static struct generic_bl_info gta02_bl_info = {
-       .name                   = "gta02-bl",
-       .max_intensity          = 0xff,
-       .default_intensity      = 0xff,
-       .set_bl_intensity       = gta02_bl_set_intensity,
-};
-
-static struct platform_device gta02_bl_dev = {
-       .name                   = "generic-bl",
-       .id                     = 1,
-       .dev = {
-               .platform_data = &gta02_bl_info,
-       },
-};
-
-
-
 /* USB */
 static struct s3c2410_hcd_info gta02_usb_info __initdata = {
        .port[0]        = {
@@ -579,7 +522,6 @@ static struct platform_device *gta02_devices[] __initdata = {
 /* These guys DO need to be children of PMU. */
 
 static struct platform_device *gta02_devices_pmu_children[] = {
-       &gta02_bl_dev,
 };
 
 
index 7a4138b..fbd85a9 100644 (file)
@@ -259,6 +259,12 @@ static struct clk init_clocks[] = {
                .enable         = s3c64xx_hclk_ctrl,
                .ctrlbit        = S3C_CLKCON_HCLK_HSMMC2,
        }, {
+               .name           = "otg",
+               .id             = -1,
+               .parent         = &clk_h,
+               .enable         = s3c64xx_hclk_ctrl,
+               .ctrlbit        = S3C_CLKCON_HCLK_USB,
+       }, {
                .name           = "timers",
                .id             = -1,
                .parent         = &clk_p,
index c73ed06..f0394ba 100644 (file)
@@ -9,7 +9,7 @@
  */
 #include <linux/kernel.h>
 #include <linux/i2c.h>
-#include <linux/mfd/ab3100.h>
+#include <linux/mfd/abx500.h>
 #include <linux/regulator/machine.h>
 #include <linux/amba/bus.h>
 #include <mach/irqs.h>
@@ -46,6 +46,7 @@
 /* BUCK SLEEP 0xAC: 1.05V, Not used, SLEEP_A and B, Not used */
 #define BUCK_SLEEP_SETTING     0xAC
 
+#ifdef CONFIG_AB3100_CORE
 static struct regulator_consumer_supply supply_ldo_c[] = {
        {
                .dev_name = "ab3100-codec",
@@ -253,14 +254,68 @@ static struct ab3100_platform_data ab3100_plf_data = {
                LDO_D_SETTING,
        },
 };
+#endif
+
+#ifdef CONFIG_AB3550_CORE
+static struct abx500_init_settings ab3550_init_settings[] = {
+       {
+               .bank = 0,
+               .reg = AB3550_IMR1,
+               .setting = 0xff
+       },
+       {
+               .bank = 0,
+               .reg = AB3550_IMR2,
+               .setting = 0xff
+       },
+       {
+               .bank = 0,
+               .reg = AB3550_IMR3,
+               .setting = 0xff
+       },
+       {
+               .bank = 0,
+               .reg = AB3550_IMR4,
+               .setting = 0xff
+       },
+       {
+               .bank = 0,
+               .reg = AB3550_IMR5,
+               /* The two most significant bits are not used */
+               .setting = 0x3f
+       },
+};
+
+static struct ab3550_platform_data ab3550_plf_data = {
+       .irq = {
+               .base = IRQ_AB3550_BASE,
+               .count = (IRQ_AB3550_END - IRQ_AB3550_BASE + 1),
+       },
+       .dev_data = {
+       },
+       .init_settings = ab3550_init_settings,
+       .init_settings_sz = ARRAY_SIZE(ab3550_init_settings),
+};
+#endif
 
 static struct i2c_board_info __initdata bus0_i2c_board_info[] = {
+#if defined(CONFIG_AB3550_CORE)
+       {
+               .type = "ab3550",
+               .addr = 0x4A,
+               .irq = IRQ_U300_IRQ0_EXT,
+               .platform_data = &ab3550_plf_data,
+       },
+#elif defined(CONFIG_AB3100_CORE)
        {
                .type = "ab3100",
                .addr = 0x48,
                .irq = IRQ_U300_IRQ0_EXT,
                .platform_data = &ab3100_plf_data,
        },
+#else
+       { },
+#endif
 };
 
 static struct i2c_board_info __initdata bus1_i2c_board_info[] = {
index a6867b1..09b1b28 100644 (file)
 #define U300_NR_IRQS                   48
 #endif
 
+#ifdef CONFIG_AB3550_CORE
+#define IRQ_AB3550_BASE                        (U300_NR_IRQS)
+#define IRQ_AB3550_END                 (IRQ_AB3550_BASE + 37)
+
+#define NR_IRQS                                (IRQ_AB3550_END + 1)
+#else
 #define NR_IRQS U300_NR_IRQS
+#endif
 
 #endif
index 072196c..bb8d7b7 100644 (file)
@@ -50,7 +50,7 @@ struct pl022_config_chip ab4500_chip_info = {
 
 static struct spi_board_info u8500_spi_devices[] = {
        {
-               .modalias = "ab4500",
+               .modalias = "ab8500",
                .controller_data = &ab4500_chip_info,
                .max_speed_hz = 12000000,
                .bus_num = 0,
index 1b2c989..6544855 100644 (file)
@@ -411,7 +411,7 @@ static struct clk_lookup u8500_common_clks[] = {
        CLK(apetraceclk,        "apetrace",     NULL),
        CLK(mcdeclk,    "mcde",         NULL),
        CLK(ipi2clk,    "ipi2",         NULL),
-       CLK(dmaclk,     "dma40",        NULL),
+       CLK(dmaclk,     "dma40.0",      NULL),
        CLK(b2r2clk,    "b2r2",         NULL),
        CLK(tvclk,      "tv",           NULL),
 };
index d04299f..f21c444 100644 (file)
@@ -32,6 +32,7 @@ static struct platform_device *platform_devs[] __initdata = {
        &u8500_gpio_devs[6],
        &u8500_gpio_devs[7],
        &u8500_gpio_devs[8],
+       &u8500_dma40_device,
 };
 
 /* minimum static i/o mapping required to boot U8500 platforms */
@@ -71,6 +72,9 @@ void __init u8500_init_devices(void)
 {
        ux500_init_devices();
 
+       if (cpu_is_u8500ed())
+               dma40_u8500ed_fixup();
+
        /* Register the platform devices */
        platform_add_devices(platform_devs, ARRAY_SIZE(platform_devs));
 
index 2033423..8229034 100644 (file)
 #include <linux/gpio.h>
 #include <linux/amba/bus.h>
 
+#include <plat/ste_dma40.h>
+
 #include <mach/hardware.h>
 #include <mach/setup.h>
 
+#include "ste-dma40-db8500.h"
+
 static struct nmk_gpio_platform_data u8500_gpio_data[] = {
        GPIO_DATA("GPIO-0-31", 0),
        GPIO_DATA("GPIO-32-63", 32), /* 37..63 not routed to pin */
@@ -105,3 +109,108 @@ struct platform_device u8500_i2c4_device = {
        .resource       = u8500_i2c4_resources,
        .num_resources  = ARRAY_SIZE(u8500_i2c4_resources),
 };
+
+static struct resource dma40_resources[] = {
+       [0] = {
+               .start = U8500_DMA_BASE,
+               .end = U8500_DMA_BASE + SZ_4K - 1,
+               .flags = IORESOURCE_MEM,
+               .name = "base",
+       },
+       [1] = {
+               .start = U8500_DMA_LCPA_BASE,
+               .end = U8500_DMA_LCPA_BASE + SZ_4K - 1,
+               .flags = IORESOURCE_MEM,
+               .name = "lcpa",
+       },
+       [2] = {
+               .start = U8500_DMA_LCLA_BASE,
+               .end = U8500_DMA_LCLA_BASE + 16 * 1024 - 1,
+               .flags = IORESOURCE_MEM,
+               .name = "lcla",
+       },
+       [3] = {
+               .start = IRQ_DMA,
+               .end = IRQ_DMA,
+               .flags = IORESOURCE_IRQ}
+};
+
+/* Default configuration for physcial memcpy */
+struct stedma40_chan_cfg dma40_memcpy_conf_phy = {
+       .channel_type = (STEDMA40_CHANNEL_IN_PHY_MODE |
+                        STEDMA40_LOW_PRIORITY_CHANNEL |
+                        STEDMA40_PCHAN_BASIC_MODE),
+       .dir = STEDMA40_MEM_TO_MEM,
+
+       .src_info.endianess = STEDMA40_LITTLE_ENDIAN,
+       .src_info.data_width = STEDMA40_BYTE_WIDTH,
+       .src_info.psize = STEDMA40_PSIZE_PHY_1,
+
+       .dst_info.endianess = STEDMA40_LITTLE_ENDIAN,
+       .dst_info.data_width = STEDMA40_BYTE_WIDTH,
+       .dst_info.psize = STEDMA40_PSIZE_PHY_1,
+
+};
+/* Default configuration for logical memcpy */
+struct stedma40_chan_cfg dma40_memcpy_conf_log = {
+       .channel_type = (STEDMA40_CHANNEL_IN_LOG_MODE |
+                        STEDMA40_LOW_PRIORITY_CHANNEL |
+                        STEDMA40_LCHAN_SRC_LOG_DST_LOG |
+                        STEDMA40_NO_TIM_FOR_LINK),
+       .dir = STEDMA40_MEM_TO_MEM,
+
+       .src_info.endianess = STEDMA40_LITTLE_ENDIAN,
+       .src_info.data_width = STEDMA40_BYTE_WIDTH,
+       .src_info.psize = STEDMA40_PSIZE_LOG_1,
+
+       .dst_info.endianess = STEDMA40_LITTLE_ENDIAN,
+       .dst_info.data_width = STEDMA40_BYTE_WIDTH,
+       .dst_info.psize = STEDMA40_PSIZE_LOG_1,
+
+};
+
+/*
+ * Mapping between destination event lines and physical device address.
+ * The event line is tied to a device and therefor the address is constant.
+ */
+static const dma_addr_t dma40_tx_map[STEDMA40_NR_DEV];
+
+/* Mapping between source event lines and physical device address */
+static const dma_addr_t dma40_rx_map[STEDMA40_NR_DEV];
+
+/* Reserved event lines for memcpy only */
+static int dma40_memcpy_event[] = {
+       STEDMA40_MEMCPY_TX_1,
+       STEDMA40_MEMCPY_TX_2,
+       STEDMA40_MEMCPY_TX_3,
+       STEDMA40_MEMCPY_TX_4,
+};
+
+static struct stedma40_platform_data dma40_plat_data = {
+       .dev_len = STEDMA40_NR_DEV,
+       .dev_rx = dma40_rx_map,
+       .dev_tx = dma40_tx_map,
+       .memcpy = dma40_memcpy_event,
+       .memcpy_len = ARRAY_SIZE(dma40_memcpy_event),
+       .memcpy_conf_phy = &dma40_memcpy_conf_phy,
+       .memcpy_conf_log = &dma40_memcpy_conf_log,
+       .llis_per_log = 8,
+};
+
+struct platform_device u8500_dma40_device = {
+       .dev = {
+               .platform_data = &dma40_plat_data,
+       },
+       .name = "dma40",
+       .id = 0,
+       .num_resources = ARRAY_SIZE(dma40_resources),
+       .resource = dma40_resources
+};
+
+void dma40_u8500ed_fixup(void)
+{
+       dma40_plat_data.memcpy = NULL;
+       dma40_plat_data.memcpy_len = 0;
+       dma40_resources[0].start = U8500_DMA_BASE_ED;
+       dma40_resources[0].end = U8500_DMA_BASE_ED + SZ_4K - 1;
+}
index 9169e1e..85fc6a8 100644 (file)
@@ -7,6 +7,18 @@
 #ifndef __MACH_DB8500_REGS_H
 #define __MACH_DB8500_REGS_H
 
+/* Base address and bank offsets for ESRAM */
+#define U8500_ESRAM_BASE       0x40000000
+#define U8500_ESRAM_BANK_SIZE  0x00020000
+#define U8500_ESRAM_BANK0      U8500_ESRAM_BASE
+#define U8500_ESRAM_BANK1      (U8500_ESRAM_BASE + U8500_ESRAM_BANK_SIZE)
+#define U8500_ESRAM_BANK2      (U8500_ESRAM_BANK1 + U8500_ESRAM_BANK_SIZE)
+#define U8500_ESRAM_BANK3      (U8500_ESRAM_BANK2 + U8500_ESRAM_BANK_SIZE)
+#define U8500_ESRAM_BANK4      (U8500_ESRAM_BANK3 + U8500_ESRAM_BANK_SIZE)
+/* Use bank 4 for DMA LCLA and LCPA */
+#define U8500_DMA_LCLA_BASE    U8500_ESRAM_BANK4
+#define U8500_DMA_LCPA_BASE    (U8500_ESRAM_BANK4 + 0x4000)
+
 #define U8500_PER3_BASE                0x80000000
 #define U8500_STM_BASE         0x80100000
 #define U8500_STM_REG_BASE     (U8500_STM_BASE + 0xF000)
index 0422af0..c2b2f25 100644 (file)
@@ -25,5 +25,8 @@ extern struct platform_device ux500_i2c3_device;
 
 extern struct platform_device u8500_i2c0_device;
 extern struct platform_device u8500_i2c4_device;
+extern struct platform_device u8500_dma40_device;
+
+void dma40_u8500ed_fixup(void);
 
 #endif
diff --git a/arch/arm/mach-ux500/ste-dma40-db8500.h b/arch/arm/mach-ux500/ste-dma40-db8500.h
new file mode 100644 (file)
index 0000000..e701627
--- /dev/null
@@ -0,0 +1,154 @@
+/*
+ * arch/arm/mach-ux500/ste_dma40_db8500.h
+ * DB8500-SoC-specific configuration for DMA40
+ *
+ * Copyright (C) ST-Ericsson 2007-2010
+ * License terms: GNU General Public License (GPL) version 2
+ * Author: Per Friden <per.friden@stericsson.com>
+ * Author: Jonas Aaberg <jonas.aberg@stericsson.com>
+ */
+#ifndef STE_DMA40_DB8500_H
+#define STE_DMA40_DB8500_H
+
+#define STEDMA40_NR_DEV 64
+
+enum dma_src_dev_type {
+       STEDMA40_DEV_SPI0_RX = 0,
+       STEDMA40_DEV_SD_MMC0_RX = 1,
+       STEDMA40_DEV_SD_MMC1_RX = 2,
+       STEDMA40_DEV_SD_MMC2_RX = 3,
+       STEDMA40_DEV_I2C1_RX = 4,
+       STEDMA40_DEV_I2C3_RX = 5,
+       STEDMA40_DEV_I2C2_RX = 6,
+       STEDMA40_DEV_I2C4_RX = 7, /* Only on V1 */
+       STEDMA40_DEV_SSP0_RX = 8,
+       STEDMA40_DEV_SSP1_RX = 9,
+       STEDMA40_DEV_MCDE_RX = 10,
+       STEDMA40_DEV_UART2_RX = 11,
+       STEDMA40_DEV_UART1_RX = 12,
+       STEDMA40_DEV_UART0_RX = 13,
+       STEDMA40_DEV_MSP2_RX = 14,
+       STEDMA40_DEV_I2C0_RX = 15,
+       STEDMA40_DEV_USB_OTG_IEP_8 = 16,
+       STEDMA40_DEV_USB_OTG_IEP_1_9 = 17,
+       STEDMA40_DEV_USB_OTG_IEP_2_10 = 18,
+       STEDMA40_DEV_USB_OTG_IEP_3_11 = 19,
+       STEDMA40_DEV_SLIM0_CH0_RX_HSI_RX_CH0 = 20,
+       STEDMA40_DEV_SLIM0_CH1_RX_HSI_RX_CH1 = 21,
+       STEDMA40_DEV_SLIM0_CH2_RX_HSI_RX_CH2 = 22,
+       STEDMA40_DEV_SLIM0_CH3_RX_HSI_RX_CH3 = 23,
+       STEDMA40_DEV_SRC_SXA0_RX_TX = 24,
+       STEDMA40_DEV_SRC_SXA1_RX_TX = 25,
+       STEDMA40_DEV_SRC_SXA2_RX_TX = 26,
+       STEDMA40_DEV_SRC_SXA3_RX_TX = 27,
+       STEDMA40_DEV_SD_MM2_RX = 28,
+       STEDMA40_DEV_SD_MM0_RX = 29,
+       STEDMA40_DEV_MSP1_RX = 30,
+       /*
+        * This channel is either SlimBus or MSP,
+        * never both at the same time.
+        */
+       STEDMA40_SLIM0_CH0_RX = 31,
+       STEDMA40_DEV_MSP0_RX = 31,
+       STEDMA40_DEV_SD_MM1_RX = 32,
+       STEDMA40_DEV_SPI2_RX = 33,
+       STEDMA40_DEV_I2C3_RX2 = 34,
+       STEDMA40_DEV_SPI1_RX = 35,
+       STEDMA40_DEV_USB_OTG_IEP_4_12 = 36,
+       STEDMA40_DEV_USB_OTG_IEP_5_13 = 37,
+       STEDMA40_DEV_USB_OTG_IEP_6_14 = 38,
+       STEDMA40_DEV_USB_OTG_IEP_7_15 = 39,
+       STEDMA40_DEV_SPI3_RX = 40,
+       STEDMA40_DEV_SD_MM3_RX = 41,
+       STEDMA40_DEV_SD_MM4_RX = 42,
+       STEDMA40_DEV_SD_MM5_RX = 43,
+       STEDMA40_DEV_SRC_SXA4_RX_TX = 44,
+       STEDMA40_DEV_SRC_SXA5_RX_TX = 45,
+       STEDMA40_DEV_SRC_SXA6_RX_TX = 46,
+       STEDMA40_DEV_SRC_SXA7_RX_TX = 47,
+       STEDMA40_DEV_CAC1_RX = 48,
+       /* RX channels 49 and 50 are unused */
+       STEDMA40_DEV_MSHC_RX = 51,
+       STEDMA40_DEV_SLIM1_CH0_RX_HSI_RX_CH4 = 52,
+       STEDMA40_DEV_SLIM1_CH1_RX_HSI_RX_CH5 = 53,
+       STEDMA40_DEV_SLIM1_CH2_RX_HSI_RX_CH6 = 54,
+       STEDMA40_DEV_SLIM1_CH3_RX_HSI_RX_CH7 = 55,
+       /* RX channels 56 thru 60 are unused */
+       STEDMA40_DEV_CAC0_RX = 61,
+       /* RX channels 62 and 63 are unused */
+};
+
+enum dma_dest_dev_type {
+       STEDMA40_DEV_SPI0_TX = 0,
+       STEDMA40_DEV_SD_MMC0_TX = 1,
+       STEDMA40_DEV_SD_MMC1_TX = 2,
+       STEDMA40_DEV_SD_MMC2_TX = 3,
+       STEDMA40_DEV_I2C1_TX = 4,
+       STEDMA40_DEV_I2C3_TX = 5,
+       STEDMA40_DEV_I2C2_TX = 6,
+       STEDMA50_DEV_I2C4_TX = 7, /* Only on V1 */
+       STEDMA40_DEV_SSP0_TX = 8,
+       STEDMA40_DEV_SSP1_TX = 9,
+       /* TX channel 10 is unused */
+       STEDMA40_DEV_UART2_TX = 11,
+       STEDMA40_DEV_UART1_TX = 12,
+       STEDMA40_DEV_UART0_TX= 13,
+       STEDMA40_DEV_MSP2_TX = 14,
+       STEDMA40_DEV_I2C0_TX = 15,
+       STEDMA40_DEV_USB_OTG_OEP_8 = 16,
+       STEDMA40_DEV_USB_OTG_OEP_1_9 = 17,
+       STEDMA40_DEV_USB_OTG_OEP_2_10= 18,
+       STEDMA40_DEV_USB_OTG_OEP_3_11 = 19,
+       STEDMA40_DEV_SLIM0_CH0_TX_HSI_TX_CH0 = 20,
+       STEDMA40_DEV_SLIM0_CH1_TX_HSI_TX_CH1 = 21,
+       STEDMA40_DEV_SLIM0_CH2_TX_HSI_TX_CH2 = 22,
+       STEDMA40_DEV_SLIM0_CH3_TX_HSI_TX_CH3 = 23,
+       STEDMA40_DEV_DST_SXA0_RX_TX = 24,
+       STEDMA40_DEV_DST_SXA1_RX_TX = 25,
+       STEDMA40_DEV_DST_SXA2_RX_TX = 26,
+       STEDMA40_DEV_DST_SXA3_RX_TX = 27,
+       STEDMA40_DEV_SD_MM2_TX = 28,
+       STEDMA40_DEV_SD_MM0_TX = 29,
+       STEDMA40_DEV_MSP1_TX = 30,
+       /*
+        * This channel is either SlimBus or MSP,
+        * never both at the same time.
+        */
+       STEDMA40_SLIM0_CH0_TX = 31,
+       STEDMA40_DEV_MSP0_TX = 31,
+       STEDMA40_DEV_SD_MM1_TX = 32,
+       STEDMA40_DEV_SPI2_TX = 33,
+       /* Secondary I2C3 channel */
+       STEDMA40_DEV_I2C3_TX2 = 34,
+       STEDMA40_DEV_SPI1_TX = 35,
+       STEDMA40_DEV_USB_OTG_OEP_4_12 = 36,
+       STEDMA40_DEV_USB_OTG_OEP_5_13 = 37,
+       STEDMA40_DEV_USB_OTG_OEP_6_14 = 38,
+       STEDMA40_DEV_USB_OTG_OEP_7_15 = 39,
+       STEDMA40_DEV_SPI3_TX = 40,
+       STEDMA40_DEV_SD_MM3_TX = 41,
+       STEDMA40_DEV_SD_MM4_TX = 42,
+       STEDMA40_DEV_SD_MM5_TX = 43,
+       STEDMA40_DEV_DST_SXA4_RX_TX = 44,
+       STEDMA40_DEV_DST_SXA5_RX_TX = 45,
+       STEDMA40_DEV_DST_SXA6_RX_TX = 46,
+       STEDMA40_DEV_DST_SXA7_RX_TX = 47,
+       STEDMA40_DEV_CAC1_TX = 48,
+       STEDMA40_DEV_CAC1_TX_HAC1_TX = 49,
+       STEDMA40_DEV_HAC1_TX = 50,
+       STEDMA40_MEMXCPY_TX_0 = 51,
+       STEDMA40_DEV_SLIM1_CH0_TX_HSI_TX_CH4 = 52,
+       STEDMA40_DEV_SLIM1_CH1_TX_HSI_TX_CH5 = 53,
+       STEDMA40_DEV_SLIM1_CH2_TX_HSI_TX_CH6 = 54,
+       STEDMA40_DEV_SLIM1_CH3_TX_HSI_TX_CH7 = 55,
+       STEDMA40_MEMCPY_TX_1 = 56,
+       STEDMA40_MEMCPY_TX_2 = 57,
+       STEDMA40_MEMCPY_TX_3 = 58,
+       STEDMA40_MEMCPY_TX_4 = 59,
+       STEDMA40_MEMCPY_TX_5 = 60,
+       STEDMA40_DEV_CAC0_TX = 61,
+       STEDMA40_DEV_CAC0_TX_HAC0_TX = 62,
+       STEDMA40_DEV_HAC0_TX = 63,
+};
+
+#endif
index 24a931f..b5e2552 100644 (file)
@@ -148,6 +148,7 @@ static struct clk *s5p_clks[] __initdata = {
        &clk_fout_vpll,
        &clk_arm,
        &clk_vpll,
+       &clk_xusbxti,
 };
 
 void __init s5p_register_clocks(unsigned long xtal_freq)
index 34efdd2..db4112c 100644 (file)
@@ -43,6 +43,11 @@ struct s3c_gpio_chip;
  * layouts. Provide an point to vector control routine and provide any
  * per-bank configuration information that other systems such as the
  * external interrupt code will need.
+ *
+ * @sa s3c_gpio_cfgpin
+ * @sa s3c_gpio_getcfg
+ * @sa s3c_gpio_setpull
+ * @sa s3c_gpio_getpull
  */
 struct s3c_gpio_cfg {
        unsigned int    cfg_eint;
@@ -70,11 +75,25 @@ struct s3c_gpio_cfg {
 /**
  * s3c_gpio_cfgpin() - Change the GPIO function of a pin.
  * @pin pin The pin number to configure.
- * @pin to The configuration for the pin's function.
+ * @to to The configuration for the pin's function.
  *
  * Configure which function is actually connected to the external
  * pin, such as an gpio input, output or some form of special function
  * connected to an internal peripheral block.
+ *
+ * The @to parameter can be one of the generic S3C_GPIO_INPUT, S3C_GPIO_OUTPUT
+ * or S3C_GPIO_SFN() to indicate one of the possible values that the helper
+ * will then generate the correct bit mask and shift for the configuration.
+ *
+ * If a bank of GPIOs all needs to be set to special-function 2, then
+ * the following code will work:
+ *
+ *     for (gpio = start; gpio < end; gpio++)
+ *             s3c_gpio_cfgpin(gpio, S3C_GPIO_SFN(2));
+ *
+ * The @to parameter can also be a specific value already shifted to the
+ * correct position in the control register, although these are discouraged
+ * in newer kernels and are only being kept for compatibility.
  */
 extern int s3c_gpio_cfgpin(unsigned int pin, unsigned int to);
 
@@ -108,6 +127,8 @@ extern unsigned s3c_gpio_getcfg(unsigned int pin);
  * This function sets the state of the pull-{up,down} resistor for the
  * specified pin. It will return 0 if successfull, or a negative error
  * code if the pin cannot support the requested pull setting.
+ *
+ * @pull is one of S3C_GPIO_PULL_NONE, S3C_GPIO_PULL_DOWN or S3C_GPIO_PULL_UP.
 */
 extern int s3c_gpio_setpull(unsigned int pin, s3c_gpio_pull_t pull);
 
index 7dc0f0f..2797163 100644 (file)
@@ -17,8 +17,6 @@
 #define L1_CACHE_SHIFT         (CONFIG_FRV_L1_CACHE_SHIFT)
 #define L1_CACHE_BYTES         (1 << L1_CACHE_SHIFT)
 
-#define ARCH_KMALLOC_MINALIGN  L1_CACHE_BYTES
-
 #define __cacheline_aligned    __attribute__((aligned(L1_CACHE_BYTES)))
 #define ____cacheline_aligned  __attribute__((aligned(L1_CACHE_BYTES)))
 
index 2947764..ccae981 100644 (file)
@@ -35,8 +35,8 @@
  * the slab must be aligned such that load- and store-double instructions don't
  * fault if used
  */
-#define        ARCH_KMALLOC_MINALIGN           8
-#define        ARCH_SLAB_MINALIGN              8
+#define        ARCH_KMALLOC_MINALIGN           L1_CACHE_BYTES
+#define        ARCH_SLAB_MINALIGN              L1_CACHE_BYTES
 
 /*****************************************************************************/
 /*
index 21adbd7..837dc82 100644 (file)
@@ -94,7 +94,6 @@ ia64_acpi_release_global_lock (unsigned int *lock)
 #define acpi_noirq 0   /* ACPI always enabled on IA64 */
 #define acpi_pci_disabled 0 /* ACPI PCI always enabled on IA64 */
 #define acpi_strict 1  /* no ACPI spec workarounds on IA64 */
-#define acpi_ht 0      /* no HT-only mode on IA64 */
 #endif
 #define acpi_processor_cstate_check(x) (x) /* no idle limits on IA64 :) */
 static inline void disable_acpi(void) { }
index 518e876..6a1380e 100644 (file)
@@ -390,11 +390,13 @@ smp_callin (void)
 
        fix_b0_for_bsp();
 
+#ifdef CONFIG_NUMA
        /*
         * numa_node_id() works after this.
         */
        set_numa_node(cpu_to_node_map[cpuid]);
        set_numa_mem(local_memory_node(cpu_to_node_map[cpuid]));
+#endif
 
        ipi_call_lock_irq();
        spin_lock(&vector_lock);
@@ -638,7 +640,9 @@ void __devinit smp_prepare_boot_cpu(void)
 {
        cpu_set(smp_processor_id(), cpu_online_map);
        cpu_set(smp_processor_id(), cpu_callin_map);
+#ifdef CONFIG_NUMA
        set_numa_node(cpu_to_node_map[smp_processor_id()]);
+#endif
        per_cpu(cpu_state, smp_processor_id()) = CPU_ONLINE;
        paravirt_post_smp_prepare_boot_cpu();
 }
index 3efea7d..2437718 100644 (file)
@@ -17,6 +17,7 @@
 #include <linux/init.h>
 #include <linux/bootmem.h>
 #include <linux/module.h>
+#include <linux/random.h>
 #include <asm/mmzone.h>
 #include <asm/numa.h>
 
@@ -50,6 +51,22 @@ paddr_to_nid(unsigned long paddr)
        return (i < num_node_memblks) ? node_memblk[i].nid : (num_node_memblks ? -1 : 0);
 }
 
+/*
+ * Return the bit number of a random bit set in the nodemask.
+ *   (returns -1 if nodemask is empty)
+ */
+int __node_random(const nodemask_t *maskp)
+{
+       int w, bit = -1;
+
+       w = nodes_weight(*maskp);
+       if (w)
+               bit = bitmap_ord_to_pos(maskp->bits,
+                       get_random_int() % w, MAX_NUMNODES);
+       return bit;
+}
+EXPORT_SYMBOL(__node_random);
+
 #if defined(CONFIG_SPARSEMEM) && defined(CONFIG_NUMA)
 /*
  * Because of holes evaluate on section limits.
index 64aff52..aa2533a 100644 (file)
@@ -335,8 +335,11 @@ pcibios_setup_root_windows(struct pci_bus *bus, struct pci_controller *ctrl)
 }
 
 struct pci_bus * __devinit
-pci_acpi_scan_root(struct acpi_device *device, int domain, int bus)
+pci_acpi_scan_root(struct acpi_pci_root *root)
 {
+       struct acpi_device *device = root->device;
+       int domain = root->segment;
+       int bus = root->secondary.start;
        struct pci_controller *controller;
        unsigned int windows = 0;
        struct pci_bus *pbus;
index 4772777..4556d82 100644 (file)
@@ -2,6 +2,7 @@
 #define _PARISC_CACHEFLUSH_H
 
 #include <linux/mm.h>
+#include <linux/uaccess.h>
 
 /* The usual comment is "Caches aren't brain-dead on the <architecture>".
  * Unfortunately, that doesn't apply to PA-RISC. */
@@ -125,11 +126,20 @@ static inline void *kmap(struct page *page)
 
 #define kunmap(page)                   kunmap_parisc(page_address(page))
 
-#define kmap_atomic(page, idx)         page_address(page)
+static inline void *kmap_atomic(struct page *page, enum km_type idx)
+{
+       pagefault_disable();
+       return page_address(page);
+}
 
-#define kunmap_atomic(addr, idx)       kunmap_parisc(addr)
+static inline void kunmap_atomic(void *addr, enum km_type idx)
+{
+       kunmap_parisc(addr);
+       pagefault_enable();
+}
 
-#define kmap_atomic_pfn(pfn, idx)      page_address(pfn_to_page(pfn))
+#define kmap_atomic_prot(page, idx, prot)      kmap_atomic(page, idx)
+#define kmap_atomic_pfn(pfn, idx)      kmap_atomic(pfn_to_page(pfn), (idx))
 #define kmap_atomic_to_page(ptr)       virt_to_page(ptr)
 #endif
 
index ec787b4..dcd5510 100644 (file)
 #else
 #define FRAME_SIZE     64
 #endif
+#define FRAME_ALIGN    64
 
-#define align(x,y) (((x)+FRAME_SIZE+(y)-1) - (((x)+(y)-1)%(y)))
+/* Add FRAME_SIZE to the size x and align it to y. All definitions
+ * that use align_frame will include space for a frame.
+ */
+#define align_frame(x,y) (((x)+FRAME_SIZE+(y)-1) - (((x)+(y)-1)%(y)))
 
 int main(void)
 {
@@ -146,7 +150,8 @@ int main(void)
        DEFINE(TASK_PT_IOR, offsetof(struct task_struct, thread.regs.ior));
        BLANK();
        DEFINE(TASK_SZ, sizeof(struct task_struct));
-       DEFINE(TASK_SZ_ALGN, align(sizeof(struct task_struct), 64));
+       /* TASK_SZ_ALGN includes space for a stack frame. */
+       DEFINE(TASK_SZ_ALGN, align_frame(sizeof(struct task_struct), FRAME_ALIGN));
        BLANK();
        DEFINE(PT_PSW, offsetof(struct pt_regs, gr[ 0]));
        DEFINE(PT_GR1, offsetof(struct pt_regs, gr[ 1]));
@@ -233,7 +238,8 @@ int main(void)
        DEFINE(PT_ISR, offsetof(struct pt_regs, isr));
        DEFINE(PT_IOR, offsetof(struct pt_regs, ior));
        DEFINE(PT_SIZE, sizeof(struct pt_regs));
-       DEFINE(PT_SZ_ALGN, align(sizeof(struct pt_regs), 64));
+       /* PT_SZ_ALGN includes space for a stack frame. */
+       DEFINE(PT_SZ_ALGN, align_frame(sizeof(struct pt_regs), FRAME_ALIGN));
        BLANK();
        DEFINE(TI_TASK, offsetof(struct thread_info, task));
        DEFINE(TI_EXEC_DOMAIN, offsetof(struct thread_info, exec_domain));
@@ -242,7 +248,8 @@ int main(void)
        DEFINE(TI_SEGMENT, offsetof(struct thread_info, addr_limit));
        DEFINE(TI_PRE_COUNT, offsetof(struct thread_info, preempt_count));
        DEFINE(THREAD_SZ, sizeof(struct thread_info));
-       DEFINE(THREAD_SZ_ALGN, align(sizeof(struct thread_info), 64));
+       /* THREAD_SZ_ALGN includes space for a stack frame. */
+       DEFINE(THREAD_SZ_ALGN, align_frame(sizeof(struct thread_info), FRAME_ALIGN));
        BLANK();
        DEFINE(ICACHE_BASE, offsetof(struct pdc_cache_info, ic_base));
        DEFINE(ICACHE_STRIDE, offsetof(struct pdc_cache_info, ic_stride));
index 3a44f7f..6337ade 100644 (file)
        .align          32
        .endm
 
-       /* The following are simple 32 vs 64 bit instruction
-        * abstractions for the macros */
-       .macro          EXTR    reg1,start,length,reg2
-#ifdef CONFIG_64BIT
-       extrd,u         \reg1,32+(\start),\length,\reg2
-#else
-       extrw,u         \reg1,\start,\length,\reg2
-#endif
-       .endm
-
-       .macro          DEP     reg1,start,length,reg2
-#ifdef CONFIG_64BIT
-       depd            \reg1,32+(\start),\length,\reg2
-#else
-       depw            \reg1,\start,\length,\reg2
-#endif
-       .endm
-
-       .macro          DEPI    val,start,length,reg
-#ifdef CONFIG_64BIT
-       depdi           \val,32+(\start),\length,\reg
-#else
-       depwi           \val,\start,\length,\reg
-#endif
-       .endm
-
        /* In LP64, the space contains part of the upper 32 bits of the
         * fault.  We have to extract this and place it in the va,
         * zeroing the corresponding bits in the space register */
         */
        .macro          L2_ptep pmd,pte,index,va,fault
 #if PT_NLEVELS == 3
-       EXTR            \va,31-ASM_PMD_SHIFT,ASM_BITS_PER_PMD,\index
+       extru           \va,31-ASM_PMD_SHIFT,ASM_BITS_PER_PMD,\index
 #else
-       EXTR            \va,31-ASM_PGDIR_SHIFT,ASM_BITS_PER_PGD,\index
+       extru           \va,31-ASM_PGDIR_SHIFT,ASM_BITS_PER_PGD,\index
 #endif
-       DEP             %r0,31,PAGE_SHIFT,\pmd  /* clear offset */
+       dep             %r0,31,PAGE_SHIFT,\pmd  /* clear offset */
        copy            %r0,\pte
        ldw,s           \index(\pmd),\pmd
        bb,>=,n         \pmd,_PxD_PRESENT_BIT,\fault
-       DEP             %r0,31,PxD_FLAG_SHIFT,\pmd /* clear flags */
+       dep             %r0,31,PxD_FLAG_SHIFT,\pmd /* clear flags */
        copy            \pmd,%r9
        SHLREG          %r9,PxD_VALUE_SHIFT,\pmd
-       EXTR            \va,31-PAGE_SHIFT,ASM_BITS_PER_PTE,\index
-       DEP             %r0,31,PAGE_SHIFT,\pmd  /* clear offset */
+       extru           \va,31-PAGE_SHIFT,ASM_BITS_PER_PTE,\index
+       dep             %r0,31,PAGE_SHIFT,\pmd  /* clear offset */
        shladd          \index,BITS_PER_PTE_ENTRY,\pmd,\pmd
        LDREG           %r0(\pmd),\pte          /* pmd is now pte */
        bb,>=,n         \pte,_PAGE_PRESENT_BIT,\fault
        depdi           0,31,32,\tmp
 #endif
        copy            \va,\tmp1
-       DEPI            0,31,23,\tmp1
+       depi            0,31,23,\tmp1
        cmpb,COND(<>),n \tmp,\tmp1,\fault
        ldi             (_PAGE_DIRTY|_PAGE_WRITE|_PAGE_READ),\prot
        depd,z          \prot,8,7,\prot
@@ -997,13 +971,6 @@ intr_restore:
 
        rfi
        nop
-       nop
-       nop
-       nop
-       nop
-       nop
-       nop
-       nop
 
 #ifndef CONFIG_PREEMPT
 # define intr_do_preempt       intr_restore
@@ -2076,9 +2043,10 @@ syscall_restore:
        LDREG   TASK_PT_GR31(%r1),%r31     /* restore syscall rp */
 
        /* NOTE: We use rsm/ssm pair to make this operation atomic */
+       LDREG   TASK_PT_GR30(%r1),%r1              /* Get user sp */
        rsm     PSW_SM_I, %r0
-       LDREG   TASK_PT_GR30(%r1),%r30             /* restore user sp */
-       mfsp    %sr3,%r1                           /* Get users space id */
+       copy    %r1,%r30                           /* Restore user sp */
+       mfsp    %sr3,%r1                           /* Get user space id */
        mtsp    %r1,%sr7                           /* Restore sr7 */
        ssm     PSW_SM_I, %r0
 
index f5f9602..68e75ce 100644 (file)
@@ -47,18 +47,17 @@ ENTRY(linux_gateway_page)
        KILL_INSN
        .endr
 
-       /* ADDRESS 0xb0 to 0xb4, lws uses 1 insns for entry */
+       /* ADDRESS 0xb0 to 0xb8, lws uses two insns for entry */
        /* Light-weight-syscall entry must always be located at 0xb0 */
        /* WARNING: Keep this number updated with table size changes */
 #define __NR_lws_entries (2)
 
 lws_entry:
-       /* Unconditional branch to lws_start, located on the 
-          same gateway page */
-       b,n     lws_start
+       gate    lws_start, %r0          /* increase privilege */
+       depi    3, 31, 2, %r31          /* Ensure we return into user mode. */
 
-       /* Fill from 0xb4 to 0xe0 */
-       .rept 11
+       /* Fill from 0xb8 to 0xe0 */
+       .rept 10
        KILL_INSN
        .endr
 
@@ -423,9 +422,6 @@ tracesys_sigexit:
 
        *********************************************************/
 lws_start:
-       /* Gate and ensure we return to userspace */
-       gate    .+8, %r0
-       depi    3, 31, 2, %r31  /* Ensure we return to userspace */
 
 #ifdef CONFIG_64BIT
        /* FIXME: If we are a 64-bit kernel just
@@ -442,7 +438,7 @@ lws_start:
 #endif 
 
         /* Is the lws entry number valid? */
-       comiclr,>>=     __NR_lws_entries, %r20, %r0
+       comiclr,>>      __NR_lws_entries, %r20, %r0
        b,n     lws_exit_nosys
 
        /* WARNING: Trashing sr2 and sr3 */
@@ -473,7 +469,7 @@ lws_exit:
        /* now reset the lowest bit of sp if it was set */
        xor     %r30,%r1,%r30
 #endif
-       be,n    0(%sr3, %r31)
+       be,n    0(%sr7, %r31)
 
 
        
@@ -529,7 +525,6 @@ lws_compare_and_swap32:
 #endif
 
 lws_compare_and_swap:
-#ifdef CONFIG_SMP
        /* Load start of lock table */
        ldil    L%lws_lock_start, %r20
        ldo     R%lws_lock_start(%r20), %r28
@@ -572,8 +567,6 @@ cas_wouldblock:
        ldo     2(%r0), %r28                            /* 2nd case */
        b       lws_exit                                /* Contended... */
        ldo     -EAGAIN(%r0), %r21                      /* Spin in userspace */
-#endif
-/* CONFIG_SMP */
 
        /*
                prev = *addr;
@@ -601,13 +594,11 @@ cas_action:
 1:     ldw     0(%sr3,%r26), %r28
        sub,<>  %r28, %r25, %r0
 2:     stw     %r24, 0(%sr3,%r26)
-#ifdef CONFIG_SMP
        /* Free lock */
        stw     %r20, 0(%sr2,%r20)
-# if ENABLE_LWS_DEBUG
+#if ENABLE_LWS_DEBUG
        /* Clear thread register indicator */
        stw     %r0, 4(%sr2,%r20)
-# endif
 #endif
        /* Return to userspace, set no error */
        b       lws_exit
@@ -615,12 +606,10 @@ cas_action:
 
 3:             
        /* Error occured on load or store */
-#ifdef CONFIG_SMP
        /* Free lock */
        stw     %r20, 0(%sr2,%r20)
-# if ENABLE_LWS_DEBUG
+#if ENABLE_LWS_DEBUG
        stw     %r0, 4(%sr2,%r20)
-# endif
 #endif
        b       lws_exit
        ldo     -EFAULT(%r0),%r21       /* set errno */
@@ -672,7 +661,6 @@ ENTRY(sys_call_table64)
 END(sys_call_table64)
 #endif
 
-#ifdef CONFIG_SMP
        /*
                All light-weight-syscall atomic operations 
                will use this set of locks 
@@ -694,8 +682,6 @@ ENTRY(lws_lock_start)
        .endr
 END(lws_lock_start)
        .previous
-#endif
-/* CONFIG_SMP for lws_lock_start */
 
 .end
 
index 3ca1c61..27a7492 100644 (file)
@@ -342,6 +342,7 @@ decode_fpu(unsigned int Fpu_register[], unsigned int trap_counts[])
                return SIGNALCODE(SIGFPE, FPE_FLTINV);
          case DIVISIONBYZEROEXCEPTION:
                update_trap_counts(Fpu_register, aflags, bflags, trap_counts);
+               Clear_excp_register(exception_index);
                return SIGNALCODE(SIGFPE, FPE_FLTDIV);
          case INEXACTEXCEPTION:
                update_trap_counts(Fpu_register, aflags, bflags, trap_counts);
index c6afbfc..18162ce 100644 (file)
@@ -264,8 +264,7 @@ no_context:
 
   out_of_memory:
        up_read(&mm->mmap_sem);
-       printk(KERN_CRIT "VM: killing process %s\n", current->comm);
-       if (user_mode(regs))
-               do_group_exit(SIGKILL);
-       goto no_context;
+       if (!user_mode(regs))
+               goto no_context;
+       pagefault_out_of_memory();
 }
index 5c28082..1a40da9 100644 (file)
@@ -1849,8 +1849,7 @@ out:
        return ret;
 }
 
-static int spufs_mfc_fsync(struct file *file, struct dentry *dentry,
-                          int datasync)
+static int spufs_mfc_fsync(struct file *file, int datasync)
 {
        return spufs_mfc_flush(file, NULL);
 }
index fc1b1c4..e5e5f82 100644 (file)
@@ -251,7 +251,7 @@ const struct file_operations spufs_context_fops = {
        .llseek         = dcache_dir_lseek,
        .read           = generic_read_dir,
        .readdir        = dcache_readdir,
-       .fsync          = simple_sync_file,
+       .fsync          = noop_fsync,
 };
 EXPORT_SYMBOL_GPL(spufs_context_fops);
 
index 1fefae7..e19ff02 100644 (file)
@@ -102,7 +102,7 @@ static const struct file_operations hcall_inst_seq_fops = {
 #define CPU_NAME_BUF_SIZE      32
 
 
-static void probe_hcall_entry(unsigned long opcode, unsigned long *args)
+static void probe_hcall_entry(void *ignored, unsigned long opcode, unsigned long *args)
 {
        struct hcall_stats *h;
 
@@ -114,7 +114,7 @@ static void probe_hcall_entry(unsigned long opcode, unsigned long *args)
        h->purr_start = mfspr(SPRN_PURR);
 }
 
-static void probe_hcall_exit(unsigned long opcode, unsigned long retval,
+static void probe_hcall_exit(void *ignored, unsigned long opcode, unsigned long retval,
                             unsigned long *retbuf)
 {
        struct hcall_stats *h;
@@ -140,11 +140,11 @@ static int __init hcall_inst_init(void)
        if (!firmware_has_feature(FW_FEATURE_LPAR))
                return 0;
 
-       if (register_trace_hcall_entry(probe_hcall_entry))
+       if (register_trace_hcall_entry(probe_hcall_entry, NULL))
                return -EINVAL;
 
-       if (register_trace_hcall_exit(probe_hcall_exit)) {
-               unregister_trace_hcall_entry(probe_hcall_entry);
+       if (register_trace_hcall_exit(probe_hcall_exit, NULL)) {
+               unregister_trace_hcall_entry(probe_hcall_entry, NULL);
                return -EINVAL;
        }
 
index 34ce49f..0ec92c8 100644 (file)
@@ -92,6 +92,8 @@ struct cpu_hw_events {
 
        /* Enabled/disable state.  */
        int                     enabled;
+
+       unsigned int            group_flag;
 };
 DEFINE_PER_CPU(struct cpu_hw_events, cpu_hw_events) = { .enabled = 1, };
 
@@ -981,53 +983,6 @@ static int collect_events(struct perf_event *group, int max_count,
        return n;
 }
 
-static void event_sched_in(struct perf_event *event)
-{
-       event->state = PERF_EVENT_STATE_ACTIVE;
-       event->oncpu = smp_processor_id();
-       event->tstamp_running += event->ctx->time - event->tstamp_stopped;
-       if (is_software_event(event))
-               event->pmu->enable(event);
-}
-
-int hw_perf_group_sched_in(struct perf_event *group_leader,
-                          struct perf_cpu_context *cpuctx,
-                          struct perf_event_context *ctx)
-{
-       struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events);
-       struct perf_event *sub;
-       int n0, n;
-
-       if (!sparc_pmu)
-               return 0;
-
-       n0 = cpuc->n_events;
-       n = collect_events(group_leader, perf_max_events - n0,
-                          &cpuc->event[n0], &cpuc->events[n0],
-                          &cpuc->current_idx[n0]);
-       if (n < 0)
-               return -EAGAIN;
-       if (check_excludes(cpuc->event, n0, n))
-               return -EINVAL;
-       if (sparc_check_constraints(cpuc->event, cpuc->events, n + n0))
-               return -EAGAIN;
-       cpuc->n_events = n0 + n;
-       cpuc->n_added += n;
-
-       cpuctx->active_oncpu += n;
-       n = 1;
-       event_sched_in(group_leader);
-       list_for_each_entry(sub, &group_leader->sibling_list, group_entry) {
-               if (sub->state != PERF_EVENT_STATE_OFF) {
-                       event_sched_in(sub);
-                       n++;
-               }
-       }
-       ctx->nr_active += n;
-
-       return 1;
-}
-
 static int sparc_pmu_enable(struct perf_event *event)
 {
        struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events);
@@ -1045,11 +1000,20 @@ static int sparc_pmu_enable(struct perf_event *event)
        cpuc->events[n0] = event->hw.event_base;
        cpuc->current_idx[n0] = PIC_NO_INDEX;
 
+       /*
+        * If group events scheduling transaction was started,
+        * skip the schedulability test here, it will be peformed
+        * at commit time(->commit_txn) as a whole
+        */
+       if (cpuc->group_flag & PERF_EVENT_TXN_STARTED)
+               goto nocheck;
+
        if (check_excludes(cpuc->event, n0, 1))
                goto out;
        if (sparc_check_constraints(cpuc->event, cpuc->events, n0 + 1))
                goto out;
 
+nocheck:
        cpuc->n_events++;
        cpuc->n_added++;
 
@@ -1129,11 +1093,61 @@ static int __hw_perf_event_init(struct perf_event *event)
        return 0;
 }
 
+/*
+ * Start group events scheduling transaction
+ * Set the flag to make pmu::enable() not perform the
+ * schedulability test, it will be performed at commit time
+ */
+static void sparc_pmu_start_txn(const struct pmu *pmu)
+{
+       struct cpu_hw_events *cpuhw = &__get_cpu_var(cpu_hw_events);
+
+       cpuhw->group_flag |= PERF_EVENT_TXN_STARTED;
+}
+
+/*
+ * Stop group events scheduling transaction
+ * Clear the flag and pmu::enable() will perform the
+ * schedulability test.
+ */
+static void sparc_pmu_cancel_txn(const struct pmu *pmu)
+{
+       struct cpu_hw_events *cpuhw = &__get_cpu_var(cpu_hw_events);
+
+       cpuhw->group_flag &= ~PERF_EVENT_TXN_STARTED;
+}
+
+/*
+ * Commit group events scheduling transaction
+ * Perform the group schedulability test as a whole
+ * Return 0 if success
+ */
+static int sparc_pmu_commit_txn(const struct pmu *pmu)
+{
+       struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events);
+       int n;
+
+       if (!sparc_pmu)
+               return -EINVAL;
+
+       cpuc = &__get_cpu_var(cpu_hw_events);
+       n = cpuc->n_events;
+       if (check_excludes(cpuc->event, 0, n))
+               return -EINVAL;
+       if (sparc_check_constraints(cpuc->event, cpuc->events, n))
+               return -EAGAIN;
+
+       return 0;
+}
+
 static const struct pmu pmu = {
        .enable         = sparc_pmu_enable,
        .disable        = sparc_pmu_disable,
        .read           = sparc_pmu_read,
        .unthrottle     = sparc_pmu_unthrottle,
+       .start_txn      = sparc_pmu_start_txn,
+       .cancel_txn     = sparc_pmu_cancel_txn,
+       .commit_txn     = sparc_pmu_commit_txn,
 };
 
 const struct pmu *hw_perf_event_init(struct perf_event *event)
index 56f462c..aa2c39d 100644 (file)
@@ -85,7 +85,6 @@ extern int acpi_ioapic;
 extern int acpi_noirq;
 extern int acpi_strict;
 extern int acpi_disabled;
-extern int acpi_ht;
 extern int acpi_pci_disabled;
 extern int acpi_skip_timer_override;
 extern int acpi_use_timer_override;
@@ -97,7 +96,6 @@ void acpi_pic_sci_set_trigger(unsigned int, u16);
 static inline void disable_acpi(void)
 {
        acpi_disabled = 1;
-       acpi_ht = 0;
        acpi_pci_disabled = 1;
        acpi_noirq = 1;
 }
index dca9c54..4681459 100644 (file)
@@ -332,6 +332,7 @@ static __always_inline __pure bool __static_cpu_has(u8 bit)
 #endif
 }
 
+#if __GNUC__ >= 4
 #define static_cpu_has(bit)                                    \
 (                                                              \
        __builtin_constant_p(boot_cpu_has(bit)) ?               \
@@ -340,6 +341,12 @@ static __always_inline __pure bool __static_cpu_has(u8 bit)
                __static_cpu_has(bit) :                         \
                boot_cpu_has(bit)                               \
 )
+#else
+/*
+ * gcc 3.x is too stupid to do the static test; fall back to dynamic.
+ */
+#define static_cpu_has(bit) boot_cpu_has(bit)
+#endif
 
 #endif /* defined(__KERNEL__) && !defined(__ASSEMBLY__) */
 
index 6c3fdd6..f32a430 100644 (file)
@@ -225,5 +225,13 @@ extern void mcheck_intel_therm_init(void);
 static inline void mcheck_intel_therm_init(void) { }
 #endif
 
+/*
+ * Used by APEI to report memory error via /dev/mcelog
+ */
+
+struct cper_sec_mem_err;
+extern void apei_mce_report_mem_error(int corrected,
+                                     struct cper_sec_mem_err *mem_err);
+
 #endif /* __KERNEL__ */
 #endif /* _ASM_X86_MCE_H */
index b05400a..64a8ebf 100644 (file)
@@ -89,7 +89,8 @@
        P4_CCCR_ENABLE)
 
 /* HT mask */
-#define P4_CCCR_MASK_HT        (P4_CCCR_MASK | P4_CCCR_THREAD_ANY)
+#define P4_CCCR_MASK_HT                                \
+       (P4_CCCR_MASK | P4_CCCR_OVF_PMI_T1 | P4_CCCR_THREAD_ANY)
 
 #define P4_GEN_ESCR_EMASK(class, name, bit)    \
        class##__##name = ((1 << bit) << P4_ESCR_EVENTMASK_SHIFT)
diff --git a/arch/x86/include/asm/rdc321x_defs.h b/arch/x86/include/asm/rdc321x_defs.h
deleted file mode 100644 (file)
index c8e9c8b..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-#define PFX    "rdc321x: "
-
-/* General purpose configuration and data registers */
-#define RDC3210_CFGREG_ADDR     0x0CF8
-#define RDC3210_CFGREG_DATA     0x0CFC
-
-#define RDC321X_GPIO_CTRL_REG1 0x48
-#define RDC321X_GPIO_CTRL_REG2 0x84
-#define RDC321X_GPIO_DATA_REG1 0x4c
-#define RDC321X_GPIO_DATA_REG2 0x88
-
-#define RDC321X_MAX_GPIO       58
index 62ba940..f0b6e5d 100644 (file)
@@ -239,8 +239,8 @@ static inline struct thread_info *current_thread_info(void)
 #define TS_USEDFPU             0x0001  /* FPU was used by this task
                                           this quantum (SMP) */
 #define TS_COMPAT              0x0002  /* 32bit syscall active (64BIT)*/
-#define TS_POLLING             0x0004  /* true if in idle loop
-                                          and not sleeping */
+#define TS_POLLING             0x0004  /* idle task polling need_resched,
+                                          skip sending interrupt */
 #define TS_RESTORE_SIGMASK     0x0008  /* restore signal mask in do_signal() */
 
 #define tsk_is_polling(t) (task_thread_info(t)->status & TS_POLLING)
index 488be46..60cc405 100644 (file)
@@ -63,7 +63,6 @@ EXPORT_SYMBOL(acpi_disabled);
 int acpi_noirq;                                /* skip ACPI IRQ initialization */
 int acpi_pci_disabled;         /* skip ACPI PCI scan and IRQ initialization */
 EXPORT_SYMBOL(acpi_pci_disabled);
-int acpi_ht __initdata = 1;    /* enable HT */
 
 int acpi_lapic;
 int acpi_ioapic;
@@ -1501,9 +1500,8 @@ void __init acpi_boot_table_init(void)
 
        /*
         * If acpi_disabled, bail out
-        * One exception: acpi=ht continues far enough to enumerate LAPICs
         */
-       if (acpi_disabled && !acpi_ht)
+       if (acpi_disabled)
                return; 
 
        /*
@@ -1534,9 +1532,8 @@ int __init early_acpi_boot_init(void)
 {
        /*
         * If acpi_disabled, bail out
-        * One exception: acpi=ht continues far enough to enumerate LAPICs
         */
-       if (acpi_disabled && !acpi_ht)
+       if (acpi_disabled)
                return 1;
 
        /*
@@ -1554,9 +1551,8 @@ int __init acpi_boot_init(void)
 
        /*
         * If acpi_disabled, bail out
-        * One exception: acpi=ht continues far enough to enumerate LAPICs
         */
-       if (acpi_disabled && !acpi_ht)
+       if (acpi_disabled)
                return 1;
 
        acpi_table_parse(ACPI_SIG_BOOT, acpi_parse_sbf);
@@ -1591,21 +1587,12 @@ static int __init parse_acpi(char *arg)
        /* acpi=force to over-ride black-list */
        else if (strcmp(arg, "force") == 0) {
                acpi_force = 1;
-               acpi_ht = 1;
                acpi_disabled = 0;
        }
        /* acpi=strict disables out-of-spec workarounds */
        else if (strcmp(arg, "strict") == 0) {
                acpi_strict = 1;
        }
-       /* Limit ACPI just to boot-time to enable HT */
-       else if (strcmp(arg, "ht") == 0) {
-               if (!acpi_force) {
-                       printk(KERN_WARNING "acpi=ht will be removed in Linux-2.6.35\n");
-                       disable_acpi();
-               }
-               acpi_ht = 1;
-       }
        /* acpi=rsdt use RSDT instead of XSDT */
        else if (strcmp(arg, "rsdt") == 0) {
                acpi_rsdt_forced = 1;
index f996103..82e5086 100644 (file)
@@ -162,8 +162,6 @@ static int __init acpi_sleep_setup(char *str)
 #endif
                if (strncmp(str, "old_ordering", 12) == 0)
                        acpi_old_suspend_ordering();
-               if (strncmp(str, "sci_force_enable", 16) == 0)
-                       acpi_set_sci_en_on_resume();
                str = strchr(str, ',');
                if (str != NULL)
                        str += strspn(str, ", \t");
index e5a4a1e..c02cc69 100644 (file)
@@ -51,6 +51,7 @@
 #include <asm/smp.h>
 #include <asm/mce.h>
 #include <asm/kvm_para.h>
+#include <asm/tsc.h>
 
 unsigned int num_processors;
 
@@ -1151,8 +1152,13 @@ static void __cpuinit lapic_setup_esr(void)
  */
 void __cpuinit setup_local_APIC(void)
 {
-       unsigned int value;
-       int i, j;
+       unsigned int value, queued;
+       int i, j, acked = 0;
+       unsigned long long tsc = 0, ntsc;
+       long long max_loops = cpu_khz;
+
+       if (cpu_has_tsc)
+               rdtscll(tsc);
 
        if (disable_apic) {
                arch_disable_smp_support();
@@ -1204,13 +1210,32 @@ void __cpuinit setup_local_APIC(void)
         * the interrupt. Hence a vector might get locked. It was noticed
         * for timer irq (vector 0x31). Issue an extra EOI to clear ISR.
         */
-       for (i = APIC_ISR_NR - 1; i >= 0; i--) {
-               value = apic_read(APIC_ISR + i*0x10);
-               for (j = 31; j >= 0; j--) {
-                       if (value & (1<<j))
-                               ack_APIC_irq();
+       do {
+               queued = 0;
+               for (i = APIC_ISR_NR - 1; i >= 0; i--)
+                       queued |= apic_read(APIC_IRR + i*0x10);
+
+               for (i = APIC_ISR_NR - 1; i >= 0; i--) {
+                       value = apic_read(APIC_ISR + i*0x10);
+                       for (j = 31; j >= 0; j--) {
+                               if (value & (1<<j)) {
+                                       ack_APIC_irq();
+                                       acked++;
+                               }
+                       }
                }
-       }
+               if (acked > 256) {
+                       printk(KERN_ERR "LAPIC pending interrupts after %d EOI\n",
+                              acked);
+                       break;
+               }
+               if (cpu_has_tsc) {
+                       rdtscll(ntsc);
+                       max_loops = (cpu_khz << 10) - (ntsc - tsc);
+               } else
+                       max_loops--;
+       } while (queued && max_loops > 0);
+       WARN_ON(max_loops <= 0);
 
        /*
         * Now that we are all set up, enable the APIC
index 6f3dc8f..7ec2123 100644 (file)
@@ -1497,8 +1497,8 @@ static struct cpufreq_driver cpufreq_amd64_driver = {
  * simply keep the boost-disable flag in sync with the current global
  * state.
  */
-static int __cpuinit cpb_notify(struct notifier_block *nb, unsigned long action,
-                               void *hcpu)
+static int cpb_notify(struct notifier_block *nb, unsigned long action,
+                     void *hcpu)
 {
        unsigned cpu = (long)hcpu;
        u32 lo, hi;
@@ -1528,7 +1528,7 @@ static int __cpuinit cpb_notify(struct notifier_block *nb, unsigned long action,
        return NOTIFY_OK;
 }
 
-static struct notifier_block __cpuinitdata cpb_nb = {
+static struct notifier_block cpb_nb = {
        .notifier_call          = cpb_notify,
 };
 
index 4ac6d48..bb34b03 100644 (file)
@@ -7,3 +7,5 @@ obj-$(CONFIG_X86_MCE_THRESHOLD) += threshold.o
 obj-$(CONFIG_X86_MCE_INJECT)   += mce-inject.o
 
 obj-$(CONFIG_X86_THERMAL_VECTOR) += therm_throt.o
+
+obj-$(CONFIG_ACPI_APEI)                += mce-apei.o
diff --git a/arch/x86/kernel/cpu/mcheck/mce-apei.c b/arch/x86/kernel/cpu/mcheck/mce-apei.c
new file mode 100644 (file)
index 0000000..745b54f
--- /dev/null
@@ -0,0 +1,138 @@
+/*
+ * Bridge between MCE and APEI
+ *
+ * On some machine, corrected memory errors are reported via APEI
+ * generic hardware error source (GHES) instead of corrected Machine
+ * Check. These corrected memory errors can be reported to user space
+ * through /dev/mcelog via faking a corrected Machine Check, so that
+ * the error memory page can be offlined by /sbin/mcelog if the error
+ * count for one page is beyond the threshold.
+ *
+ * For fatal MCE, save MCE record into persistent storage via ERST, so
+ * that the MCE record can be logged after reboot via ERST.
+ *
+ * Copyright 2010 Intel Corp.
+ *   Author: Huang Ying <ying.huang@intel.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License version
+ * 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <linux/kernel.h>
+#include <linux/acpi.h>
+#include <linux/cper.h>
+#include <acpi/apei.h>
+#include <asm/mce.h>
+
+#include "mce-internal.h"
+
+void apei_mce_report_mem_error(int corrected, struct cper_sec_mem_err *mem_err)
+{
+       struct mce m;
+
+       /* Only corrected MC is reported */
+       if (!corrected)
+               return;
+
+       mce_setup(&m);
+       m.bank = 1;
+       /* Fake a memory read corrected error with unknown channel */
+       m.status = MCI_STATUS_VAL | MCI_STATUS_EN | MCI_STATUS_ADDRV | 0x9f;
+       m.addr = mem_err->physical_addr;
+       mce_log(&m);
+       mce_notify_irq();
+}
+EXPORT_SYMBOL_GPL(apei_mce_report_mem_error);
+
+#define CPER_CREATOR_MCE                                               \
+       UUID_LE(0x75a574e3, 0x5052, 0x4b29, 0x8a, 0x8e, 0xbe, 0x2c,     \
+               0x64, 0x90, 0xb8, 0x9d)
+#define CPER_SECTION_TYPE_MCE                                          \
+       UUID_LE(0xfe08ffbe, 0x95e4, 0x4be7, 0xbc, 0x73, 0x40, 0x96,     \
+               0x04, 0x4a, 0x38, 0xfc)
+
+/*
+ * CPER specification (in UEFI specification 2.3 appendix N) requires
+ * byte-packed.
+ */
+struct cper_mce_record {
+       struct cper_record_header hdr;
+       struct cper_section_descriptor sec_hdr;
+       struct mce mce;
+} __packed;
+
+int apei_write_mce(struct mce *m)
+{
+       struct cper_mce_record rcd;
+
+       memset(&rcd, 0, sizeof(rcd));
+       memcpy(rcd.hdr.signature, CPER_SIG_RECORD, CPER_SIG_SIZE);
+       rcd.hdr.revision = CPER_RECORD_REV;
+       rcd.hdr.signature_end = CPER_SIG_END;
+       rcd.hdr.section_count = 1;
+       rcd.hdr.error_severity = CPER_SER_FATAL;
+       /* timestamp, platform_id, partition_id are all invalid */
+       rcd.hdr.validation_bits = 0;
+       rcd.hdr.record_length = sizeof(rcd);
+       rcd.hdr.creator_id = CPER_CREATOR_MCE;
+       rcd.hdr.notification_type = CPER_NOTIFY_MCE;
+       rcd.hdr.record_id = cper_next_record_id();
+       rcd.hdr.flags = CPER_HW_ERROR_FLAGS_PREVERR;
+
+       rcd.sec_hdr.section_offset = (void *)&rcd.mce - (void *)&rcd;
+       rcd.sec_hdr.section_length = sizeof(rcd.mce);
+       rcd.sec_hdr.revision = CPER_SEC_REV;
+       /* fru_id and fru_text is invalid */
+       rcd.sec_hdr.validation_bits = 0;
+       rcd.sec_hdr.flags = CPER_SEC_PRIMARY;
+       rcd.sec_hdr.section_type = CPER_SECTION_TYPE_MCE;
+       rcd.sec_hdr.section_severity = CPER_SER_FATAL;
+
+       memcpy(&rcd.mce, m, sizeof(*m));
+
+       return erst_write(&rcd.hdr);
+}
+
+ssize_t apei_read_mce(struct mce *m, u64 *record_id)
+{
+       struct cper_mce_record rcd;
+       ssize_t len;
+
+       len = erst_read_next(&rcd.hdr, sizeof(rcd));
+       if (len <= 0)
+               return len;
+       /* Can not skip other records in storage via ERST unless clear them */
+       else if (len != sizeof(rcd) ||
+                uuid_le_cmp(rcd.hdr.creator_id, CPER_CREATOR_MCE)) {
+               if (printk_ratelimit())
+                       pr_warning(
+                       "MCE-APEI: Can not skip the unknown record in ERST");
+               return -EIO;
+       }
+
+       memcpy(m, &rcd.mce, sizeof(*m));
+       *record_id = rcd.hdr.record_id;
+
+       return sizeof(*m);
+}
+
+/* Check whether there is record in ERST */
+int apei_check_mce(void)
+{
+       return erst_get_record_count();
+}
+
+int apei_clear_mce(u64 record_id)
+{
+       return erst_clear(record_id);
+}
index 32996f9..fefcc69 100644 (file)
@@ -28,3 +28,26 @@ extern int mce_ser;
 
 extern struct mce_bank *mce_banks;
 
+#ifdef CONFIG_ACPI_APEI
+int apei_write_mce(struct mce *m);
+ssize_t apei_read_mce(struct mce *m, u64 *record_id);
+int apei_check_mce(void);
+int apei_clear_mce(u64 record_id);
+#else
+static inline int apei_write_mce(struct mce *m)
+{
+       return -EINVAL;
+}
+static inline ssize_t apei_read_mce(struct mce *m, u64 *record_id)
+{
+       return 0;
+}
+static inline int apei_check_mce(void)
+{
+       return 0;
+}
+static inline int apei_clear_mce(u64 record_id)
+{
+       return -EINVAL;
+}
+#endif
index 7a355dd..707165d 100644 (file)
@@ -264,7 +264,7 @@ static void wait_for_panic(void)
 
 static void mce_panic(char *msg, struct mce *final, char *exp)
 {
-       int i;
+       int i, apei_err = 0;
 
        if (!fake_panic) {
                /*
@@ -287,8 +287,11 @@ static void mce_panic(char *msg, struct mce *final, char *exp)
                struct mce *m = &mcelog.entry[i];
                if (!(m->status & MCI_STATUS_VAL))
                        continue;
-               if (!(m->status & MCI_STATUS_UC))
+               if (!(m->status & MCI_STATUS_UC)) {
                        print_mce(m);
+                       if (!apei_err)
+                               apei_err = apei_write_mce(m);
+               }
        }
        /* Now print uncorrected but with the final one last */
        for (i = 0; i < MCE_LOG_LEN; i++) {
@@ -297,11 +300,17 @@ static void mce_panic(char *msg, struct mce *final, char *exp)
                        continue;
                if (!(m->status & MCI_STATUS_UC))
                        continue;
-               if (!final || memcmp(m, final, sizeof(struct mce)))
+               if (!final || memcmp(m, final, sizeof(struct mce))) {
                        print_mce(m);
+                       if (!apei_err)
+                               apei_err = apei_write_mce(m);
+               }
        }
-       if (final)
+       if (final) {
                print_mce(final);
+               if (!apei_err)
+                       apei_err = apei_write_mce(final);
+       }
        if (cpu_missing)
                printk(KERN_EMERG "Some CPUs didn't answer in synchronization\n");
        print_mce_tail();
@@ -1493,6 +1502,43 @@ static void collect_tscs(void *data)
        rdtscll(cpu_tsc[smp_processor_id()]);
 }
 
+static int mce_apei_read_done;
+
+/* Collect MCE record of previous boot in persistent storage via APEI ERST. */
+static int __mce_read_apei(char __user **ubuf, size_t usize)
+{
+       int rc;
+       u64 record_id;
+       struct mce m;
+
+       if (usize < sizeof(struct mce))
+               return -EINVAL;
+
+       rc = apei_read_mce(&m, &record_id);
+       /* Error or no more MCE record */
+       if (rc <= 0) {
+               mce_apei_read_done = 1;
+               return rc;
+       }
+       rc = -EFAULT;
+       if (copy_to_user(*ubuf, &m, sizeof(struct mce)))
+               return rc;
+       /*
+        * In fact, we should have cleared the record after that has
+        * been flushed to the disk or sent to network in
+        * /sbin/mcelog, but we have no interface to support that now,
+        * so just clear it to avoid duplication.
+        */
+       rc = apei_clear_mce(record_id);
+       if (rc) {
+               mce_apei_read_done = 1;
+               return rc;
+       }
+       *ubuf += sizeof(struct mce);
+
+       return 0;
+}
+
 static ssize_t mce_read(struct file *filp, char __user *ubuf, size_t usize,
                        loff_t *off)
 {
@@ -1506,15 +1552,19 @@ static ssize_t mce_read(struct file *filp, char __user *ubuf, size_t usize,
                return -ENOMEM;
 
        mutex_lock(&mce_read_mutex);
+
+       if (!mce_apei_read_done) {
+               err = __mce_read_apei(&buf, usize);
+               if (err || buf != ubuf)
+                       goto out;
+       }
+
        next = rcu_dereference_check_mce(mcelog.next);
 
        /* Only supports full reads right now */
-       if (*off != 0 || usize < MCE_LOG_LEN*sizeof(struct mce)) {
-               mutex_unlock(&mce_read_mutex);
-               kfree(cpu_tsc);
-
-               return -EINVAL;
-       }
+       err = -EINVAL;
+       if (*off != 0 || usize < MCE_LOG_LEN*sizeof(struct mce))
+               goto out;
 
        err = 0;
        prev = 0;
@@ -1562,10 +1612,15 @@ timeout:
                        memset(&mcelog.entry[i], 0, sizeof(struct mce));
                }
        }
+
+       if (err)
+               err = -EFAULT;
+
+out:
        mutex_unlock(&mce_read_mutex);
        kfree(cpu_tsc);
 
-       return err ? -EFAULT : buf - ubuf;
+       return err ? err : buf - ubuf;
 }
 
 static unsigned int mce_poll(struct file *file, poll_table *wait)
@@ -1573,6 +1628,8 @@ static unsigned int mce_poll(struct file *file, poll_table *wait)
        poll_wait(file, &mce_wait, wait);
        if (rcu_dereference_check_mce(mcelog.next))
                return POLLIN | POLLRDNORM;
+       if (!mce_apei_read_done && apei_check_mce())
+               return POLLIN | POLLRDNORM;
        return 0;
 }
 
index fd4db0d..c775860 100644 (file)
@@ -1717,7 +1717,11 @@ void perf_arch_fetch_caller_regs(struct pt_regs *regs, unsigned long ip, int ski
         */
        regs->bp = rewind_frame_pointer(skip + 1);
        regs->cs = __KERNEL_CS;
-       local_save_flags(regs->flags);
+       /*
+        * We abuse bit 3 to pass exact information, see perf_misc_flags
+        * and the comment with PERF_EFLAGS_EXACT.
+        */
+       regs->flags = 0;
 }
 
 unsigned long perf_instruction_pointer(struct pt_regs *regs)
index 424fc8d..ae85d69 100644 (file)
@@ -465,15 +465,21 @@ out:
        return rc;
 }
 
-static inline void p4_pmu_clear_cccr_ovf(struct hw_perf_event *hwc)
+static inline int p4_pmu_clear_cccr_ovf(struct hw_perf_event *hwc)
 {
-       unsigned long dummy;
+       int overflow = 0;
+       u32 low, high;
 
-       rdmsrl(hwc->config_base + hwc->idx, dummy);
-       if (dummy & P4_CCCR_OVF) {
+       rdmsr(hwc->config_base + hwc->idx, low, high);
+
+       /* we need to check high bit for unflagged overflows */
+       if ((low & P4_CCCR_OVF) || !(high & (1 << 31))) {
+               overflow = 1;
                (void)checking_wrmsrl(hwc->config_base + hwc->idx,
-                       ((u64)dummy) & ~P4_CCCR_OVF);
+                       ((u64)low) & ~P4_CCCR_OVF);
        }
+
+       return overflow;
 }
 
 static inline void p4_pmu_disable_event(struct perf_event *event)
@@ -584,21 +590,15 @@ static int p4_pmu_handle_irq(struct pt_regs *regs)
 
                WARN_ON_ONCE(hwc->idx != idx);
 
-               /*
-                * FIXME: Redundant call, actually not needed
-                * but just to check if we're screwed
-                */
-               p4_pmu_clear_cccr_ovf(hwc);
+               /* it might be unflagged overflow */
+               handled = p4_pmu_clear_cccr_ovf(hwc);
 
                val = x86_perf_event_update(event);
-               if (val & (1ULL << (x86_pmu.cntval_bits - 1)))
+               if (!handled && (val & (1ULL << (x86_pmu.cntval_bits - 1))))
                        continue;
 
-               /*
-                * event overflow
-                */
-               handled         = 1;
-               data.period     = event->hw.last_period;
+               /* event overflow for sure */
+               data.period = event->hw.last_period;
 
                if (!x86_perf_event_set_period(event))
                        continue;
@@ -670,7 +670,7 @@ static void p4_pmu_swap_config_ts(struct hw_perf_event *hwc, int cpu)
 
 /*
  * ESCR address hashing is tricky, ESCRs are not sequential
- * in memory but all starts from MSR_P4_BSU_ESCR0 (0x03e0) and
+ * in memory but all starts from MSR_P4_BSU_ESCR0 (0x03a0) and
  * the metric between any ESCRs is laid in range [0xa0,0xe1]
  *
  * so we make ~70% filled hashtable
@@ -735,8 +735,9 @@ static int p4_get_escr_idx(unsigned int addr)
 {
        unsigned int idx = P4_ESCR_MSR_IDX(addr);
 
-       if (unlikely(idx >= P4_ESCR_MSR_TABLE_SIZE ||
-                       !p4_escr_table[idx])) {
+       if (unlikely(idx >= P4_ESCR_MSR_TABLE_SIZE      ||
+                       !p4_escr_table[idx]             ||
+                       p4_escr_table[idx] != addr)) {
                WARN_ONCE(1, "P4 PMU: Wrong address passed: %x\n", addr);
                return -1;
        }
@@ -762,7 +763,7 @@ static int p4_pmu_schedule_events(struct cpu_hw_events *cpuc, int n, int *assign
 {
        unsigned long used_mask[BITS_TO_LONGS(X86_PMC_IDX_MAX)];
        unsigned long escr_mask[BITS_TO_LONGS(P4_ESCR_MSR_TABLE_SIZE)];
-       int cpu = raw_smp_processor_id();
+       int cpu = smp_processor_id();
        struct hw_perf_event *hwc;
        struct p4_event_bind *bind;
        unsigned int i, thread, num;
index e802989..b4ae4ac 100644 (file)
@@ -676,6 +676,17 @@ static struct dmi_system_id __initdata bad_bios_dmi_table[] = {
                        DMI_MATCH(DMI_BOARD_NAME, "DG45FC"),
                },
        },
+       /*
+        * The Dell Inspiron Mini 1012 has DMI_BIOS_VENDOR = "Dell Inc.", so
+        * match on the product name.
+        */
+       {
+               .callback = dmi_low_memory_corruption,
+               .ident = "Phoenix BIOS",
+               .matches = {
+                       DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 1012"),
+               },
+       },
 #endif
        {}
 };
index 763d815..37462f1 100644 (file)
@@ -1215,9 +1215,17 @@ __init void prefill_possible_map(void)
        if (!num_processors)
                num_processors = 1;
 
-       if (setup_possible_cpus == -1)
-               possible = num_processors + disabled_cpus;
-       else
+       i = setup_max_cpus ?: 1;
+       if (setup_possible_cpus == -1) {
+               possible = num_processors;
+#ifdef CONFIG_HOTPLUG_CPU
+               if (setup_max_cpus)
+                       possible += disabled_cpus;
+#else
+               if (possible > i)
+                       possible = i;
+#endif
+       } else
                possible = setup_possible_cpus;
 
        total_cpus = max_t(int, possible, num_processors + disabled_cpus);
@@ -1230,11 +1238,23 @@ __init void prefill_possible_map(void)
                possible = nr_cpu_ids;
        }
 
+#ifdef CONFIG_HOTPLUG_CPU
+       if (!setup_max_cpus)
+#endif
+       if (possible > i) {
+               printk(KERN_WARNING
+                       "%d Processors exceeds max_cpus limit of %u\n",
+                       possible, setup_max_cpus);
+               possible = i;
+       }
+
        printk(KERN_INFO "SMP: Allowing %d CPUs, %d hotplug CPUs\n",
                possible, max_t(int, possible - num_processors, 0));
 
        for (i = 0; i < possible; i++)
                set_cpu_possible(i, true);
+       for (; i < NR_CPUS; i++)
+               set_cpu_possible(i, false);
 
        nr_cpu_ids = possible;
 }
index 2bdf628..9257510 100644 (file)
@@ -1390,7 +1390,6 @@ __init void lguest_init(void)
 #endif
 #ifdef CONFIG_ACPI
        acpi_disabled = 1;
-       acpi_ht = 0;
 #endif
 
        /*
index 10c27bb..550df48 100644 (file)
@@ -2,7 +2,6 @@
 #include <linux/topology.h>
 #include <linux/module.h>
 #include <linux/bootmem.h>
-#include <linux/random.h>
 
 #ifdef CONFIG_DEBUG_PER_CPU_MAPS
 # define DBG(x...) printk(KERN_DEBUG x)
@@ -66,19 +65,3 @@ const struct cpumask *cpumask_of_node(int node)
 }
 EXPORT_SYMBOL(cpumask_of_node);
 #endif
-
-/*
- * Return the bit number of a random bit set in the nodemask.
- *   (returns -1 if nodemask is empty)
- */
-int __node_random(const nodemask_t *maskp)
-{
-       int w, bit = -1;
-
-       w = nodes_weight(*maskp);
-       if (w)
-               bit = bitmap_ord_to_pos(maskp->bits,
-                       get_random_int() % w, MAX_NUMNODES);
-       return bit;
-}
-EXPORT_SYMBOL(__node_random);
index bbe5502..acc15b2 100644 (file)
@@ -336,6 +336,7 @@ int free_memtype(u64 start, u64 end)
 {
        int err = -EINVAL;
        int is_range_ram;
+       struct memtype *entry;
 
        if (!pat_enabled)
                return 0;
@@ -355,17 +356,20 @@ int free_memtype(u64 start, u64 end)
        }
 
        spin_lock(&memtype_lock);
-       err = rbt_memtype_erase(start, end);
+       entry = rbt_memtype_erase(start, end);
        spin_unlock(&memtype_lock);
 
-       if (err) {
+       if (!entry) {
                printk(KERN_INFO "%s:%d freeing invalid memtype %Lx-%Lx\n",
                        current->comm, current->pid, start, end);
+               return -EINVAL;
        }
 
+       kfree(entry);
+
        dprintk("free_memtype request 0x%Lx-0x%Lx\n", start, end);
 
-       return err;
+       return 0;
 }
 
 
index 4f39eef..77e5ba1 100644 (file)
@@ -28,15 +28,15 @@ static inline char *cattr_name(unsigned long flags)
 #ifdef CONFIG_X86_PAT
 extern int rbt_memtype_check_insert(struct memtype *new,
                                        unsigned long *new_type);
-extern int rbt_memtype_erase(u64 start, u64 end);
+extern struct memtype *rbt_memtype_erase(u64 start, u64 end);
 extern struct memtype *rbt_memtype_lookup(u64 addr);
 extern int rbt_memtype_copy_nth_element(struct memtype *out, loff_t pos);
 #else
 static inline int rbt_memtype_check_insert(struct memtype *new,
                                        unsigned long *new_type)
 { return 0; }
-static inline int rbt_memtype_erase(u64 start, u64 end)
-{ return 0; }
+static inline struct memtype *rbt_memtype_erase(u64 start, u64 end)
+{ return NULL; }
 static inline struct memtype *rbt_memtype_lookup(u64 addr)
 { return NULL; }
 static inline int rbt_memtype_copy_nth_element(struct memtype *out, loff_t pos)
index 07de4cb..f537087 100644 (file)
@@ -231,16 +231,17 @@ int rbt_memtype_check_insert(struct memtype *new, unsigned long *ret_type)
        return err;
 }
 
-int rbt_memtype_erase(u64 start, u64 end)
+struct memtype *rbt_memtype_erase(u64 start, u64 end)
 {
        struct memtype *data;
 
        data = memtype_rb_exact_match(&memtype_rbroot, start, end);
        if (!data)
-               return -EINVAL;
+               goto out;
 
        rb_erase(&data->rb, &memtype_rbroot);
-       return 0;
+out:
+       return data;
 }
 
 struct memtype *rbt_memtype_lookup(u64 addr)
index df3d5c8..308e325 100644 (file)
@@ -34,7 +34,7 @@
 /* IA32 Manual 3, 2-1 */
 static unsigned char prefix_codes[] = {
        0xF0, 0xF2, 0xF3, 0x2E, 0x36, 0x3E, 0x26, 0x64,
-       0x65, 0x2E, 0x3E, 0x66, 0x67
+       0x65, 0x66, 0x67
 };
 /* IA32 Manual 3, 3-432*/
 static unsigned int reg_rop[] = {
index 7928540..cac7184 100644 (file)
@@ -9,7 +9,6 @@
 #include <linux/pagemap.h>
 #include <linux/spinlock.h>
 #include <linux/module.h>
-#include <linux/quicklist.h>
 
 #include <asm/system.h>
 #include <asm/pgtable.h>
index 31930fd..2ec04c4 100644 (file)
@@ -207,10 +207,9 @@ get_current_resources(struct acpi_device *device, int busnum,
        if (!info.res)
                goto res_alloc_fail;
 
-       info.name = kmalloc(16, GFP_KERNEL);
+       info.name = kasprintf(GFP_KERNEL, "PCI Bus %04x:%02x", domain, busnum);
        if (!info.name)
                goto name_alloc_fail;
-       sprintf(info.name, "PCI Bus %04x:%02x", domain, busnum);
 
        info.res_num = 0;
        acpi_walk_resources(device->handle, METHOD_NAME__CRS, setup_resource,
@@ -224,8 +223,11 @@ res_alloc_fail:
        return;
 }
 
-struct pci_bus * __devinit pci_acpi_scan_root(struct acpi_device *device, int domain, int busnum)
+struct pci_bus * __devinit pci_acpi_scan_root(struct acpi_pci_root *root)
 {
+       struct acpi_device *device = root->device;
+       int domain = root->segment;
+       int busnum = root->secondary.start;
        struct pci_bus *bus;
        struct pci_sysdata *sd;
        int node;
index f42a030..91874e0 100644 (file)
@@ -10,6 +10,7 @@ obj-$(CONFIG_PCI)             += pci/
 obj-$(CONFIG_PARISC)           += parisc/
 obj-$(CONFIG_RAPIDIO)          += rapidio/
 obj-y                          += video/
+obj-y                          += idle/
 obj-$(CONFIG_ACPI)             += acpi/
 obj-$(CONFIG_SFI)              += sfi/
 # PnP must come after ACPI since it will eventually need to check if acpi
@@ -91,7 +92,6 @@ obj-$(CONFIG_EISA)            += eisa/
 obj-y                          += lguest/
 obj-$(CONFIG_CPU_FREQ)         += cpufreq/
 obj-$(CONFIG_CPU_IDLE)         += cpuidle/
-obj-y                          += idle/
 obj-$(CONFIG_MMC)              += mmc/
 obj-$(CONFIG_MEMSTICK)         += memstick/
 obj-$(CONFIG_NEW_LEDS)         += leds/
index 93d2c79..7464115 100644 (file)
@@ -360,4 +360,13 @@ config ACPI_SBS
          To compile this driver as a module, choose M here:
          the modules will be called sbs and sbshc.
 
+config ACPI_HED
+       tristate "Hardware Error Device"
+       help
+         This driver supports the Hardware Error Device (PNP0C33),
+         which is used to report some hardware errors notified via
+         SCI, mainly the corrected errors.
+
+source "drivers/acpi/apei/Kconfig"
+
 endif  # ACPI
index a8d8998..6ee3316 100644 (file)
@@ -19,7 +19,7 @@ obj-y                         += acpi.o \
 
 # All the builtin files are in the "acpi." module_param namespace.
 acpi-y                         += osl.o utils.o reboot.o
-acpi-y                         += hest.o
+acpi-y                         += atomicio.o
 
 # sleep related files
 acpi-y                         += wakeup.o
@@ -59,6 +59,7 @@ obj-$(CONFIG_ACPI_BATTERY)    += battery.o
 obj-$(CONFIG_ACPI_SBS)         += sbshc.o
 obj-$(CONFIG_ACPI_SBS)         += sbs.o
 obj-$(CONFIG_ACPI_POWER_METER) += power_meter.o
+obj-$(CONFIG_ACPI_HED)         += hed.o
 
 # processor has its own "processor." module_param namespace
 processor-y                    := processor_driver.o processor_throttling.o
@@ -66,3 +67,5 @@ processor-y                   += processor_idle.o processor_thermal.o
 processor-$(CONFIG_CPU_FREQ)   += processor_perflib.o
 
 obj-$(CONFIG_ACPI_PROCESSOR_AGGREGATOR) += acpi_pad.o
+
+obj-$(CONFIG_ACPI_APEI)                += apei/
index 6212213..d269a8f 100644 (file)
@@ -43,6 +43,10 @@ static DEFINE_MUTEX(isolated_cpus_lock);
 #define CPUID5_ECX_EXTENSIONS_SUPPORTED (0x1)
 #define CPUID5_ECX_INTERRUPT_BREAK     (0x2)
 static unsigned long power_saving_mwait_eax;
+
+static unsigned char tsc_detected_unstable;
+static unsigned char tsc_marked_unstable;
+
 static void power_saving_mwait_init(void)
 {
        unsigned int eax, ebx, ecx, edx;
@@ -87,8 +91,8 @@ static void power_saving_mwait_init(void)
 
                /*FALL THROUGH*/
        default:
-               /* TSC could halt in idle, so notify users */
-               mark_tsc_unstable("TSC halts in idle");
+               /* TSC could halt in idle */
+               tsc_detected_unstable = 1;
        }
 #endif
 }
@@ -168,16 +172,14 @@ static int power_saving_thread(void *data)
 
                do_sleep = 0;
 
-               current_thread_info()->status &= ~TS_POLLING;
-               /*
-                * TS_POLLING-cleared state must be visible before we test
-                * NEED_RESCHED:
-                */
-               smp_mb();
-
                expire_time = jiffies + HZ * (100 - idle_pct) / 100;
 
                while (!need_resched()) {
+                       if (tsc_detected_unstable && !tsc_marked_unstable) {
+                               /* TSC could halt in idle, so notify users */
+                               mark_tsc_unstable("TSC halts in idle");
+                               tsc_marked_unstable = 1;
+                       }
                        local_irq_disable();
                        cpu = smp_processor_id();
                        clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_ENTER,
@@ -200,8 +202,6 @@ static int power_saving_thread(void *data)
                        }
                }
 
-               current_thread_info()->status |= TS_POLLING;
-
                /*
                 * current sched_rt has threshold for rt task running time.
                 * When a rt task uses 95% CPU time, the rt thread will be
index 7c7bbb4..d5a5efc 100644 (file)
@@ -69,7 +69,7 @@ acpi_ev_get_gpe_device(struct acpi_gpe_xrupt_info *gpe_xrupt_info,
 
 acpi_status acpi_enable(void)
 {
-       acpi_status status = AE_OK;
+       acpi_status status;
 
        ACPI_FUNCTION_TRACE(acpi_enable);
 
@@ -84,21 +84,30 @@ acpi_status acpi_enable(void)
        if (acpi_hw_get_mode() == ACPI_SYS_MODE_ACPI) {
                ACPI_DEBUG_PRINT((ACPI_DB_INIT,
                                  "System is already in ACPI mode\n"));
-       } else {
-               /* Transition to ACPI mode */
+               return_ACPI_STATUS(AE_OK);
+       }
 
-               status = acpi_hw_set_mode(ACPI_SYS_MODE_ACPI);
-               if (ACPI_FAILURE(status)) {
-                       ACPI_ERROR((AE_INFO,
-                                   "Could not transition to ACPI mode"));
-                       return_ACPI_STATUS(status);
-               }
+       /* Transition to ACPI mode */
 
-               ACPI_DEBUG_PRINT((ACPI_DB_INIT,
-                                 "Transition to ACPI mode successful\n"));
+       status = acpi_hw_set_mode(ACPI_SYS_MODE_ACPI);
+       if (ACPI_FAILURE(status)) {
+               ACPI_ERROR((AE_INFO,
+                           "Could not transition to ACPI mode"));
+               return_ACPI_STATUS(status);
        }
 
-       return_ACPI_STATUS(status);
+       /* Sanity check that transition succeeded */
+
+       if (acpi_hw_get_mode() != ACPI_SYS_MODE_ACPI) {
+               ACPI_ERROR((AE_INFO,
+                           "Hardware did not enter ACPI mode"));
+               return_ACPI_STATUS(AE_NO_HARDWARE_RESPONSE);
+       }
+
+       ACPI_DEBUG_PRINT((ACPI_DB_INIT,
+                         "Transition to ACPI mode successful\n"));
+
+       return_ACPI_STATUS(AE_OK);
 }
 
 ACPI_EXPORT_SYMBOL(acpi_enable)
index 679a112..b44274a 100644 (file)
@@ -63,7 +63,6 @@ acpi_status acpi_hw_set_mode(u32 mode)
 {
 
        acpi_status status;
-       u32 retry;
 
        ACPI_FUNCTION_TRACE(hw_set_mode);
 
@@ -125,24 +124,7 @@ acpi_status acpi_hw_set_mode(u32 mode)
                return_ACPI_STATUS(status);
        }
 
-       /*
-        * Some hardware takes a LONG time to switch modes. Give them 3 sec to
-        * do so, but allow faster systems to proceed more quickly.
-        */
-       retry = 3000;
-       while (retry) {
-               if (acpi_hw_get_mode() == mode) {
-                       ACPI_DEBUG_PRINT((ACPI_DB_INFO,
-                                         "Mode %X successfully enabled\n",
-                                         mode));
-                       return_ACPI_STATUS(AE_OK);
-               }
-               acpi_os_stall(1000);
-               retry--;
-       }
-
-       ACPI_ERROR((AE_INFO, "Hardware did not change modes"));
-       return_ACPI_STATUS(AE_NO_HARDWARE_RESPONSE);
+       return_ACPI_STATUS(AE_OK);
 }
 
 /*******************************************************************************
diff --git a/drivers/acpi/apei/Kconfig b/drivers/acpi/apei/Kconfig
new file mode 100644 (file)
index 0000000..f8c668f
--- /dev/null
@@ -0,0 +1,30 @@
+config ACPI_APEI
+       bool "ACPI Platform Error Interface (APEI)"
+       depends on X86
+       help
+         APEI allows to report errors (for example from the chipset)
+         to the operating system. This improves NMI handling
+         especially. In addition it supports error serialization and
+         error injection.
+
+config ACPI_APEI_GHES
+       tristate "APEI Generic Hardware Error Source"
+       depends on ACPI_APEI && X86
+       select ACPI_HED
+       help
+         Generic Hardware Error Source provides a way to report
+         platform hardware errors (such as that from chipset). It
+         works in so called "Firmware First" mode, that is, hardware
+         errors are reported to firmware firstly, then reported to
+         Linux by firmware. This way, some non-standard hardware
+         error registers or non-standard hardware link can be checked
+         by firmware to produce more valuable hardware error
+         information for Linux.
+
+config ACPI_APEI_EINJ
+       tristate "APEI Error INJection (EINJ)"
+       depends on ACPI_APEI && DEBUG_FS
+       help
+         EINJ provides a hardware error injection mechanism, it is
+         mainly used for debugging and testing the other parts of
+         APEI and some other RAS features.
diff --git a/drivers/acpi/apei/Makefile b/drivers/acpi/apei/Makefile
new file mode 100644 (file)
index 0000000..b13b03a
--- /dev/null
@@ -0,0 +1,5 @@
+obj-$(CONFIG_ACPI_APEI)                += apei.o
+obj-$(CONFIG_ACPI_APEI_GHES)   += ghes.o
+obj-$(CONFIG_ACPI_APEI_EINJ)   += einj.o
+
+apei-y := apei-base.o hest.o cper.o erst.o
diff --git a/drivers/acpi/apei/apei-base.c b/drivers/acpi/apei/apei-base.c
new file mode 100644 (file)
index 0000000..db3946e
--- /dev/null
@@ -0,0 +1,593 @@
+/*
+ * apei-base.c - ACPI Platform Error Interface (APEI) supporting
+ * infrastructure
+ *
+ * APEI allows to report errors (for example from the chipset) to the
+ * the operating system. This improves NMI handling especially. In
+ * addition it supports error serialization and error injection.
+ *
+ * For more information about APEI, please refer to ACPI Specification
+ * version 4.0, chapter 17.
+ *
+ * This file has Common functions used by more than one APEI table,
+ * including framework of interpreter for ERST and EINJ; resource
+ * management for APEI registers.
+ *
+ * Copyright (C) 2009, Intel Corp.
+ *     Author: Huang Ying <ying.huang@intel.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License version
+ * 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/acpi.h>
+#include <linux/io.h>
+#include <linux/kref.h>
+#include <linux/rculist.h>
+#include <linux/interrupt.h>
+#include <linux/debugfs.h>
+#include <acpi/atomicio.h>
+
+#include "apei-internal.h"
+
+#define APEI_PFX "APEI: "
+
+/*
+ * APEI ERST (Error Record Serialization Table) and EINJ (Error
+ * INJection) interpreter framework.
+ */
+
+#define APEI_EXEC_PRESERVE_REGISTER    0x1
+
+void apei_exec_ctx_init(struct apei_exec_context *ctx,
+                       struct apei_exec_ins_type *ins_table,
+                       u32 instructions,
+                       struct acpi_whea_header *action_table,
+                       u32 entries)
+{
+       ctx->ins_table = ins_table;
+       ctx->instructions = instructions;
+       ctx->action_table = action_table;
+       ctx->entries = entries;
+}
+EXPORT_SYMBOL_GPL(apei_exec_ctx_init);
+
+int __apei_exec_read_register(struct acpi_whea_header *entry, u64 *val)
+{
+       int rc;
+
+       rc = acpi_atomic_read(val, &entry->register_region);
+       if (rc)
+               return rc;
+       *val >>= entry->register_region.bit_offset;
+       *val &= entry->mask;
+
+       return 0;
+}
+
+int apei_exec_read_register(struct apei_exec_context *ctx,
+                           struct acpi_whea_header *entry)
+{
+       int rc;
+       u64 val = 0;
+
+       rc = __apei_exec_read_register(entry, &val);
+       if (rc)
+               return rc;
+       ctx->value = val;
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(apei_exec_read_register);
+
+int apei_exec_read_register_value(struct apei_exec_context *ctx,
+                                 struct acpi_whea_header *entry)
+{
+       int rc;
+
+       rc = apei_exec_read_register(ctx, entry);
+       if (rc)
+               return rc;
+       ctx->value = (ctx->value == entry->value);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(apei_exec_read_register_value);
+
+int __apei_exec_write_register(struct acpi_whea_header *entry, u64 val)
+{
+       int rc;
+
+       val &= entry->mask;
+       val <<= entry->register_region.bit_offset;
+       if (entry->flags & APEI_EXEC_PRESERVE_REGISTER) {
+               u64 valr = 0;
+               rc = acpi_atomic_read(&valr, &entry->register_region);
+               if (rc)
+                       return rc;
+               valr &= ~(entry->mask << entry->register_region.bit_offset);
+               val |= valr;
+       }
+       rc = acpi_atomic_write(val, &entry->register_region);
+
+       return rc;
+}
+
+int apei_exec_write_register(struct apei_exec_context *ctx,
+                            struct acpi_whea_header *entry)
+{
+       return __apei_exec_write_register(entry, ctx->value);
+}
+EXPORT_SYMBOL_GPL(apei_exec_write_register);
+
+int apei_exec_write_register_value(struct apei_exec_context *ctx,
+                                  struct acpi_whea_header *entry)
+{
+       int rc;
+
+       ctx->value = entry->value;
+       rc = apei_exec_write_register(ctx, entry);
+
+       return rc;
+}
+EXPORT_SYMBOL_GPL(apei_exec_write_register_value);
+
+int apei_exec_noop(struct apei_exec_context *ctx,
+                  struct acpi_whea_header *entry)
+{
+       return 0;
+}
+EXPORT_SYMBOL_GPL(apei_exec_noop);
+
+/*
+ * Interpret the specified action. Go through whole action table,
+ * execute all instructions belong to the action.
+ */
+int apei_exec_run(struct apei_exec_context *ctx, u8 action)
+{
+       int rc;
+       u32 i, ip;
+       struct acpi_whea_header *entry;
+       apei_exec_ins_func_t run;
+
+       ctx->ip = 0;
+
+       /*
+        * "ip" is the instruction pointer of current instruction,
+        * "ctx->ip" specifies the next instruction to executed,
+        * instruction "run" function may change the "ctx->ip" to
+        * implement "goto" semantics.
+        */
+rewind:
+       ip = 0;
+       for (i = 0; i < ctx->entries; i++) {
+               entry = &ctx->action_table[i];
+               if (entry->action != action)
+                       continue;
+               if (ip == ctx->ip) {
+                       if (entry->instruction >= ctx->instructions ||
+                           !ctx->ins_table[entry->instruction].run) {
+                               pr_warning(FW_WARN APEI_PFX
+                       "Invalid action table, unknown instruction type: %d\n",
+                                          entry->instruction);
+                               return -EINVAL;
+                       }
+                       run = ctx->ins_table[entry->instruction].run;
+                       rc = run(ctx, entry);
+                       if (rc < 0)
+                               return rc;
+                       else if (rc != APEI_EXEC_SET_IP)
+                               ctx->ip++;
+               }
+               ip++;
+               if (ctx->ip < ip)
+                       goto rewind;
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(apei_exec_run);
+
+typedef int (*apei_exec_entry_func_t)(struct apei_exec_context *ctx,
+                                     struct acpi_whea_header *entry,
+                                     void *data);
+
+static int apei_exec_for_each_entry(struct apei_exec_context *ctx,
+                                   apei_exec_entry_func_t func,
+                                   void *data,
+                                   int *end)
+{
+       u8 ins;
+       int i, rc;
+       struct acpi_whea_header *entry;
+       struct apei_exec_ins_type *ins_table = ctx->ins_table;
+
+       for (i = 0; i < ctx->entries; i++) {
+               entry = ctx->action_table + i;
+               ins = entry->instruction;
+               if (end)
+                       *end = i;
+               if (ins >= ctx->instructions || !ins_table[ins].run) {
+                       pr_warning(FW_WARN APEI_PFX
+                       "Invalid action table, unknown instruction type: %d\n",
+                                  ins);
+                       return -EINVAL;
+               }
+               rc = func(ctx, entry, data);
+               if (rc)
+                       return rc;
+       }
+
+       return 0;
+}
+
+static int pre_map_gar_callback(struct apei_exec_context *ctx,
+                               struct acpi_whea_header *entry,
+                               void *data)
+{
+       u8 ins = entry->instruction;
+
+       if (ctx->ins_table[ins].flags & APEI_EXEC_INS_ACCESS_REGISTER)
+               return acpi_pre_map_gar(&entry->register_region);
+
+       return 0;
+}
+
+/*
+ * Pre-map all GARs in action table to make it possible to access them
+ * in NMI handler.
+ */
+int apei_exec_pre_map_gars(struct apei_exec_context *ctx)
+{
+       int rc, end;
+
+       rc = apei_exec_for_each_entry(ctx, pre_map_gar_callback,
+                                     NULL, &end);
+       if (rc) {
+               struct apei_exec_context ctx_unmap;
+               memcpy(&ctx_unmap, ctx, sizeof(*ctx));
+               ctx_unmap.entries = end;
+               apei_exec_post_unmap_gars(&ctx_unmap);
+       }
+
+       return rc;
+}
+EXPORT_SYMBOL_GPL(apei_exec_pre_map_gars);
+
+static int post_unmap_gar_callback(struct apei_exec_context *ctx,
+                                  struct acpi_whea_header *entry,
+                                  void *data)
+{
+       u8 ins = entry->instruction;
+
+       if (ctx->ins_table[ins].flags & APEI_EXEC_INS_ACCESS_REGISTER)
+               acpi_post_unmap_gar(&entry->register_region);
+
+       return 0;
+}
+
+/* Post-unmap all GAR in action table. */
+int apei_exec_post_unmap_gars(struct apei_exec_context *ctx)
+{
+       return apei_exec_for_each_entry(ctx, post_unmap_gar_callback,
+                                       NULL, NULL);
+}
+EXPORT_SYMBOL_GPL(apei_exec_post_unmap_gars);
+
+/*
+ * Resource management for GARs in APEI
+ */
+struct apei_res {
+       struct list_head list;
+       unsigned long start;
+       unsigned long end;
+};
+
+/* Collect all resources requested, to avoid conflict */
+struct apei_resources apei_resources_all = {
+       .iomem = LIST_HEAD_INIT(apei_resources_all.iomem),
+       .ioport = LIST_HEAD_INIT(apei_resources_all.ioport),
+};
+
+static int apei_res_add(struct list_head *res_list,
+                       unsigned long start, unsigned long size)
+{
+       struct apei_res *res, *resn, *res_ins = NULL;
+       unsigned long end = start + size;
+
+       if (end <= start)
+               return 0;
+repeat:
+       list_for_each_entry_safe(res, resn, res_list, list) {
+               if (res->start > end || res->end < start)
+                       continue;
+               else if (end <= res->end && start >= res->start) {
+                       kfree(res_ins);
+                       return 0;
+               }
+               list_del(&res->list);
+               res->start = start = min(res->start, start);
+               res->end = end = max(res->end, end);
+               kfree(res_ins);
+               res_ins = res;
+               goto repeat;
+       }
+
+       if (res_ins)
+               list_add(&res_ins->list, res_list);
+       else {
+               res_ins = kmalloc(sizeof(*res), GFP_KERNEL);
+               if (!res_ins)
+                       return -ENOMEM;
+               res_ins->start = start;
+               res_ins->end = end;
+               list_add(&res_ins->list, res_list);
+       }
+
+       return 0;
+}
+
+static int apei_res_sub(struct list_head *res_list1,
+                       struct list_head *res_list2)
+{
+       struct apei_res *res1, *resn1, *res2, *res;
+       res1 = list_entry(res_list1->next, struct apei_res, list);
+       resn1 = list_entry(res1->list.next, struct apei_res, list);
+       while (&res1->list != res_list1) {
+               list_for_each_entry(res2, res_list2, list) {
+                       if (res1->start >= res2->end ||
+                           res1->end <= res2->start)
+                               continue;
+                       else if (res1->end <= res2->end &&
+                                res1->start >= res2->start) {
+                               list_del(&res1->list);
+                               kfree(res1);
+                               break;
+                       } else if (res1->end > res2->end &&
+                                  res1->start < res2->start) {
+                               res = kmalloc(sizeof(*res), GFP_KERNEL);
+                               if (!res)
+                                       return -ENOMEM;
+                               res->start = res2->end;
+                               res->end = res1->end;
+                               res1->end = res2->start;
+                               list_add(&res->list, &res1->list);
+                               resn1 = res;
+                       } else {
+                               if (res1->start < res2->start)
+                                       res1->end = res2->start;
+                               else
+                                       res1->start = res2->end;
+                       }
+               }
+               res1 = resn1;
+               resn1 = list_entry(resn1->list.next, struct apei_res, list);
+       }
+
+       return 0;
+}
+
+static void apei_res_clean(struct list_head *res_list)
+{
+       struct apei_res *res, *resn;
+
+       list_for_each_entry_safe(res, resn, res_list, list) {
+               list_del(&res->list);
+               kfree(res);
+       }
+}
+
+void apei_resources_fini(struct apei_resources *resources)
+{
+       apei_res_clean(&resources->iomem);
+       apei_res_clean(&resources->ioport);
+}
+EXPORT_SYMBOL_GPL(apei_resources_fini);
+
+static int apei_resources_merge(struct apei_resources *resources1,
+                               struct apei_resources *resources2)
+{
+       int rc;
+       struct apei_res *res;
+
+       list_for_each_entry(res, &resources2->iomem, list) {
+               rc = apei_res_add(&resources1->iomem, res->start,
+                                 res->end - res->start);
+               if (rc)
+                       return rc;
+       }
+       list_for_each_entry(res, &resources2->ioport, list) {
+               rc = apei_res_add(&resources1->ioport, res->start,
+                                 res->end - res->start);
+               if (rc)
+                       return rc;
+       }
+
+       return 0;
+}
+
+/*
+ * EINJ has two groups of GARs (EINJ table entry and trigger table
+ * entry), so common resources are subtracted from the trigger table
+ * resources before the second requesting.
+ */
+int apei_resources_sub(struct apei_resources *resources1,
+                      struct apei_resources *resources2)
+{
+       int rc;
+
+       rc = apei_res_sub(&resources1->iomem, &resources2->iomem);
+       if (rc)
+               return rc;
+       return apei_res_sub(&resources1->ioport, &resources2->ioport);
+}
+EXPORT_SYMBOL_GPL(apei_resources_sub);
+
+/*
+ * IO memory/port rersource management mechanism is used to check
+ * whether memory/port area used by GARs conflicts with normal memory
+ * or IO memory/port of devices.
+ */
+int apei_resources_request(struct apei_resources *resources,
+                          const char *desc)
+{
+       struct apei_res *res, *res_bak;
+       struct resource *r;
+
+       apei_resources_sub(resources, &apei_resources_all);
+
+       list_for_each_entry(res, &resources->iomem, list) {
+               r = request_mem_region(res->start, res->end - res->start,
+                                      desc);
+               if (!r) {
+                       pr_err(APEI_PFX
+               "Can not request iomem region <%016llx-%016llx> for GARs.\n",
+                              (unsigned long long)res->start,
+                              (unsigned long long)res->end);
+                       res_bak = res;
+                       goto err_unmap_iomem;
+               }
+       }
+
+       list_for_each_entry(res, &resources->ioport, list) {
+               r = request_region(res->start, res->end - res->start, desc);
+               if (!r) {
+                       pr_err(APEI_PFX
+               "Can not request ioport region <%016llx-%016llx> for GARs.\n",
+                              (unsigned long long)res->start,
+                              (unsigned long long)res->end);
+                       res_bak = res;
+                       goto err_unmap_ioport;
+               }
+       }
+
+       apei_resources_merge(&apei_resources_all, resources);
+
+       return 0;
+err_unmap_ioport:
+       list_for_each_entry(res, &resources->ioport, list) {
+               if (res == res_bak)
+                       break;
+               release_mem_region(res->start, res->end - res->start);
+       }
+       res_bak = NULL;
+err_unmap_iomem:
+       list_for_each_entry(res, &resources->iomem, list) {
+               if (res == res_bak)
+                       break;
+               release_region(res->start, res->end - res->start);
+       }
+       return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(apei_resources_request);
+
+void apei_resources_release(struct apei_resources *resources)
+{
+       struct apei_res *res;
+
+       list_for_each_entry(res, &resources->iomem, list)
+               release_mem_region(res->start, res->end - res->start);
+       list_for_each_entry(res, &resources->ioport, list)
+               release_region(res->start, res->end - res->start);
+
+       apei_resources_sub(&apei_resources_all, resources);
+}
+EXPORT_SYMBOL_GPL(apei_resources_release);
+
+static int apei_check_gar(struct acpi_generic_address *reg, u64 *paddr)
+{
+       u32 width, space_id;
+
+       width = reg->bit_width;
+       space_id = reg->space_id;
+       /* Handle possible alignment issues */
+       memcpy(paddr, &reg->address, sizeof(*paddr));
+       if (!*paddr) {
+               pr_warning(FW_BUG APEI_PFX
+                          "Invalid physical address in GAR [0x%llx/%u/%u]\n",
+                          *paddr, width, space_id);
+               return -EINVAL;
+       }
+
+       if ((width != 8) && (width != 16) && (width != 32) && (width != 64)) {
+               pr_warning(FW_BUG APEI_PFX
+                          "Invalid bit width in GAR [0x%llx/%u/%u]\n",
+                          *paddr, width, space_id);
+               return -EINVAL;
+       }
+
+       if (space_id != ACPI_ADR_SPACE_SYSTEM_MEMORY &&
+           space_id != ACPI_ADR_SPACE_SYSTEM_IO) {
+               pr_warning(FW_BUG APEI_PFX
+                          "Invalid address space type in GAR [0x%llx/%u/%u]\n",
+                          *paddr, width, space_id);
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int collect_res_callback(struct apei_exec_context *ctx,
+                               struct acpi_whea_header *entry,
+                               void *data)
+{
+       struct apei_resources *resources = data;
+       struct acpi_generic_address *reg = &entry->register_region;
+       u8 ins = entry->instruction;
+       u64 paddr;
+       int rc;
+
+       if (!(ctx->ins_table[ins].flags & APEI_EXEC_INS_ACCESS_REGISTER))
+               return 0;
+
+       rc = apei_check_gar(reg, &paddr);
+       if (rc)
+               return rc;
+
+       switch (reg->space_id) {
+       case ACPI_ADR_SPACE_SYSTEM_MEMORY:
+               return apei_res_add(&resources->iomem, paddr,
+                                   reg->bit_width / 8);
+       case ACPI_ADR_SPACE_SYSTEM_IO:
+               return apei_res_add(&resources->ioport, paddr,
+                                   reg->bit_width / 8);
+       default:
+               return -EINVAL;
+       }
+}
+
+/*
+ * Same register may be used by multiple instructions in GARs, so
+ * resources are collected before requesting.
+ */
+int apei_exec_collect_resources(struct apei_exec_context *ctx,
+                               struct apei_resources *resources)
+{
+       return apei_exec_for_each_entry(ctx, collect_res_callback,
+                                       resources, NULL);
+}
+EXPORT_SYMBOL_GPL(apei_exec_collect_resources);
+
+struct dentry *apei_get_debugfs_dir(void)
+{
+       static struct dentry *dapei;
+
+       if (!dapei)
+               dapei = debugfs_create_dir("apei", NULL);
+
+       return dapei;
+}
+EXPORT_SYMBOL_GPL(apei_get_debugfs_dir);
diff --git a/drivers/acpi/apei/apei-internal.h b/drivers/acpi/apei/apei-internal.h
new file mode 100644 (file)
index 0000000..18df1e9
--- /dev/null
@@ -0,0 +1,114 @@
+/*
+ * apei-internal.h - ACPI Platform Error Interface internal
+ * definations.
+ */
+
+#ifndef APEI_INTERNAL_H
+#define APEI_INTERNAL_H
+
+#include <linux/cper.h>
+
+struct apei_exec_context;
+
+typedef int (*apei_exec_ins_func_t)(struct apei_exec_context *ctx,
+                                   struct acpi_whea_header *entry);
+
+#define APEI_EXEC_INS_ACCESS_REGISTER  0x0001
+
+struct apei_exec_ins_type {
+       u32 flags;
+       apei_exec_ins_func_t run;
+};
+
+struct apei_exec_context {
+       u32 ip;
+       u64 value;
+       u64 var1;
+       u64 var2;
+       u64 src_base;
+       u64 dst_base;
+       struct apei_exec_ins_type *ins_table;
+       u32 instructions;
+       struct acpi_whea_header *action_table;
+       u32 entries;
+};
+
+void apei_exec_ctx_init(struct apei_exec_context *ctx,
+                       struct apei_exec_ins_type *ins_table,
+                       u32 instructions,
+                       struct acpi_whea_header *action_table,
+                       u32 entries);
+
+static inline void apei_exec_ctx_set_input(struct apei_exec_context *ctx,
+                                          u64 input)
+{
+       ctx->value = input;
+}
+
+static inline u64 apei_exec_ctx_get_output(struct apei_exec_context *ctx)
+{
+       return ctx->value;
+}
+
+int apei_exec_run(struct apei_exec_context *ctx, u8 action);
+
+/* Common instruction implementation */
+
+/* IP has been set in instruction function */
+#define APEI_EXEC_SET_IP       1
+
+int __apei_exec_read_register(struct acpi_whea_header *entry, u64 *val);
+int __apei_exec_write_register(struct acpi_whea_header *entry, u64 val);
+int apei_exec_read_register(struct apei_exec_context *ctx,
+                           struct acpi_whea_header *entry);
+int apei_exec_read_register_value(struct apei_exec_context *ctx,
+                                 struct acpi_whea_header *entry);
+int apei_exec_write_register(struct apei_exec_context *ctx,
+                            struct acpi_whea_header *entry);
+int apei_exec_write_register_value(struct apei_exec_context *ctx,
+                                  struct acpi_whea_header *entry);
+int apei_exec_noop(struct apei_exec_context *ctx,
+                  struct acpi_whea_header *entry);
+int apei_exec_pre_map_gars(struct apei_exec_context *ctx);
+int apei_exec_post_unmap_gars(struct apei_exec_context *ctx);
+
+struct apei_resources {
+       struct list_head iomem;
+       struct list_head ioport;
+};
+
+static inline void apei_resources_init(struct apei_resources *resources)
+{
+       INIT_LIST_HEAD(&resources->iomem);
+       INIT_LIST_HEAD(&resources->ioport);
+}
+
+void apei_resources_fini(struct apei_resources *resources);
+int apei_resources_sub(struct apei_resources *resources1,
+                      struct apei_resources *resources2);
+int apei_resources_request(struct apei_resources *resources,
+                          const char *desc);
+void apei_resources_release(struct apei_resources *resources);
+int apei_exec_collect_resources(struct apei_exec_context *ctx,
+                               struct apei_resources *resources);
+
+struct dentry;
+struct dentry *apei_get_debugfs_dir(void);
+
+#define apei_estatus_for_each_section(estatus, section)                        \
+       for (section = (struct acpi_hest_generic_data *)(estatus + 1);  \
+            (void *)section - (void *)estatus < estatus->data_length;  \
+            section = (void *)(section+1) + section->error_data_length)
+
+static inline u32 apei_estatus_len(struct acpi_hest_generic_status *estatus)
+{
+       if (estatus->raw_data_length)
+               return estatus->raw_data_offset + \
+                       estatus->raw_data_length;
+       else
+               return sizeof(*estatus) + estatus->data_length;
+}
+
+int apei_estatus_check_header(const struct acpi_hest_generic_status *estatus);
+int apei_estatus_check(const struct acpi_hest_generic_status *estatus);
+#endif
diff --git a/drivers/acpi/apei/cper.c b/drivers/acpi/apei/cper.c
new file mode 100644 (file)
index 0000000..f4cf2fc
--- /dev/null
@@ -0,0 +1,84 @@
+/*
+ * UEFI Common Platform Error Record (CPER) support
+ *
+ * Copyright (C) 2010, Intel Corp.
+ *     Author: Huang Ying <ying.huang@intel.com>
+ *
+ * CPER is the format used to describe platform hardware error by
+ * various APEI tables, such as ERST, BERT and HEST etc.
+ *
+ * For more information about CPER, please refer to Appendix N of UEFI
+ * Specification version 2.3.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License version
+ * 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/time.h>
+#include <linux/cper.h>
+#include <linux/acpi.h>
+
+/*
+ * CPER record ID need to be unique even after reboot, because record
+ * ID is used as index for ERST storage, while CPER records from
+ * multiple boot may co-exist in ERST.
+ */
+u64 cper_next_record_id(void)
+{
+       static atomic64_t seq;
+
+       if (!atomic64_read(&seq))
+               atomic64_set(&seq, ((u64)get_seconds()) << 32);
+
+       return atomic64_inc_return(&seq);
+}
+EXPORT_SYMBOL_GPL(cper_next_record_id);
+
+int apei_estatus_check_header(const struct acpi_hest_generic_status *estatus)
+{
+       if (estatus->data_length &&
+           estatus->data_length < sizeof(struct acpi_hest_generic_data))
+               return -EINVAL;
+       if (estatus->raw_data_length &&
+           estatus->raw_data_offset < sizeof(*estatus) + estatus->data_length)
+               return -EINVAL;
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(apei_estatus_check_header);
+
+int apei_estatus_check(const struct acpi_hest_generic_status *estatus)
+{
+       struct acpi_hest_generic_data *gdata;
+       unsigned int data_len, gedata_len;
+       int rc;
+
+       rc = apei_estatus_check_header(estatus);
+       if (rc)
+               return rc;
+       data_len = estatus->data_length;
+       gdata = (struct acpi_hest_generic_data *)(estatus + 1);
+       while (data_len > sizeof(*gdata)) {
+               gedata_len = gdata->error_data_length;
+               if (gedata_len > data_len - sizeof(*gdata))
+                       return -EINVAL;
+               data_len -= gedata_len + sizeof(*gdata);
+       }
+       if (data_len)
+               return -EINVAL;
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(apei_estatus_check);
diff --git a/drivers/acpi/apei/einj.c b/drivers/acpi/apei/einj.c
new file mode 100644 (file)
index 0000000..465c885
--- /dev/null
@@ -0,0 +1,548 @@
+/*
+ * APEI Error INJection support
+ *
+ * EINJ provides a hardware error injection mechanism, this is useful
+ * for debugging and testing of other APEI and RAS features.
+ *
+ * For more information about EINJ, please refer to ACPI Specification
+ * version 4.0, section 17.5.
+ *
+ * Copyright 2009-2010 Intel Corp.
+ *   Author: Huang Ying <ying.huang@intel.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License version
+ * 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+#include <linux/nmi.h>
+#include <linux/delay.h>
+#include <acpi/acpi.h>
+
+#include "apei-internal.h"
+
+#define EINJ_PFX "EINJ: "
+
+#define SPIN_UNIT              100                     /* 100ns */
+/* Firmware should respond within 1 miliseconds */
+#define FIRMWARE_TIMEOUT       (1 * NSEC_PER_MSEC)
+
+/*
+ * Some BIOSes allow parameters to the SET_ERROR_TYPE entries in the
+ * EINJ table through an unpublished extension. Use with caution as
+ * most will ignore the parameter and make their own choice of address
+ * for error injection.
+ */
+struct einj_parameter {
+       u64 type;
+       u64 reserved1;
+       u64 reserved2;
+       u64 param1;
+       u64 param2;
+};
+
+#define EINJ_OP_BUSY                   0x1
+#define EINJ_STATUS_SUCCESS            0x0
+#define EINJ_STATUS_FAIL               0x1
+#define EINJ_STATUS_INVAL              0x2
+
+#define EINJ_TAB_ENTRY(tab)                                            \
+       ((struct acpi_whea_header *)((char *)(tab) +                    \
+                                   sizeof(struct acpi_table_einj)))
+
+static struct acpi_table_einj *einj_tab;
+
+static struct apei_resources einj_resources;
+
+static struct apei_exec_ins_type einj_ins_type[] = {
+       [ACPI_EINJ_READ_REGISTER] = {
+               .flags = APEI_EXEC_INS_ACCESS_REGISTER,
+               .run   = apei_exec_read_register,
+       },
+       [ACPI_EINJ_READ_REGISTER_VALUE] = {
+               .flags = APEI_EXEC_INS_ACCESS_REGISTER,
+               .run   = apei_exec_read_register_value,
+       },
+       [ACPI_EINJ_WRITE_REGISTER] = {
+               .flags = APEI_EXEC_INS_ACCESS_REGISTER,
+               .run   = apei_exec_write_register,
+       },
+       [ACPI_EINJ_WRITE_REGISTER_VALUE] = {
+               .flags = APEI_EXEC_INS_ACCESS_REGISTER,
+               .run   = apei_exec_write_register_value,
+       },
+       [ACPI_EINJ_NOOP] = {
+               .flags = 0,
+               .run   = apei_exec_noop,
+       },
+};
+
+/*
+ * Prevent EINJ interpreter to run simultaneously, because the
+ * corresponding firmware implementation may not work properly when
+ * invoked simultaneously.
+ */
+static DEFINE_MUTEX(einj_mutex);
+
+static struct einj_parameter *einj_param;
+
+static void einj_exec_ctx_init(struct apei_exec_context *ctx)
+{
+       apei_exec_ctx_init(ctx, einj_ins_type, ARRAY_SIZE(einj_ins_type),
+                          EINJ_TAB_ENTRY(einj_tab), einj_tab->entries);
+}
+
+static int __einj_get_available_error_type(u32 *type)
+{
+       struct apei_exec_context ctx;
+       int rc;
+
+       einj_exec_ctx_init(&ctx);
+       rc = apei_exec_run(&ctx, ACPI_EINJ_GET_ERROR_TYPE);
+       if (rc)
+               return rc;
+       *type = apei_exec_ctx_get_output(&ctx);
+
+       return 0;
+}
+
+/* Get error injection capabilities of the platform */
+static int einj_get_available_error_type(u32 *type)
+{
+       int rc;
+
+       mutex_lock(&einj_mutex);
+       rc = __einj_get_available_error_type(type);
+       mutex_unlock(&einj_mutex);
+
+       return rc;
+}
+
+static int einj_timedout(u64 *t)
+{
+       if ((s64)*t < SPIN_UNIT) {
+               pr_warning(FW_WARN EINJ_PFX
+                          "Firmware does not respond in time\n");
+               return 1;
+       }
+       *t -= SPIN_UNIT;
+       ndelay(SPIN_UNIT);
+       touch_nmi_watchdog();
+       return 0;
+}
+
+static u64 einj_get_parameter_address(void)
+{
+       int i;
+       u64 paddr = 0;
+       struct acpi_whea_header *entry;
+
+       entry = EINJ_TAB_ENTRY(einj_tab);
+       for (i = 0; i < einj_tab->entries; i++) {
+               if (entry->action == ACPI_EINJ_SET_ERROR_TYPE &&
+                   entry->instruction == ACPI_EINJ_WRITE_REGISTER &&
+                   entry->register_region.space_id ==
+                   ACPI_ADR_SPACE_SYSTEM_MEMORY)
+                       memcpy(&paddr, &entry->register_region.address,
+                              sizeof(paddr));
+               entry++;
+       }
+
+       return paddr;
+}
+
+/* do sanity check to trigger table */
+static int einj_check_trigger_header(struct acpi_einj_trigger *trigger_tab)
+{
+       if (trigger_tab->header_size != sizeof(struct acpi_einj_trigger))
+               return -EINVAL;
+       if (trigger_tab->table_size > PAGE_SIZE ||
+           trigger_tab->table_size <= trigger_tab->header_size)
+               return -EINVAL;
+       if (trigger_tab->entry_count !=
+           (trigger_tab->table_size - trigger_tab->header_size) /
+           sizeof(struct acpi_einj_entry))
+               return -EINVAL;
+
+       return 0;
+}
+
+/* Execute instructions in trigger error action table */
+static int __einj_error_trigger(u64 trigger_paddr)
+{
+       struct acpi_einj_trigger *trigger_tab = NULL;
+       struct apei_exec_context trigger_ctx;
+       struct apei_resources trigger_resources;
+       struct acpi_whea_header *trigger_entry;
+       struct resource *r;
+       u32 table_size;
+       int rc = -EIO;
+
+       r = request_mem_region(trigger_paddr, sizeof(*trigger_tab),
+                              "APEI EINJ Trigger Table");
+       if (!r) {
+               pr_err(EINJ_PFX
+       "Can not request iomem region <%016llx-%016llx> for Trigger table.\n",
+                      (unsigned long long)trigger_paddr,
+                      (unsigned long long)trigger_paddr+sizeof(*trigger_tab));
+               goto out;
+       }
+       trigger_tab = ioremap_cache(trigger_paddr, sizeof(*trigger_tab));
+       if (!trigger_tab) {
+               pr_err(EINJ_PFX "Failed to map trigger table!\n");
+               goto out_rel_header;
+       }
+       rc = einj_check_trigger_header(trigger_tab);
+       if (rc) {
+               pr_warning(FW_BUG EINJ_PFX
+                          "The trigger error action table is invalid\n");
+               goto out_rel_header;
+       }
+       rc = -EIO;
+       table_size = trigger_tab->table_size;
+       r = request_mem_region(trigger_paddr + sizeof(*trigger_tab),
+                              table_size - sizeof(*trigger_tab),
+                              "APEI EINJ Trigger Table");
+       if (!r) {
+               pr_err(EINJ_PFX
+"Can not request iomem region <%016llx-%016llx> for Trigger Table Entry.\n",
+                      (unsigned long long)trigger_paddr+sizeof(*trigger_tab),
+                      (unsigned long long)trigger_paddr + table_size);
+               goto out_rel_header;
+       }
+       iounmap(trigger_tab);
+       trigger_tab = ioremap_cache(trigger_paddr, table_size);
+       if (!trigger_tab) {
+               pr_err(EINJ_PFX "Failed to map trigger table!\n");
+               goto out_rel_entry;
+       }
+       trigger_entry = (struct acpi_whea_header *)
+               ((char *)trigger_tab + sizeof(struct acpi_einj_trigger));
+       apei_resources_init(&trigger_resources);
+       apei_exec_ctx_init(&trigger_ctx, einj_ins_type,
+                          ARRAY_SIZE(einj_ins_type),
+                          trigger_entry, trigger_tab->entry_count);
+       rc = apei_exec_collect_resources(&trigger_ctx, &trigger_resources);
+       if (rc)
+               goto out_fini;
+       rc = apei_resources_sub(&trigger_resources, &einj_resources);
+       if (rc)
+               goto out_fini;
+       rc = apei_resources_request(&trigger_resources, "APEI EINJ Trigger");
+       if (rc)
+               goto out_fini;
+       rc = apei_exec_pre_map_gars(&trigger_ctx);
+       if (rc)
+               goto out_release;
+
+       rc = apei_exec_run(&trigger_ctx, ACPI_EINJ_TRIGGER_ERROR);
+
+       apei_exec_post_unmap_gars(&trigger_ctx);
+out_release:
+       apei_resources_release(&trigger_resources);
+out_fini:
+       apei_resources_fini(&trigger_resources);
+out_rel_entry:
+       release_mem_region(trigger_paddr + sizeof(*trigger_tab),
+                          table_size - sizeof(*trigger_tab));
+out_rel_header:
+       release_mem_region(trigger_paddr, sizeof(*trigger_tab));
+out:
+       if (trigger_tab)
+               iounmap(trigger_tab);
+
+       return rc;
+}
+
+static int __einj_error_inject(u32 type, u64 param1, u64 param2)
+{
+       struct apei_exec_context ctx;
+       u64 val, trigger_paddr, timeout = FIRMWARE_TIMEOUT;
+       int rc;
+
+       einj_exec_ctx_init(&ctx);
+
+       rc = apei_exec_run(&ctx, ACPI_EINJ_BEGIN_OPERATION);
+       if (rc)
+               return rc;
+       apei_exec_ctx_set_input(&ctx, type);
+       rc = apei_exec_run(&ctx, ACPI_EINJ_SET_ERROR_TYPE);
+       if (rc)
+               return rc;
+       if (einj_param) {
+               writeq(param1, &einj_param->param1);
+               writeq(param2, &einj_param->param2);
+       }
+       rc = apei_exec_run(&ctx, ACPI_EINJ_EXECUTE_OPERATION);
+       if (rc)
+               return rc;
+       for (;;) {
+               rc = apei_exec_run(&ctx, ACPI_EINJ_CHECK_BUSY_STATUS);
+               if (rc)
+                       return rc;
+               val = apei_exec_ctx_get_output(&ctx);
+               if (!(val & EINJ_OP_BUSY))
+                       break;
+               if (einj_timedout(&timeout))
+                       return -EIO;
+       }
+       rc = apei_exec_run(&ctx, ACPI_EINJ_GET_COMMAND_STATUS);
+       if (rc)
+               return rc;
+       val = apei_exec_ctx_get_output(&ctx);
+       if (val != EINJ_STATUS_SUCCESS)
+               return -EBUSY;
+
+       rc = apei_exec_run(&ctx, ACPI_EINJ_GET_TRIGGER_TABLE);
+       if (rc)
+               return rc;
+       trigger_paddr = apei_exec_ctx_get_output(&ctx);
+       rc = __einj_error_trigger(trigger_paddr);
+       if (rc)
+               return rc;
+       rc = apei_exec_run(&ctx, ACPI_EINJ_END_OPERATION);
+
+       return rc;
+}
+
+/* Inject the specified hardware error */
+static int einj_error_inject(u32 type, u64 param1, u64 param2)
+{
+       int rc;
+
+       mutex_lock(&einj_mutex);
+       rc = __einj_error_inject(type, param1, param2);
+       mutex_unlock(&einj_mutex);
+
+       return rc;
+}
+
+static u32 error_type;
+static u64 error_param1;
+static u64 error_param2;
+static struct dentry *einj_debug_dir;
+
+static int available_error_type_show(struct seq_file *m, void *v)
+{
+       int rc;
+       u32 available_error_type = 0;
+
+       rc = einj_get_available_error_type(&available_error_type);
+       if (rc)
+               return rc;
+       if (available_error_type & 0x0001)
+               seq_printf(m, "0x00000001\tProcessor Correctable\n");
+       if (available_error_type & 0x0002)
+               seq_printf(m, "0x00000002\tProcessor Uncorrectable non-fatal\n");
+       if (available_error_type & 0x0004)
+               seq_printf(m, "0x00000004\tProcessor Uncorrectable fatal\n");
+       if (available_error_type & 0x0008)
+               seq_printf(m, "0x00000008\tMemory Correctable\n");
+       if (available_error_type & 0x0010)
+               seq_printf(m, "0x00000010\tMemory Uncorrectable non-fatal\n");
+       if (available_error_type & 0x0020)
+               seq_printf(m, "0x00000020\tMemory Uncorrectable fatal\n");
+       if (available_error_type & 0x0040)
+               seq_printf(m, "0x00000040\tPCI Express Correctable\n");
+       if (available_error_type & 0x0080)
+               seq_printf(m, "0x00000080\tPCI Express Uncorrectable non-fatal\n");
+       if (available_error_type & 0x0100)
+               seq_printf(m, "0x00000100\tPCI Express Uncorrectable fatal\n");
+       if (available_error_type & 0x0200)
+               seq_printf(m, "0x00000200\tPlatform Correctable\n");
+       if (available_error_type & 0x0400)
+               seq_printf(m, "0x00000400\tPlatform Uncorrectable non-fatal\n");
+       if (available_error_type & 0x0800)
+               seq_printf(m, "0x00000800\tPlatform Uncorrectable fatal\n");
+
+       return 0;
+}
+
+static int available_error_type_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, available_error_type_show, NULL);
+}
+
+static const struct file_operations available_error_type_fops = {
+       .open           = available_error_type_open,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = single_release,
+};
+
+static int error_type_get(void *data, u64 *val)
+{
+       *val = error_type;
+
+       return 0;
+}
+
+static int error_type_set(void *data, u64 val)
+{
+       int rc;
+       u32 available_error_type = 0;
+
+       /* Only one error type can be specified */
+       if (val & (val - 1))
+               return -EINVAL;
+       rc = einj_get_available_error_type(&available_error_type);
+       if (rc)
+               return rc;
+       if (!(val & available_error_type))
+               return -EINVAL;
+       error_type = val;
+
+       return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(error_type_fops, error_type_get,
+                       error_type_set, "0x%llx\n");
+
+static int error_inject_set(void *data, u64 val)
+{
+       if (!error_type)
+               return -EINVAL;
+
+       return einj_error_inject(error_type, error_param1, error_param2);
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(error_inject_fops, NULL,
+                       error_inject_set, "%llu\n");
+
+static int einj_check_table(struct acpi_table_einj *einj_tab)
+{
+       if (einj_tab->header_length != sizeof(struct acpi_table_einj))
+               return -EINVAL;
+       if (einj_tab->header.length < sizeof(struct acpi_table_einj))
+               return -EINVAL;
+       if (einj_tab->entries !=
+           (einj_tab->header.length - sizeof(struct acpi_table_einj)) /
+           sizeof(struct acpi_einj_entry))
+               return -EINVAL;
+
+       return 0;
+}
+
+static int __init einj_init(void)
+{
+       int rc;
+       u64 param_paddr;
+       acpi_status status;
+       struct dentry *fentry;
+       struct apei_exec_context ctx;
+
+       if (acpi_disabled)
+               return -ENODEV;
+
+       status = acpi_get_table(ACPI_SIG_EINJ, 0,
+                               (struct acpi_table_header **)&einj_tab);
+       if (status == AE_NOT_FOUND) {
+               pr_info(EINJ_PFX "Table is not found!\n");
+               return -ENODEV;
+       } else if (ACPI_FAILURE(status)) {
+               const char *msg = acpi_format_exception(status);
+               pr_err(EINJ_PFX "Failed to get table, %s\n", msg);
+               return -EINVAL;
+       }
+
+       rc = einj_check_table(einj_tab);
+       if (rc) {
+               pr_warning(FW_BUG EINJ_PFX "EINJ table is invalid\n");
+               return -EINVAL;
+       }
+
+       rc = -ENOMEM;
+       einj_debug_dir = debugfs_create_dir("einj", apei_get_debugfs_dir());
+       if (!einj_debug_dir)
+               goto err_cleanup;
+       fentry = debugfs_create_file("available_error_type", S_IRUSR,
+                                    einj_debug_dir, NULL,
+                                    &available_error_type_fops);
+       if (!fentry)
+               goto err_cleanup;
+       fentry = debugfs_create_file("error_type", S_IRUSR | S_IWUSR,
+                                    einj_debug_dir, NULL, &error_type_fops);
+       if (!fentry)
+               goto err_cleanup;
+       fentry = debugfs_create_x64("param1", S_IRUSR | S_IWUSR,
+                                   einj_debug_dir, &error_param1);
+       if (!fentry)
+               goto err_cleanup;
+       fentry = debugfs_create_x64("param2", S_IRUSR | S_IWUSR,
+                                   einj_debug_dir, &error_param2);
+       if (!fentry)
+               goto err_cleanup;
+       fentry = debugfs_create_file("error_inject", S_IWUSR,
+                                    einj_debug_dir, NULL, &error_inject_fops);
+       if (!fentry)
+               goto err_cleanup;
+
+       apei_resources_init(&einj_resources);
+       einj_exec_ctx_init(&ctx);
+       rc = apei_exec_collect_resources(&ctx, &einj_resources);
+       if (rc)
+               goto err_fini;
+       rc = apei_resources_request(&einj_resources, "APEI EINJ");
+       if (rc)
+               goto err_fini;
+       rc = apei_exec_pre_map_gars(&ctx);
+       if (rc)
+               goto err_release;
+       param_paddr = einj_get_parameter_address();
+       if (param_paddr) {
+               einj_param = ioremap(param_paddr, sizeof(*einj_param));
+               rc = -ENOMEM;
+               if (!einj_param)
+                       goto err_unmap;
+       }
+
+       pr_info(EINJ_PFX "Error INJection is initialized.\n");
+
+       return 0;
+
+err_unmap:
+       apei_exec_post_unmap_gars(&ctx);
+err_release:
+       apei_resources_release(&einj_resources);
+err_fini:
+       apei_resources_fini(&einj_resources);
+err_cleanup:
+       debugfs_remove_recursive(einj_debug_dir);
+
+       return rc;
+}
+
+static void __exit einj_exit(void)
+{
+       struct apei_exec_context ctx;
+
+       if (einj_param)
+               iounmap(einj_param);
+       einj_exec_ctx_init(&ctx);
+       apei_exec_post_unmap_gars(&ctx);
+       apei_resources_release(&einj_resources);
+       apei_resources_fini(&einj_resources);
+       debugfs_remove_recursive(einj_debug_dir);
+}
+
+module_init(einj_init);
+module_exit(einj_exit);
+
+MODULE_AUTHOR("Huang Ying");
+MODULE_DESCRIPTION("APEI Error INJection support");
+MODULE_LICENSE("GPL");
diff --git a/drivers/acpi/apei/erst.c b/drivers/acpi/apei/erst.c
new file mode 100644 (file)
index 0000000..2ebc391
--- /dev/null
@@ -0,0 +1,855 @@
+/*
+ * APEI Error Record Serialization Table support
+ *
+ * ERST is a way provided by APEI to save and retrieve hardware error
+ * infomation to and from a persistent store.
+ *
+ * For more information about ERST, please refer to ACPI Specification
+ * version 4.0, section 17.4.
+ *
+ * Copyright 2010 Intel Corp.
+ *   Author: Huang Ying <ying.huang@intel.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License version
+ * 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/acpi.h>
+#include <linux/uaccess.h>
+#include <linux/cper.h>
+#include <linux/nmi.h>
+#include <acpi/apei.h>
+
+#include "apei-internal.h"
+
+#define ERST_PFX "ERST: "
+
+/* ERST command status */
+#define ERST_STATUS_SUCCESS                    0x0
+#define ERST_STATUS_NOT_ENOUGH_SPACE           0x1
+#define ERST_STATUS_HARDWARE_NOT_AVAILABLE     0x2
+#define ERST_STATUS_FAILED                     0x3
+#define ERST_STATUS_RECORD_STORE_EMPTY         0x4
+#define ERST_STATUS_RECORD_NOT_FOUND           0x5
+
+#define ERST_TAB_ENTRY(tab)                                            \
+       ((struct acpi_whea_header *)((char *)(tab) +                    \
+                                    sizeof(struct acpi_table_erst)))
+
+#define SPIN_UNIT              100                     /* 100ns */
+/* Firmware should respond within 1 miliseconds */
+#define FIRMWARE_TIMEOUT       (1 * NSEC_PER_MSEC)
+#define FIRMWARE_MAX_STALL     50                      /* 50us */
+
+int erst_disable;
+EXPORT_SYMBOL_GPL(erst_disable);
+
+static struct acpi_table_erst *erst_tab;
+
+/* ERST Error Log Address Range atrributes */
+#define ERST_RANGE_RESERVED    0x0001
+#define ERST_RANGE_NVRAM       0x0002
+#define ERST_RANGE_SLOW                0x0004
+
+/*
+ * ERST Error Log Address Range, used as buffer for reading/writing
+ * error records.
+ */
+static struct erst_erange {
+       u64 base;
+       u64 size;
+       void __iomem *vaddr;
+       u32 attr;
+} erst_erange;
+
+/*
+ * Prevent ERST interpreter to run simultaneously, because the
+ * corresponding firmware implementation may not work properly when
+ * invoked simultaneously.
+ *
+ * It is used to provide exclusive accessing for ERST Error Log
+ * Address Range too.
+ */
+static DEFINE_SPINLOCK(erst_lock);
+
+static inline int erst_errno(int command_status)
+{
+       switch (command_status) {
+       case ERST_STATUS_SUCCESS:
+               return 0;
+       case ERST_STATUS_HARDWARE_NOT_AVAILABLE:
+               return -ENODEV;
+       case ERST_STATUS_NOT_ENOUGH_SPACE:
+               return -ENOSPC;
+       case ERST_STATUS_RECORD_STORE_EMPTY:
+       case ERST_STATUS_RECORD_NOT_FOUND:
+               return -ENOENT;
+       default:
+               return -EINVAL;
+       }
+}
+
+static int erst_timedout(u64 *t, u64 spin_unit)
+{
+       if ((s64)*t < spin_unit) {
+               pr_warning(FW_WARN ERST_PFX
+                          "Firmware does not respond in time\n");
+               return 1;
+       }
+       *t -= spin_unit;
+       ndelay(spin_unit);
+       touch_nmi_watchdog();
+       return 0;
+}
+
+static int erst_exec_load_var1(struct apei_exec_context *ctx,
+                              struct acpi_whea_header *entry)
+{
+       return __apei_exec_read_register(entry, &ctx->var1);
+}
+
+static int erst_exec_load_var2(struct apei_exec_context *ctx,
+                              struct acpi_whea_header *entry)
+{
+       return __apei_exec_read_register(entry, &ctx->var2);
+}
+
+static int erst_exec_store_var1(struct apei_exec_context *ctx,
+                               struct acpi_whea_header *entry)
+{
+       return __apei_exec_write_register(entry, ctx->var1);
+}
+
+static int erst_exec_add(struct apei_exec_context *ctx,
+                        struct acpi_whea_header *entry)
+{
+       ctx->var1 += ctx->var2;
+       return 0;
+}
+
+static int erst_exec_subtract(struct apei_exec_context *ctx,
+                             struct acpi_whea_header *entry)
+{
+       ctx->var1 -= ctx->var2;
+       return 0;
+}
+
+static int erst_exec_add_value(struct apei_exec_context *ctx,
+                              struct acpi_whea_header *entry)
+{
+       int rc;
+       u64 val;
+
+       rc = __apei_exec_read_register(entry, &val);
+       if (rc)
+               return rc;
+       val += ctx->value;
+       rc = __apei_exec_write_register(entry, val);
+       return rc;
+}
+
+static int erst_exec_subtract_value(struct apei_exec_context *ctx,
+                                   struct acpi_whea_header *entry)
+{
+       int rc;
+       u64 val;
+
+       rc = __apei_exec_read_register(entry, &val);
+       if (rc)
+               return rc;
+       val -= ctx->value;
+       rc = __apei_exec_write_register(entry, val);
+       return rc;
+}
+
+static int erst_exec_stall(struct apei_exec_context *ctx,
+                          struct acpi_whea_header *entry)
+{
+       u64 stall_time;
+
+       if (ctx->value > FIRMWARE_MAX_STALL) {
+               if (!in_nmi())
+                       pr_warning(FW_WARN ERST_PFX
+                       "Too long stall time for stall instruction: %llx.\n",
+                                  ctx->value);
+               stall_time = FIRMWARE_MAX_STALL;
+       } else
+               stall_time = ctx->value;
+       udelay(stall_time);
+       return 0;
+}
+
+static int erst_exec_stall_while_true(struct apei_exec_context *ctx,
+                                     struct acpi_whea_header *entry)
+{
+       int rc;
+       u64 val;
+       u64 timeout = FIRMWARE_TIMEOUT;
+       u64 stall_time;
+
+       if (ctx->var1 > FIRMWARE_MAX_STALL) {
+               if (!in_nmi())
+                       pr_warning(FW_WARN ERST_PFX
+               "Too long stall time for stall while true instruction: %llx.\n",
+                                  ctx->var1);
+               stall_time = FIRMWARE_MAX_STALL;
+       } else
+               stall_time = ctx->var1;
+
+       for (;;) {
+               rc = __apei_exec_read_register(entry, &val);
+               if (rc)
+                       return rc;
+               if (val != ctx->value)
+                       break;
+               if (erst_timedout(&timeout, stall_time * NSEC_PER_USEC))
+                       return -EIO;
+       }
+       return 0;
+}
+
+static int erst_exec_skip_next_instruction_if_true(
+       struct apei_exec_context *ctx,
+       struct acpi_whea_header *entry)
+{
+       int rc;
+       u64 val;
+
+       rc = __apei_exec_read_register(entry, &val);
+       if (rc)
+               return rc;
+       if (val == ctx->value) {
+               ctx->ip += 2;
+               return APEI_EXEC_SET_IP;
+       }
+
+       return 0;
+}
+
+static int erst_exec_goto(struct apei_exec_context *ctx,
+                         struct acpi_whea_header *entry)
+{
+       ctx->ip = ctx->value;
+       return APEI_EXEC_SET_IP;
+}
+
+static int erst_exec_set_src_address_base(struct apei_exec_context *ctx,
+                                         struct acpi_whea_header *entry)
+{
+       return __apei_exec_read_register(entry, &ctx->src_base);
+}
+
+static int erst_exec_set_dst_address_base(struct apei_exec_context *ctx,
+                                         struct acpi_whea_header *entry)
+{
+       return __apei_exec_read_register(entry, &ctx->dst_base);
+}
+
+static int erst_exec_move_data(struct apei_exec_context *ctx,
+                              struct acpi_whea_header *entry)
+{
+       int rc;
+       u64 offset;
+
+       rc = __apei_exec_read_register(entry, &offset);
+       if (rc)
+               return rc;
+       memmove((void *)ctx->dst_base + offset,
+               (void *)ctx->src_base + offset,
+               ctx->var2);
+
+       return 0;
+}
+
+static struct apei_exec_ins_type erst_ins_type[] = {
+       [ACPI_ERST_READ_REGISTER] = {
+               .flags = APEI_EXEC_INS_ACCESS_REGISTER,
+               .run = apei_exec_read_register,
+       },
+       [ACPI_ERST_READ_REGISTER_VALUE] = {
+               .flags = APEI_EXEC_INS_ACCESS_REGISTER,
+               .run = apei_exec_read_register_value,
+       },
+       [ACPI_ERST_WRITE_REGISTER] = {
+               .flags = APEI_EXEC_INS_ACCESS_REGISTER,
+               .run = apei_exec_write_register,
+       },
+       [ACPI_ERST_WRITE_REGISTER_VALUE] = {
+               .flags = APEI_EXEC_INS_ACCESS_REGISTER,
+               .run = apei_exec_write_register_value,
+       },
+       [ACPI_ERST_NOOP] = {
+               .flags = 0,
+               .run = apei_exec_noop,
+       },
+       [ACPI_ERST_LOAD_VAR1] = {
+               .flags = APEI_EXEC_INS_ACCESS_REGISTER,
+               .run = erst_exec_load_var1,
+       },
+       [ACPI_ERST_LOAD_VAR2] = {
+               .flags = APEI_EXEC_INS_ACCESS_REGISTER,
+               .run = erst_exec_load_var2,
+       },
+       [ACPI_ERST_STORE_VAR1] = {
+               .flags = APEI_EXEC_INS_ACCESS_REGISTER,
+               .run = erst_exec_store_var1,
+       },
+       [ACPI_ERST_ADD] = {
+               .flags = 0,
+               .run = erst_exec_add,
+       },
+       [ACPI_ERST_SUBTRACT] = {
+               .flags = 0,
+               .run = erst_exec_subtract,
+       },
+       [ACPI_ERST_ADD_VALUE] = {
+               .flags = APEI_EXEC_INS_ACCESS_REGISTER,
+               .run = erst_exec_add_value,
+       },
+       [ACPI_ERST_SUBTRACT_VALUE] = {
+               .flags = APEI_EXEC_INS_ACCESS_REGISTER,
+               .run = erst_exec_subtract_value,
+       },
+       [ACPI_ERST_STALL] = {
+               .flags = 0,
+               .run = erst_exec_stall,
+       },
+       [ACPI_ERST_STALL_WHILE_TRUE] = {
+               .flags = APEI_EXEC_INS_ACCESS_REGISTER,
+               .run = erst_exec_stall_while_true,
+       },
+       [ACPI_ERST_SKIP_NEXT_IF_TRUE] = {
+               .flags = APEI_EXEC_INS_ACCESS_REGISTER,
+               .run = erst_exec_skip_next_instruction_if_true,
+       },
+       [ACPI_ERST_GOTO] = {
+               .flags = 0,
+               .run = erst_exec_goto,
+       },
+       [ACPI_ERST_SET_SRC_ADDRESS_BASE] = {
+               .flags = APEI_EXEC_INS_ACCESS_REGISTER,
+               .run = erst_exec_set_src_address_base,
+       },
+       [ACPI_ERST_SET_DST_ADDRESS_BASE] = {
+               .flags = APEI_EXEC_INS_ACCESS_REGISTER,
+               .run = erst_exec_set_dst_address_base,
+       },
+       [ACPI_ERST_MOVE_DATA] = {
+               .flags = APEI_EXEC_INS_ACCESS_REGISTER,
+               .run = erst_exec_move_data,
+       },
+};
+
+static inline void erst_exec_ctx_init(struct apei_exec_context *ctx)
+{
+       apei_exec_ctx_init(ctx, erst_ins_type, ARRAY_SIZE(erst_ins_type),
+                          ERST_TAB_ENTRY(erst_tab), erst_tab->entries);
+}
+
+static int erst_get_erange(struct erst_erange *range)
+{
+       struct apei_exec_context ctx;
+       int rc;
+
+       erst_exec_ctx_init(&ctx);
+       rc = apei_exec_run(&ctx, ACPI_ERST_GET_ERROR_RANGE);
+       if (rc)
+               return rc;
+       range->base = apei_exec_ctx_get_output(&ctx);
+       rc = apei_exec_run(&ctx, ACPI_ERST_GET_ERROR_LENGTH);
+       if (rc)
+               return rc;
+       range->size = apei_exec_ctx_get_output(&ctx);
+       rc = apei_exec_run(&ctx, ACPI_ERST_GET_ERROR_ATTRIBUTES);
+       if (rc)
+               return rc;
+       range->attr = apei_exec_ctx_get_output(&ctx);
+
+       return 0;
+}
+
+static ssize_t __erst_get_record_count(void)
+{
+       struct apei_exec_context ctx;
+       int rc;
+
+       erst_exec_ctx_init(&ctx);
+       rc = apei_exec_run(&ctx, ACPI_ERST_GET_RECORD_COUNT);
+       if (rc)
+               return rc;
+       return apei_exec_ctx_get_output(&ctx);
+}
+
+ssize_t erst_get_record_count(void)
+{
+       ssize_t count;
+       unsigned long flags;
+
+       if (erst_disable)
+               return -ENODEV;
+
+       spin_lock_irqsave(&erst_lock, flags);
+       count = __erst_get_record_count();
+       spin_unlock_irqrestore(&erst_lock, flags);
+
+       return count;
+}
+EXPORT_SYMBOL_GPL(erst_get_record_count);
+
+static int __erst_get_next_record_id(u64 *record_id)
+{
+       struct apei_exec_context ctx;
+       int rc;
+
+       erst_exec_ctx_init(&ctx);
+       rc = apei_exec_run(&ctx, ACPI_ERST_GET_RECORD_ID);
+       if (rc)
+               return rc;
+       *record_id = apei_exec_ctx_get_output(&ctx);
+
+       return 0;
+}
+
+/*
+ * Get the record ID of an existing error record on the persistent
+ * storage. If there is no error record on the persistent storage, the
+ * returned record_id is APEI_ERST_INVALID_RECORD_ID.
+ */
+int erst_get_next_record_id(u64 *record_id)
+{
+       int rc;
+       unsigned long flags;
+
+       if (erst_disable)
+               return -ENODEV;
+
+       spin_lock_irqsave(&erst_lock, flags);
+       rc = __erst_get_next_record_id(record_id);
+       spin_unlock_irqrestore(&erst_lock, flags);
+
+       return rc;
+}
+EXPORT_SYMBOL_GPL(erst_get_next_record_id);
+
+static int __erst_write_to_storage(u64 offset)
+{
+       struct apei_exec_context ctx;
+       u64 timeout = FIRMWARE_TIMEOUT;
+       u64 val;
+       int rc;
+
+       erst_exec_ctx_init(&ctx);
+       rc = apei_exec_run(&ctx, ACPI_ERST_BEGIN_WRITE);
+       if (rc)
+               return rc;
+       apei_exec_ctx_set_input(&ctx, offset);
+       rc = apei_exec_run(&ctx, ACPI_ERST_SET_RECORD_OFFSET);
+       if (rc)
+               return rc;
+       rc = apei_exec_run(&ctx, ACPI_ERST_EXECUTE_OPERATION);
+       if (rc)
+               return rc;
+       for (;;) {
+               rc = apei_exec_run(&ctx, ACPI_ERST_CHECK_BUSY_STATUS);
+               if (rc)
+                       return rc;
+               val = apei_exec_ctx_get_output(&ctx);
+               if (!val)
+                       break;
+               if (erst_timedout(&timeout, SPIN_UNIT))
+                       return -EIO;
+       }
+       rc = apei_exec_run(&ctx, ACPI_ERST_GET_COMMAND_STATUS);
+       if (rc)
+               return rc;
+       val = apei_exec_ctx_get_output(&ctx);
+       rc = apei_exec_run(&ctx, ACPI_ERST_END);
+       if (rc)
+               return rc;
+
+       return erst_errno(val);
+}
+
+static int __erst_read_from_storage(u64 record_id, u64 offset)
+{
+       struct apei_exec_context ctx;
+       u64 timeout = FIRMWARE_TIMEOUT;
+       u64 val;
+       int rc;
+
+       erst_exec_ctx_init(&ctx);
+       rc = apei_exec_run(&ctx, ACPI_ERST_BEGIN_READ);
+       if (rc)
+               return rc;
+       apei_exec_ctx_set_input(&ctx, offset);
+       rc = apei_exec_run(&ctx, ACPI_ERST_SET_RECORD_OFFSET);
+       if (rc)
+               return rc;
+       apei_exec_ctx_set_input(&ctx, record_id);
+       rc = apei_exec_run(&ctx, ACPI_ERST_SET_RECORD_ID);
+       if (rc)
+               return rc;
+       rc = apei_exec_run(&ctx, ACPI_ERST_EXECUTE_OPERATION);
+       if (rc)
+               return rc;
+       for (;;) {
+               rc = apei_exec_run(&ctx, ACPI_ERST_CHECK_BUSY_STATUS);
+               if (rc)
+                       return rc;
+               val = apei_exec_ctx_get_output(&ctx);
+               if (!val)
+                       break;
+               if (erst_timedout(&timeout, SPIN_UNIT))
+                       return -EIO;
+       };
+       rc = apei_exec_run(&ctx, ACPI_ERST_GET_COMMAND_STATUS);
+       if (rc)
+               return rc;
+       val = apei_exec_ctx_get_output(&ctx);
+       rc = apei_exec_run(&ctx, ACPI_ERST_END);
+       if (rc)
+               return rc;
+
+       return erst_errno(val);
+}
+
+static int __erst_clear_from_storage(u64 record_id)
+{
+       struct apei_exec_context ctx;
+       u64 timeout = FIRMWARE_TIMEOUT;
+       u64 val;
+       int rc;
+
+       erst_exec_ctx_init(&ctx);
+       rc = apei_exec_run(&ctx, ACPI_ERST_BEGIN_CLEAR);
+       if (rc)
+               return rc;
+       apei_exec_ctx_set_input(&ctx, record_id);
+       rc = apei_exec_run(&ctx, ACPI_ERST_SET_RECORD_ID);
+       if (rc)
+               return rc;
+       rc = apei_exec_run(&ctx, ACPI_ERST_EXECUTE_OPERATION);
+       if (rc)
+               return rc;
+       for (;;) {
+               rc = apei_exec_run(&ctx, ACPI_ERST_CHECK_BUSY_STATUS);
+               if (rc)
+                       return rc;
+               val = apei_exec_ctx_get_output(&ctx);
+               if (!val)
+                       break;
+               if (erst_timedout(&timeout, SPIN_UNIT))
+                       return -EIO;
+       }
+       rc = apei_exec_run(&ctx, ACPI_ERST_GET_COMMAND_STATUS);
+       if (rc)
+               return rc;
+       val = apei_exec_ctx_get_output(&ctx);
+       rc = apei_exec_run(&ctx, ACPI_ERST_END);
+       if (rc)
+               return rc;
+
+       return erst_errno(val);
+}
+
+/* NVRAM ERST Error Log Address Range is not supported yet */
+static void pr_unimpl_nvram(void)
+{
+       if (printk_ratelimit())
+               pr_warning(ERST_PFX
+               "NVRAM ERST Log Address Range is not implemented yet\n");
+}
+
+static int __erst_write_to_nvram(const struct cper_record_header *record)
+{
+       /* do not print message, because printk is not safe for NMI */
+       return -ENOSYS;
+}
+
+static int __erst_read_to_erange_from_nvram(u64 record_id, u64 *offset)
+{
+       pr_unimpl_nvram();
+       return -ENOSYS;
+}
+
+static int __erst_clear_from_nvram(u64 record_id)
+{
+       pr_unimpl_nvram();
+       return -ENOSYS;
+}
+
+int erst_write(const struct cper_record_header *record)
+{
+       int rc;
+       unsigned long flags;
+       struct cper_record_header *rcd_erange;
+
+       if (erst_disable)
+               return -ENODEV;
+
+       if (memcmp(record->signature, CPER_SIG_RECORD, CPER_SIG_SIZE))
+               return -EINVAL;
+
+       if (erst_erange.attr & ERST_RANGE_NVRAM) {
+               if (!spin_trylock_irqsave(&erst_lock, flags))
+                       return -EBUSY;
+               rc = __erst_write_to_nvram(record);
+               spin_unlock_irqrestore(&erst_lock, flags);
+               return rc;
+       }
+
+       if (record->record_length > erst_erange.size)
+               return -EINVAL;
+
+       if (!spin_trylock_irqsave(&erst_lock, flags))
+               return -EBUSY;
+       memcpy(erst_erange.vaddr, record, record->record_length);
+       rcd_erange = erst_erange.vaddr;
+       /* signature for serialization system */
+       memcpy(&rcd_erange->persistence_information, "ER", 2);
+
+       rc = __erst_write_to_storage(0);
+       spin_unlock_irqrestore(&erst_lock, flags);
+
+       return rc;
+}
+EXPORT_SYMBOL_GPL(erst_write);
+
+static int __erst_read_to_erange(u64 record_id, u64 *offset)
+{
+       int rc;
+
+       if (erst_erange.attr & ERST_RANGE_NVRAM)
+               return __erst_read_to_erange_from_nvram(
+                       record_id, offset);
+
+       rc = __erst_read_from_storage(record_id, 0);
+       if (rc)
+               return rc;
+       *offset = 0;
+
+       return 0;
+}
+
+static ssize_t __erst_read(u64 record_id, struct cper_record_header *record,
+                          size_t buflen)
+{
+       int rc;
+       u64 offset, len = 0;
+       struct cper_record_header *rcd_tmp;
+
+       rc = __erst_read_to_erange(record_id, &offset);
+       if (rc)
+               return rc;
+       rcd_tmp = erst_erange.vaddr + offset;
+       len = rcd_tmp->record_length;
+       if (len <= buflen)
+               memcpy(record, rcd_tmp, len);
+
+       return len;
+}
+
+/*
+ * If return value > buflen, the buffer size is not big enough,
+ * else if return value < 0, something goes wrong,
+ * else everything is OK, and return value is record length
+ */
+ssize_t erst_read(u64 record_id, struct cper_record_header *record,
+                 size_t buflen)
+{
+       ssize_t len;
+       unsigned long flags;
+
+       if (erst_disable)
+               return -ENODEV;
+
+       spin_lock_irqsave(&erst_lock, flags);
+       len = __erst_read(record_id, record, buflen);
+       spin_unlock_irqrestore(&erst_lock, flags);
+       return len;
+}
+EXPORT_SYMBOL_GPL(erst_read);
+
+/*
+ * If return value > buflen, the buffer size is not big enough,
+ * else if return value = 0, there is no more record to read,
+ * else if return value < 0, something goes wrong,
+ * else everything is OK, and return value is record length
+ */
+ssize_t erst_read_next(struct cper_record_header *record, size_t buflen)
+{
+       int rc;
+       ssize_t len;
+       unsigned long flags;
+       u64 record_id;
+
+       if (erst_disable)
+               return -ENODEV;
+
+       spin_lock_irqsave(&erst_lock, flags);
+       rc = __erst_get_next_record_id(&record_id);
+       if (rc) {
+               spin_unlock_irqrestore(&erst_lock, flags);
+               return rc;
+       }
+       /* no more record */
+       if (record_id == APEI_ERST_INVALID_RECORD_ID) {
+               spin_unlock_irqrestore(&erst_lock, flags);
+               return 0;
+       }
+
+       len = __erst_read(record_id, record, buflen);
+       spin_unlock_irqrestore(&erst_lock, flags);
+
+       return len;
+}
+EXPORT_SYMBOL_GPL(erst_read_next);
+
+int erst_clear(u64 record_id)
+{
+       int rc;
+       unsigned long flags;
+
+       if (erst_disable)
+               return -ENODEV;
+
+       spin_lock_irqsave(&erst_lock, flags);
+       if (erst_erange.attr & ERST_RANGE_NVRAM)
+               rc = __erst_clear_from_nvram(record_id);
+       else
+               rc = __erst_clear_from_storage(record_id);
+       spin_unlock_irqrestore(&erst_lock, flags);
+
+       return rc;
+}
+EXPORT_SYMBOL_GPL(erst_clear);
+
+static int __init setup_erst_disable(char *str)
+{
+       erst_disable = 1;
+       return 0;
+}
+
+__setup("erst_disable", setup_erst_disable);
+
+static int erst_check_table(struct acpi_table_erst *erst_tab)
+{
+       if (erst_tab->header_length != sizeof(struct acpi_table_erst))
+               return -EINVAL;
+       if (erst_tab->header.length < sizeof(struct acpi_table_erst))
+               return -EINVAL;
+       if (erst_tab->entries !=
+           (erst_tab->header.length - sizeof(struct acpi_table_erst)) /
+           sizeof(struct acpi_erst_entry))
+               return -EINVAL;
+
+       return 0;
+}
+
+static int __init erst_init(void)
+{
+       int rc = 0;
+       acpi_status status;
+       struct apei_exec_context ctx;
+       struct apei_resources erst_resources;
+       struct resource *r;
+
+       if (acpi_disabled)
+               goto err;
+
+       if (erst_disable) {
+               pr_info(ERST_PFX
+       "Error Record Serialization Table (ERST) support is disabled.\n");
+               goto err;
+       }
+
+       status = acpi_get_table(ACPI_SIG_ERST, 0,
+                               (struct acpi_table_header **)&erst_tab);
+       if (status == AE_NOT_FOUND) {
+               pr_err(ERST_PFX "Table is not found!\n");
+               goto err;
+       } else if (ACPI_FAILURE(status)) {
+               const char *msg = acpi_format_exception(status);
+               pr_err(ERST_PFX "Failed to get table, %s\n", msg);
+               rc = -EINVAL;
+               goto err;
+       }
+
+       rc = erst_check_table(erst_tab);
+       if (rc) {
+               pr_err(FW_BUG ERST_PFX "ERST table is invalid\n");
+               goto err;
+       }
+
+       apei_resources_init(&erst_resources);
+       erst_exec_ctx_init(&ctx);
+       rc = apei_exec_collect_resources(&ctx, &erst_resources);
+       if (rc)
+               goto err_fini;
+       rc = apei_resources_request(&erst_resources, "APEI ERST");
+       if (rc)
+               goto err_fini;
+       rc = apei_exec_pre_map_gars(&ctx);
+       if (rc)
+               goto err_release;
+       rc = erst_get_erange(&erst_erange);
+       if (rc) {
+               if (rc == -ENODEV)
+                       pr_info(ERST_PFX
+       "The corresponding hardware device or firmware implementation "
+       "is not available.\n");
+               else
+                       pr_err(ERST_PFX
+                              "Failed to get Error Log Address Range.\n");
+               goto err_unmap_reg;
+       }
+
+       r = request_mem_region(erst_erange.base, erst_erange.size, "APEI ERST");
+       if (!r) {
+               pr_err(ERST_PFX
+               "Can not request iomem region <0x%16llx-0x%16llx> for ERST.\n",
+               (unsigned long long)erst_erange.base,
+               (unsigned long long)erst_erange.base + erst_erange.size);
+               rc = -EIO;
+               goto err_unmap_reg;
+       }
+       rc = -ENOMEM;
+       erst_erange.vaddr = ioremap_cache(erst_erange.base,
+                                         erst_erange.size);
+       if (!erst_erange.vaddr)
+               goto err_release_erange;
+
+       pr_info(ERST_PFX
+       "Error Record Serialization Table (ERST) support is initialized.\n");
+
+       return 0;
+
+err_release_erange:
+       release_mem_region(erst_erange.base, erst_erange.size);
+err_unmap_reg:
+       apei_exec_post_unmap_gars(&ctx);
+err_release:
+       apei_resources_release(&erst_resources);
+err_fini:
+       apei_resources_fini(&erst_resources);
+err:
+       erst_disable = 1;
+       return rc;
+}
+
+device_initcall(erst_init);
diff --git a/drivers/acpi/apei/ghes.c b/drivers/acpi/apei/ghes.c
new file mode 100644 (file)
index 0000000..fd0cc01
--- /dev/null
@@ -0,0 +1,427 @@
+/*
+ * APEI Generic Hardware Error Source support
+ *
+ * Generic Hardware Error Source provides a way to report platform
+ * hardware errors (such as that from chipset). It works in so called
+ * "Firmware First" mode, that is, hardware errors are reported to
+ * firmware firstly, then reported to Linux by firmware. This way,
+ * some non-standard hardware error registers or non-standard hardware
+ * link can be checked by firmware to produce more hardware error
+ * information for Linux.
+ *
+ * For more information about Generic Hardware Error Source, please
+ * refer to ACPI Specification version 4.0, section 17.3.2.6
+ *
+ * Now, only SCI notification type and memory errors are
+ * supported. More notification type and hardware error type will be
+ * added later.
+ *
+ * Copyright 2010 Intel Corp.
+ *   Author: Huang Ying <ying.huang@intel.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License version
+ * 2 as published by the Free Software Foundation;
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/acpi.h>
+#include <linux/io.h>
+#include <linux/interrupt.h>
+#include <linux/cper.h>
+#include <linux/kdebug.h>
+#include <acpi/apei.h>
+#include <acpi/atomicio.h>
+#include <acpi/hed.h>
+#include <asm/mce.h>
+
+#include "apei-internal.h"
+
+#define GHES_PFX       "GHES: "
+
+#define GHES_ESTATUS_MAX_SIZE          65536
+
+/*
+ * One struct ghes is created for each generic hardware error
+ * source.
+ *
+ * It provides the context for APEI hardware error timer/IRQ/SCI/NMI
+ * handler. Handler for one generic hardware error source is only
+ * triggered after the previous one is done. So handler can uses
+ * struct ghes without locking.
+ *
+ * estatus: memory buffer for error status block, allocated during
+ * HEST parsing.
+ */
+#define GHES_TO_CLEAR          0x0001
+
+struct ghes {
+       struct acpi_hest_generic *generic;
+       struct acpi_hest_generic_status *estatus;
+       struct list_head list;
+       u64 buffer_paddr;
+       unsigned long flags;
+};
+
+/*
+ * Error source lists, one list for each notification method. The
+ * members in lists are struct ghes.
+ *
+ * The list members are only added in HEST parsing and deleted during
+ * module_exit, that is, single-threaded. So no lock is needed for
+ * that.
+ *
+ * But the mutual exclusion is needed between members adding/deleting
+ * and timer/IRQ/SCI/NMI handler, which may traverse the list. RCU is
+ * used for that.
+ */
+static LIST_HEAD(ghes_sci);
+
+static struct ghes *ghes_new(struct acpi_hest_generic *generic)
+{
+       struct ghes *ghes;
+       unsigned int error_block_length;
+       int rc;
+
+       ghes = kzalloc(sizeof(*ghes), GFP_KERNEL);
+       if (!ghes)
+               return ERR_PTR(-ENOMEM);
+       ghes->generic = generic;
+       INIT_LIST_HEAD(&ghes->list);
+       rc = acpi_pre_map_gar(&generic->error_status_address);
+       if (rc)
+               goto err_free;
+       error_block_length = generic->error_block_length;
+       if (error_block_length > GHES_ESTATUS_MAX_SIZE) {
+               pr_warning(FW_WARN GHES_PFX
+                          "Error status block length is too long: %u for "
+                          "generic hardware error source: %d.\n",
+                          error_block_length, generic->header.source_id);
+               error_block_length = GHES_ESTATUS_MAX_SIZE;
+       }
+       ghes->estatus = kmalloc(error_block_length, GFP_KERNEL);
+       if (!ghes->estatus) {
+               rc = -ENOMEM;
+               goto err_unmap;
+       }
+
+       return ghes;
+
+err_unmap:
+       acpi_post_unmap_gar(&generic->error_status_address);
+err_free:
+       kfree(ghes);
+       return ERR_PTR(rc);
+}
+
+static void ghes_fini(struct ghes *ghes)
+{
+       kfree(ghes->estatus);
+       acpi_post_unmap_gar(&ghes->generic->error_status_address);
+}
+
+enum {
+       GHES_SER_NO = 0x0,
+       GHES_SER_CORRECTED = 0x1,
+       GHES_SER_RECOVERABLE = 0x2,
+       GHES_SER_PANIC = 0x3,
+};
+
+static inline int ghes_severity(int severity)
+{
+       switch (severity) {
+       case CPER_SER_INFORMATIONAL:
+               return GHES_SER_NO;
+       case CPER_SER_CORRECTED:
+               return GHES_SER_CORRECTED;
+       case CPER_SER_RECOVERABLE:
+               return GHES_SER_RECOVERABLE;
+       case CPER_SER_FATAL:
+               return GHES_SER_PANIC;
+       default:
+               /* Unkown, go panic */
+               return GHES_SER_PANIC;
+       }
+}
+
+/* SCI handler run in work queue, so ioremap can be used here */
+static int ghes_copy_tofrom_phys(void *buffer, u64 paddr, u32 len,
+                                int from_phys)
+{
+       void *vaddr;
+
+       vaddr = ioremap_cache(paddr, len);
+       if (!vaddr)
+               return -ENOMEM;
+       if (from_phys)
+               memcpy(buffer, vaddr, len);
+       else
+               memcpy(vaddr, buffer, len);
+       iounmap(vaddr);
+
+       return 0;
+}
+
+static int ghes_read_estatus(struct ghes *ghes, int silent)
+{
+       struct acpi_hest_generic *g = ghes->generic;
+       u64 buf_paddr;
+       u32 len;
+       int rc;
+
+       rc = acpi_atomic_read(&buf_paddr, &g->error_status_address);
+       if (rc) {
+               if (!silent && printk_ratelimit())
+                       pr_warning(FW_WARN GHES_PFX
+"Failed to read error status block address for hardware error source: %d.\n",
+                                  g->header.source_id);
+               return -EIO;
+       }
+       if (!buf_paddr)
+               return -ENOENT;
+
+       rc = ghes_copy_tofrom_phys(ghes->estatus, buf_paddr,
+                                  sizeof(*ghes->estatus), 1);
+       if (rc)
+               return rc;
+       if (!ghes->estatus->block_status)
+               return -ENOENT;
+
+       ghes->buffer_paddr = buf_paddr;
+       ghes->flags |= GHES_TO_CLEAR;
+
+       rc = -EIO;
+       len = apei_estatus_len(ghes->estatus);
+       if (len < sizeof(*ghes->estatus))
+               goto err_read_block;
+       if (len > ghes->generic->error_block_length)
+               goto err_read_block;
+       if (apei_estatus_check_header(ghes->estatus))
+               goto err_read_block;
+       rc = ghes_copy_tofrom_phys(ghes->estatus + 1,
+                                  buf_paddr + sizeof(*ghes->estatus),
+                                  len - sizeof(*ghes->estatus), 1);
+       if (rc)
+               return rc;
+       if (apei_estatus_check(ghes->estatus))
+               goto err_read_block;
+       rc = 0;
+
+err_read_block:
+       if (rc && !silent)
+               pr_warning(FW_WARN GHES_PFX
+                          "Failed to read error status block!\n");
+       return rc;
+}
+
+static void ghes_clear_estatus(struct ghes *ghes)
+{
+       ghes->estatus->block_status = 0;
+       if (!(ghes->flags & GHES_TO_CLEAR))
+               return;
+       ghes_copy_tofrom_phys(ghes->estatus, ghes->buffer_paddr,
+                             sizeof(ghes->estatus->block_status), 0);
+       ghes->flags &= ~GHES_TO_CLEAR;
+}
+
+static void ghes_do_proc(struct ghes *ghes)
+{
+       int ser, processed = 0;
+       struct acpi_hest_generic_data *gdata;
+
+       ser = ghes_severity(ghes->estatus->error_severity);
+       apei_estatus_for_each_section(ghes->estatus, gdata) {
+#ifdef CONFIG_X86_MCE
+               if (!uuid_le_cmp(*(uuid_le *)gdata->section_type,
+                                CPER_SEC_PLATFORM_MEM)) {
+                       apei_mce_report_mem_error(
+                               ser == GHES_SER_CORRECTED,
+                               (struct cper_sec_mem_err *)(gdata+1));
+                       processed = 1;
+               }
+#endif
+       }
+
+       if (!processed && printk_ratelimit())
+               pr_warning(GHES_PFX
+               "Unknown error record from generic hardware error source: %d\n",
+                          ghes->generic->header.source_id);
+}
+
+static int ghes_proc(struct ghes *ghes)
+{
+       int rc;
+
+       rc = ghes_read_estatus(ghes, 0);
+       if (rc)
+               goto out;
+       ghes_do_proc(ghes);
+
+out:
+       ghes_clear_estatus(ghes);
+       return 0;
+}
+
+static int ghes_notify_sci(struct notifier_block *this,
+                                 unsigned long event, void *data)
+{
+       struct ghes *ghes;
+       int ret = NOTIFY_DONE;
+
+       rcu_read_lock();
+       list_for_each_entry_rcu(ghes, &ghes_sci, list) {
+               if (!ghes_proc(ghes))
+                       ret = NOTIFY_OK;
+       }
+       rcu_read_unlock();
+
+       return ret;
+}
+
+static struct notifier_block ghes_notifier_sci = {
+       .notifier_call = ghes_notify_sci,
+};
+
+static int hest_ghes_parse(struct acpi_hest_header *hest_hdr, void *data)
+{
+       struct acpi_hest_generic *generic;
+       struct ghes *ghes = NULL;
+       int rc = 0;
+
+       if (hest_hdr->type != ACPI_HEST_TYPE_GENERIC_ERROR)
+               return 0;
+
+       generic = (struct acpi_hest_generic *)hest_hdr;
+       if (!generic->enabled)
+               return 0;
+
+       if (generic->error_block_length <
+           sizeof(struct acpi_hest_generic_status)) {
+               pr_warning(FW_BUG GHES_PFX
+"Invalid error block length: %u for generic hardware error source: %d\n",
+                          generic->error_block_length,
+                          generic->header.source_id);
+               goto err;
+       }
+       if (generic->records_to_preallocate == 0) {
+               pr_warning(FW_BUG GHES_PFX
+"Invalid records to preallocate: %u for generic hardware error source: %d\n",
+                          generic->records_to_preallocate,
+                          generic->header.source_id);
+               goto err;
+       }
+       ghes = ghes_new(generic);
+       if (IS_ERR(ghes)) {
+               rc = PTR_ERR(ghes);
+               ghes = NULL;
+               goto err;
+       }
+       switch (generic->notify.type) {
+       case ACPI_HEST_NOTIFY_POLLED:
+               pr_warning(GHES_PFX
+"Generic hardware error source: %d notified via POLL is not supported!\n",
+                          generic->header.source_id);
+               break;
+       case ACPI_HEST_NOTIFY_EXTERNAL:
+       case ACPI_HEST_NOTIFY_LOCAL:
+               pr_warning(GHES_PFX
+"Generic hardware error source: %d notified via IRQ is not supported!\n",
+                          generic->header.source_id);
+               break;
+       case ACPI_HEST_NOTIFY_SCI:
+               if (list_empty(&ghes_sci))
+                       register_acpi_hed_notifier(&ghes_notifier_sci);
+               list_add_rcu(&ghes->list, &ghes_sci);
+               break;
+       case ACPI_HEST_NOTIFY_NMI:
+               pr_warning(GHES_PFX
+"Generic hardware error source: %d notified via NMI is not supported!\n",
+                          generic->header.source_id);
+               break;
+       default:
+               pr_warning(FW_WARN GHES_PFX
+       "Unknown notification type: %u for generic hardware error source: %d\n",
+                          generic->notify.type, generic->header.source_id);
+               break;
+       }
+
+       return 0;
+err:
+       if (ghes)
+               ghes_fini(ghes);
+       return rc;
+}
+
+static void ghes_cleanup(void)
+{
+       struct ghes *ghes, *nghes;
+
+       if (!list_empty(&ghes_sci))
+               unregister_acpi_hed_notifier(&ghes_notifier_sci);
+
+       synchronize_rcu();
+
+       list_for_each_entry_safe(ghes, nghes, &ghes_sci, list) {
+               list_del(&ghes->list);
+               ghes_fini(ghes);
+               kfree(ghes);
+       }
+}
+
+static int __init ghes_init(void)
+{
+       int rc;
+
+       if (acpi_disabled)
+               return -ENODEV;
+
+       if (hest_disable) {
+               pr_info(GHES_PFX "HEST is not enabled!\n");
+               return -EINVAL;
+       }
+
+       rc = apei_hest_parse(hest_ghes_parse, NULL);
+       if (rc) {
+               pr_err(GHES_PFX
+               "Error during parsing HEST generic hardware error sources.\n");
+               goto err_cleanup;
+       }
+
+       if (list_empty(&ghes_sci)) {
+               pr_info(GHES_PFX
+                       "No functional generic hardware error sources.\n");
+               rc = -ENODEV;
+               goto err_cleanup;
+       }
+
+       pr_info(GHES_PFX
+               "Generic Hardware Error Source support is initialized.\n");
+
+       return 0;
+err_cleanup:
+       ghes_cleanup();
+       return rc;
+}
+
+static void __exit ghes_exit(void)
+{
+       ghes_cleanup();
+}
+
+module_init(ghes_init);
+module_exit(ghes_exit);
+
+MODULE_AUTHOR("Huang Ying");
+MODULE_DESCRIPTION("APEI Generic Hardware Error Source support");
+MODULE_LICENSE("GPL");
diff --git a/drivers/acpi/apei/hest.c b/drivers/acpi/apei/hest.c
new file mode 100644 (file)
index 0000000..e7f40d3
--- /dev/null
@@ -0,0 +1,173 @@
+/*
+ * APEI Hardware Error Souce Table support
+ *
+ * HEST describes error sources in detail; communicates operational
+ * parameters (i.e. severity levels, masking bits, and threshold
+ * values) to Linux as necessary. It also allows the BIOS to report
+ * non-standard error sources to Linux (for example, chipset-specific
+ * error registers).
+ *
+ * For more information about HEST, please refer to ACPI Specification
+ * version 4.0, section 17.3.2.
+ *
+ * Copyright 2009 Intel Corp.
+ *   Author: Huang Ying <ying.huang@intel.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License version
+ * 2 as published by the Free Software Foundation;
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/acpi.h>
+#include <linux/kdebug.h>
+#include <linux/highmem.h>
+#include <linux/io.h>
+#include <acpi/apei.h>
+
+#include "apei-internal.h"
+
+#define HEST_PFX "HEST: "
+
+int hest_disable;
+EXPORT_SYMBOL_GPL(hest_disable);
+
+/* HEST table parsing */
+
+static struct acpi_table_hest *hest_tab;
+
+static int hest_void_parse(struct acpi_hest_header *hest_hdr, void *data)
+{
+       return 0;
+}
+
+static int hest_esrc_len_tab[ACPI_HEST_TYPE_RESERVED] = {
+       [ACPI_HEST_TYPE_IA32_CHECK] = -1,       /* need further calculation */
+       [ACPI_HEST_TYPE_IA32_CORRECTED_CHECK] = -1,
+       [ACPI_HEST_TYPE_IA32_NMI] = sizeof(struct acpi_hest_ia_nmi),
+       [ACPI_HEST_TYPE_AER_ROOT_PORT] = sizeof(struct acpi_hest_aer_root),
+       [ACPI_HEST_TYPE_AER_ENDPOINT] = sizeof(struct acpi_hest_aer),
+       [ACPI_HEST_TYPE_AER_BRIDGE] = sizeof(struct acpi_hest_aer_bridge),
+       [ACPI_HEST_TYPE_GENERIC_ERROR] = sizeof(struct acpi_hest_generic),
+};
+
+static int hest_esrc_len(struct acpi_hest_header *hest_hdr)
+{
+       u16 hest_type = hest_hdr->type;
+       int len;
+
+       if (hest_type >= ACPI_HEST_TYPE_RESERVED)
+               return 0;
+
+       len = hest_esrc_len_tab[hest_type];
+
+       if (hest_type == ACPI_HEST_TYPE_IA32_CORRECTED_CHECK) {
+               struct acpi_hest_ia_corrected *cmc;
+               cmc = (struct acpi_hest_ia_corrected *)hest_hdr;
+               len = sizeof(*cmc) + cmc->num_hardware_banks *
+                       sizeof(struct acpi_hest_ia_error_bank);
+       } else if (hest_type == ACPI_HEST_TYPE_IA32_CHECK) {
+               struct acpi_hest_ia_machine_check *mc;
+               mc = (struct acpi_hest_ia_machine_check *)hest_hdr;
+               len = sizeof(*mc) + mc->num_hardware_banks *
+                       sizeof(struct acpi_hest_ia_error_bank);
+       }
+       BUG_ON(len == -1);
+
+       return len;
+};
+
+int apei_hest_parse(apei_hest_func_t func, void *data)
+{
+       struct acpi_hest_header *hest_hdr;
+       int i, rc, len;
+
+       if (hest_disable)
+               return -EINVAL;
+
+       hest_hdr = (struct acpi_hest_header *)(hest_tab + 1);
+       for (i = 0; i < hest_tab->error_source_count; i++) {
+               len = hest_esrc_len(hest_hdr);
+               if (!len) {
+                       pr_warning(FW_WARN HEST_PFX
+                                  "Unknown or unused hardware error source "
+                                  "type: %d for hardware error source: %d.\n",
+                                  hest_hdr->type, hest_hdr->source_id);
+                       return -EINVAL;
+               }
+               if ((void *)hest_hdr + len >
+                   (void *)hest_tab + hest_tab->header.length) {
+                       pr_warning(FW_BUG HEST_PFX
+               "Table contents overflow for hardware error source: %d.\n",
+                               hest_hdr->source_id);
+                       return -EINVAL;
+               }
+
+               rc = func(hest_hdr, data);
+               if (rc)
+                       return rc;
+
+               hest_hdr = (void *)hest_hdr + len;
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(apei_hest_parse);
+
+static int __init setup_hest_disable(char *str)
+{
+       hest_disable = 1;
+       return 0;
+}
+
+__setup("hest_disable", setup_hest_disable);
+
+static int __init hest_init(void)
+{
+       acpi_status status;
+       int rc = -ENODEV;
+
+       if (acpi_disabled)
+               goto err;
+
+       if (hest_disable) {
+               pr_info(HEST_PFX "HEST tabling parsing is disabled.\n");
+               goto err;
+       }
+
+       status = acpi_get_table(ACPI_SIG_HEST, 0,
+                               (struct acpi_table_header **)&hest_tab);
+       if (status == AE_NOT_FOUND) {
+               pr_info(HEST_PFX "Table is not found!\n");
+               goto err;
+       } else if (ACPI_FAILURE(status)) {
+               const char *msg = acpi_format_exception(status);
+               pr_err(HEST_PFX "Failed to get table, %s\n", msg);
+               rc = -EINVAL;
+               goto err;
+       }
+
+       rc = apei_hest_parse(hest_void_parse, NULL);
+       if (rc)
+               goto err;
+
+       pr_info(HEST_PFX "HEST table parsing is initialized.\n");
+
+       return 0;
+err:
+       hest_disable = 1;
+       return rc;
+}
+
+subsys_initcall(hest_init);
diff --git a/drivers/acpi/atomicio.c b/drivers/acpi/atomicio.c
new file mode 100644 (file)
index 0000000..814b192
--- /dev/null
@@ -0,0 +1,360 @@
+/*
+ * atomicio.c - ACPI IO memory pre-mapping/post-unmapping, then
+ * accessing in atomic context.
+ *
+ * This is used for NMI handler to access IO memory area, because
+ * ioremap/iounmap can not be used in NMI handler. The IO memory area
+ * is pre-mapped in process context and accessed in NMI handler.
+ *
+ * Copyright (C) 2009-2010, Intel Corp.
+ *     Author: Huang Ying <ying.huang@intel.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License version
+ * 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/acpi.h>
+#include <linux/io.h>
+#include <linux/kref.h>
+#include <linux/rculist.h>
+#include <linux/interrupt.h>
+#include <acpi/atomicio.h>
+
+#define ACPI_PFX "ACPI: "
+
+static LIST_HEAD(acpi_iomaps);
+/*
+ * Used for mutual exclusion between writers of acpi_iomaps list, for
+ * synchronization between readers and writer, RCU is used.
+ */
+static DEFINE_SPINLOCK(acpi_iomaps_lock);
+
+struct acpi_iomap {
+       struct list_head list;
+       void __iomem *vaddr;
+       unsigned long size;
+       phys_addr_t paddr;
+       struct kref ref;
+};
+
+/* acpi_iomaps_lock or RCU read lock must be held before calling */
+static struct acpi_iomap *__acpi_find_iomap(phys_addr_t paddr,
+                                           unsigned long size)
+{
+       struct acpi_iomap *map;
+
+       list_for_each_entry_rcu(map, &acpi_iomaps, list) {
+               if (map->paddr + map->size >= paddr + size &&
+                   map->paddr <= paddr)
+                       return map;
+       }
+       return NULL;
+}
+
+/*
+ * Atomic "ioremap" used by NMI handler, if the specified IO memory
+ * area is not pre-mapped, NULL will be returned.
+ *
+ * acpi_iomaps_lock or RCU read lock must be held before calling
+ */
+static void __iomem *__acpi_ioremap_fast(phys_addr_t paddr,
+                                        unsigned long size)
+{
+       struct acpi_iomap *map;
+
+       map = __acpi_find_iomap(paddr, size);
+       if (map)
+               return map->vaddr + (paddr - map->paddr);
+       else
+               return NULL;
+}
+
+/* acpi_iomaps_lock must be held before calling */
+static void __iomem *__acpi_try_ioremap(phys_addr_t paddr,
+                                       unsigned long size)
+{
+       struct acpi_iomap *map;
+
+       map = __acpi_find_iomap(paddr, size);
+       if (map) {
+               kref_get(&map->ref);
+               return map->vaddr + (paddr - map->paddr);
+       } else
+               return NULL;
+}
+
+/*
+ * Used to pre-map the specified IO memory area. First try to find
+ * whether the area is already pre-mapped, if it is, increase the
+ * reference count (in __acpi_try_ioremap) and return; otherwise, do
+ * the real ioremap, and add the mapping into acpi_iomaps list.
+ */
+static void __iomem *acpi_pre_map(phys_addr_t paddr,
+                                 unsigned long size)
+{
+       void __iomem *vaddr;
+       struct acpi_iomap *map;
+       unsigned long pg_sz, flags;
+       phys_addr_t pg_off;
+
+       spin_lock_irqsave(&acpi_iomaps_lock, flags);
+       vaddr = __acpi_try_ioremap(paddr, size);
+       spin_unlock_irqrestore(&acpi_iomaps_lock, flags);
+       if (vaddr)
+               return vaddr;
+
+       pg_off = paddr & PAGE_MASK;
+       pg_sz = ((paddr + size + PAGE_SIZE - 1) & PAGE_MASK) - pg_off;
+       vaddr = ioremap(pg_off, pg_sz);
+       if (!vaddr)
+               return NULL;
+       map = kmalloc(sizeof(*map), GFP_KERNEL);
+       if (!map)
+               goto err_unmap;
+       INIT_LIST_HEAD(&map->list);
+       map->paddr = pg_off;
+       map->size = pg_sz;
+       map->vaddr = vaddr;
+       kref_init(&map->ref);
+
+       spin_lock_irqsave(&acpi_iomaps_lock, flags);
+       vaddr = __acpi_try_ioremap(paddr, size);
+       if (vaddr) {
+               spin_unlock_irqrestore(&acpi_iomaps_lock, flags);
+               iounmap(map->vaddr);
+               kfree(map);
+               return vaddr;
+       }
+       list_add_tail_rcu(&map->list, &acpi_iomaps);
+       spin_unlock_irqrestore(&acpi_iomaps_lock, flags);
+
+       return vaddr + (paddr - pg_off);
+err_unmap:
+       iounmap(vaddr);
+       return NULL;
+}
+
+/* acpi_iomaps_lock must be held before calling */
+static void __acpi_kref_del_iomap(struct kref *ref)
+{
+       struct acpi_iomap *map;
+
+       map = container_of(ref, struct acpi_iomap, ref);
+       list_del_rcu(&map->list);
+}
+
+/*
+ * Used to post-unmap the specified IO memory area. The iounmap is
+ * done only if the reference count goes zero.
+ */
+static void acpi_post_unmap(phys_addr_t paddr, unsigned long size)
+{
+       struct acpi_iomap *map;
+       unsigned long flags;
+       int del;
+
+       spin_lock_irqsave(&acpi_iomaps_lock, flags);
+       map = __acpi_find_iomap(paddr, size);
+       BUG_ON(!map);
+       del = kref_put(&map->ref, __acpi_kref_del_iomap);
+       spin_unlock_irqrestore(&acpi_iomaps_lock, flags);
+
+       if (!del)
+               return;
+
+       synchronize_rcu();
+       iounmap(map->vaddr);
+       kfree(map);
+}
+
+/* In NMI handler, should set silent = 1 */
+static int acpi_check_gar(struct acpi_generic_address *reg,
+                         u64 *paddr, int silent)
+{
+       u32 width, space_id;
+
+       width = reg->bit_width;
+       space_id = reg->space_id;
+       /* Handle possible alignment issues */
+       memcpy(paddr, &reg->address, sizeof(*paddr));
+       if (!*paddr) {
+               if (!silent)
+                       pr_warning(FW_BUG ACPI_PFX
+                       "Invalid physical address in GAR [0x%llx/%u/%u]\n",
+                                  *paddr, width, space_id);
+               return -EINVAL;
+       }
+
+       if ((width != 8) && (width != 16) && (width != 32) && (width != 64)) {
+               if (!silent)
+                       pr_warning(FW_BUG ACPI_PFX
+                                  "Invalid bit width in GAR [0x%llx/%u/%u]\n",
+                                  *paddr, width, space_id);
+               return -EINVAL;
+       }
+
+       if (space_id != ACPI_ADR_SPACE_SYSTEM_MEMORY &&
+           space_id != ACPI_ADR_SPACE_SYSTEM_IO) {
+               if (!silent)
+                       pr_warning(FW_BUG ACPI_PFX
+                       "Invalid address space type in GAR [0x%llx/%u/%u]\n",
+                                  *paddr, width, space_id);
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+/* Pre-map, working on GAR */
+int acpi_pre_map_gar(struct acpi_generic_address *reg)
+{
+       u64 paddr;
+       void __iomem *vaddr;
+       int rc;
+
+       if (reg->space_id != ACPI_ADR_SPACE_SYSTEM_MEMORY)
+               return 0;
+
+       rc = acpi_check_gar(reg, &paddr, 0);
+       if (rc)
+               return rc;
+
+       vaddr = acpi_pre_map(paddr, reg->bit_width / 8);
+       if (!vaddr)
+               return -EIO;
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(acpi_pre_map_gar);
+
+/* Post-unmap, working on GAR */
+int acpi_post_unmap_gar(struct acpi_generic_address *reg)
+{
+       u64 paddr;
+       int rc;
+
+       if (reg->space_id != ACPI_ADR_SPACE_SYSTEM_MEMORY)
+               return 0;
+
+       rc = acpi_check_gar(reg, &paddr, 0);
+       if (rc)
+               return rc;
+
+       acpi_post_unmap(paddr, reg->bit_width / 8);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(acpi_post_unmap_gar);
+
+/*
+ * Can be used in atomic (including NMI) or process context. RCU read
+ * lock can only be released after the IO memory area accessing.
+ */
+static int acpi_atomic_read_mem(u64 paddr, u64 *val, u32 width)
+{
+       void __iomem *addr;
+
+       rcu_read_lock();
+       addr = __acpi_ioremap_fast(paddr, width);
+       switch (width) {
+       case 8:
+               *val = readb(addr);
+               break;
+       case 16:
+               *val = readw(addr);
+               break;
+       case 32:
+               *val = readl(addr);
+               break;
+       case 64:
+               *val = readq(addr);
+               break;
+       default:
+               return -EINVAL;
+       }
+       rcu_read_unlock();
+
+       return 0;
+}
+
+static int acpi_atomic_write_mem(u64 paddr, u64 val, u32 width)
+{
+       void __iomem *addr;
+
+       rcu_read_lock();
+       addr = __acpi_ioremap_fast(paddr, width);
+       switch (width) {
+       case 8:
+               writeb(val, addr);
+               break;
+       case 16:
+               writew(val, addr);
+               break;
+       case 32:
+               writel(val, addr);
+               break;
+       case 64:
+               writeq(val, addr);
+               break;
+       default:
+               return -EINVAL;
+       }
+       rcu_read_unlock();
+
+       return 0;
+}
+
+/* GAR accessing in atomic (including NMI) or process context */
+int acpi_atomic_read(u64 *val, struct acpi_generic_address *reg)
+{
+       u64 paddr;
+       int rc;
+
+       rc = acpi_check_gar(reg, &paddr, 1);
+       if (rc)
+               return rc;
+
+       *val = 0;
+       switch (reg->space_id) {
+       case ACPI_ADR_SPACE_SYSTEM_MEMORY:
+               return acpi_atomic_read_mem(paddr, val, reg->bit_width);
+       case ACPI_ADR_SPACE_SYSTEM_IO:
+               return acpi_os_read_port(paddr, (u32 *)val, reg->bit_width);
+       default:
+               return -EINVAL;
+       }
+}
+EXPORT_SYMBOL_GPL(acpi_atomic_read);
+
+int acpi_atomic_write(u64 val, struct acpi_generic_address *reg)
+{
+       u64 paddr;
+       int rc;
+
+       rc = acpi_check_gar(reg, &paddr, 1);
+       if (rc)
+               return rc;
+
+       switch (reg->space_id) {
+       case ACPI_ADR_SPACE_SYSTEM_MEMORY:
+               return acpi_atomic_write_mem(paddr, val, reg->bit_width);
+       case ACPI_ADR_SPACE_SYSTEM_IO:
+               return acpi_os_write_port(paddr, val, reg->bit_width);
+       default:
+               return -EINVAL;
+       }
+}
+EXPORT_SYMBOL_GPL(acpi_atomic_write);
index f2234db..e61d4f8 100644 (file)
@@ -1027,10 +1027,9 @@ int __init acpi_ec_ecdt_probe(void)
                /* Don't trust ECDT, which comes from ASUSTek */
                if (!EC_FLAGS_VALIDATE_ECDT)
                        goto install;
-               saved_ec = kmalloc(sizeof(struct acpi_ec), GFP_KERNEL);
+               saved_ec = kmemdup(boot_ec, sizeof(struct acpi_ec), GFP_KERNEL);
                if (!saved_ec)
                        return -ENOMEM;
-               memcpy(saved_ec, boot_ec, sizeof(struct acpi_ec));
        /* fall through */
        }
 
diff --git a/drivers/acpi/hed.c b/drivers/acpi/hed.c
new file mode 100644 (file)
index 0000000..d0c1967
--- /dev/null
@@ -0,0 +1,112 @@
+/*
+ * ACPI Hardware Error Device (PNP0C33) Driver
+ *
+ * Copyright (C) 2010, Intel Corp.
+ *     Author: Huang Ying <ying.huang@intel.com>
+ *
+ * ACPI Hardware Error Device is used to report some hardware errors
+ * notified via SCI, mainly the corrected errors.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License version
+ * 2 as published by the Free Software Foundation;
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/acpi.h>
+#include <acpi/acpi_bus.h>
+#include <acpi/acpi_drivers.h>
+#include <acpi/hed.h>
+
+static struct acpi_device_id acpi_hed_ids[] = {
+       {"PNP0C33", 0},
+       {"", 0},
+};
+MODULE_DEVICE_TABLE(acpi, acpi_hed_ids);
+
+static acpi_handle hed_handle;
+
+static BLOCKING_NOTIFIER_HEAD(acpi_hed_notify_list);
+
+int register_acpi_hed_notifier(struct notifier_block *nb)
+{
+       return blocking_notifier_chain_register(&acpi_hed_notify_list, nb);
+}
+EXPORT_SYMBOL_GPL(register_acpi_hed_notifier);
+
+void unregister_acpi_hed_notifier(struct notifier_block *nb)
+{
+       blocking_notifier_chain_unregister(&acpi_hed_notify_list, nb);
+}
+EXPORT_SYMBOL_GPL(unregister_acpi_hed_notifier);
+
+/*
+ * SCI to report hardware error is forwarded to the listeners of HED,
+ * it is used by HEST Generic Hardware Error Source with notify type
+ * SCI.
+ */
+static void acpi_hed_notify(struct acpi_device *device, u32 event)
+{
+       blocking_notifier_call_chain(&acpi_hed_notify_list, 0, NULL);
+}
+
+static int __devinit acpi_hed_add(struct acpi_device *device)
+{
+       /* Only one hardware error device */
+       if (hed_handle)
+               return -EINVAL;
+       hed_handle = device->handle;
+       return 0;
+}
+
+static int __devexit acpi_hed_remove(struct acpi_device *device, int type)
+{
+       hed_handle = NULL;
+       return 0;
+}
+
+static struct acpi_driver acpi_hed_driver = {
+       .name = "hardware_error_device",
+       .class = "hardware_error",
+       .ids = acpi_hed_ids,
+       .ops = {
+               .add = acpi_hed_add,
+               .remove = acpi_hed_remove,
+               .notify = acpi_hed_notify,
+       },
+};
+
+static int __init acpi_hed_init(void)
+{
+       if (acpi_disabled)
+               return -ENODEV;
+
+       if (acpi_bus_register_driver(&acpi_hed_driver) < 0)
+               return -ENODEV;
+
+       return 0;
+}
+
+static void __exit acpi_hed_exit(void)
+{
+       acpi_bus_unregister_driver(&acpi_hed_driver);
+}
+
+module_init(acpi_hed_init);
+module_exit(acpi_hed_exit);
+
+ACPI_MODULE_NAME("hed");
+MODULE_AUTHOR("Huang Ying");
+MODULE_DESCRIPTION("ACPI Hardware Error Device Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/acpi/hest.c b/drivers/acpi/hest.c
deleted file mode 100644 (file)
index 1c527a1..0000000
+++ /dev/null
@@ -1,139 +0,0 @@
-#include <linux/acpi.h>
-#include <linux/pci.h>
-
-#define PREFIX "ACPI: "
-
-static inline unsigned long parse_acpi_hest_ia_machine_check(struct acpi_hest_ia_machine_check *p)
-{
-       return sizeof(*p) +
-               (sizeof(struct acpi_hest_ia_error_bank) * p->num_hardware_banks);
-}
-
-static inline unsigned long parse_acpi_hest_ia_corrected(struct acpi_hest_ia_corrected *p)
-{
-       return sizeof(*p) +
-               (sizeof(struct acpi_hest_ia_error_bank) * p->num_hardware_banks);
-}
-
-static inline unsigned long parse_acpi_hest_ia_nmi(struct acpi_hest_ia_nmi *p)
-{
-       return sizeof(*p);
-}
-
-static inline unsigned long parse_acpi_hest_generic(struct acpi_hest_generic *p)
-{
-       return sizeof(*p);
-}
-
-static inline unsigned int hest_match_pci(struct acpi_hest_aer_common *p, struct pci_dev *pci)
-{
-       return  (0           == pci_domain_nr(pci->bus) &&
-                p->bus      == pci->bus->number &&
-                p->device   == PCI_SLOT(pci->devfn) &&
-                p->function == PCI_FUNC(pci->devfn));
-}
-
-static unsigned long parse_acpi_hest_aer(void *hdr, int type, struct pci_dev *pci, int *firmware_first)
-{
-       struct acpi_hest_aer_common *p = hdr + sizeof(struct acpi_hest_header);
-       unsigned long rc=0;
-       u8 pcie_type = 0;
-       u8 bridge = 0;
-       switch (type) {
-       case ACPI_HEST_TYPE_AER_ROOT_PORT:
-               rc = sizeof(struct acpi_hest_aer_root);
-               pcie_type = PCI_EXP_TYPE_ROOT_PORT;
-               break;
-       case ACPI_HEST_TYPE_AER_ENDPOINT:
-               rc = sizeof(struct acpi_hest_aer);
-               pcie_type = PCI_EXP_TYPE_ENDPOINT;
-               break;
-       case ACPI_HEST_TYPE_AER_BRIDGE:
-               rc = sizeof(struct acpi_hest_aer_bridge);
-               if ((pci->class >> 16) == PCI_BASE_CLASS_BRIDGE)
-                       bridge = 1;
-               break;
-       }
-
-       if (p->flags & ACPI_HEST_GLOBAL) {
-               if ((pci->is_pcie && (pci->pcie_type == pcie_type)) || bridge)
-                       *firmware_first = !!(p->flags & ACPI_HEST_FIRMWARE_FIRST);
-       }
-       else
-               if (hest_match_pci(p, pci))
-                       *firmware_first = !!(p->flags & ACPI_HEST_FIRMWARE_FIRST);
-       return rc;
-}
-
-static int acpi_hest_firmware_first(struct acpi_table_header *stdheader, struct pci_dev *pci)
-{
-       struct acpi_table_hest *hest = (struct acpi_table_hest *)stdheader;
-       void *p = (void *)hest + sizeof(*hest); /* defined by the ACPI 4.0 spec */
-       struct acpi_hest_header *hdr = p;
-
-       int i;
-       int firmware_first = 0;
-       static unsigned char printed_unused = 0;
-       static unsigned char printed_reserved = 0;
-
-       for (i=0, hdr=p; p < (((void *)hest) + hest->header.length) && i < hest->error_source_count; i++) {
-               switch (hdr->type) {
-               case ACPI_HEST_TYPE_IA32_CHECK:
-                       p += parse_acpi_hest_ia_machine_check(p);
-                       break;
-               case ACPI_HEST_TYPE_IA32_CORRECTED_CHECK:
-                       p += parse_acpi_hest_ia_corrected(p);
-                       break;
-               case ACPI_HEST_TYPE_IA32_NMI:
-                       p += parse_acpi_hest_ia_nmi(p);
-                       break;
-               /* These three should never appear */
-               case ACPI_HEST_TYPE_NOT_USED3:
-               case ACPI_HEST_TYPE_NOT_USED4:
-               case ACPI_HEST_TYPE_NOT_USED5:
-                       if (!printed_unused) {
-                               printk(KERN_DEBUG PREFIX
-                                      "HEST Error Source list contains an obsolete type (%d).\n", hdr->type);
-                               printed_unused = 1;
-                       }
-                       break;
-               case ACPI_HEST_TYPE_AER_ROOT_PORT:
-               case ACPI_HEST_TYPE_AER_ENDPOINT:
-               case ACPI_HEST_TYPE_AER_BRIDGE:
-                       p += parse_acpi_hest_aer(p, hdr->type, pci, &firmware_first);
-                       break;
-               case ACPI_HEST_TYPE_GENERIC_ERROR:
-                       p += parse_acpi_hest_generic(p);
-                       break;
-               /* These should never appear either */
-               case ACPI_HEST_TYPE_RESERVED:
-               default:
-                       if (!printed_reserved) {
-                               printk(KERN_DEBUG PREFIX
-                                      "HEST Error Source list contains a reserved type (%d).\n", hdr->type);
-                               printed_reserved = 1;
-                       }
-                       break;
-               }
-       }
-       return firmware_first;
-}
-
-int acpi_hest_firmware_first_pci(struct pci_dev *pci)
-{
-       acpi_status status = AE_NOT_FOUND;
-       struct acpi_table_header *hest = NULL;
-
-       if (acpi_disabled)
-               return 0;
-
-       status = acpi_get_table(ACPI_SIG_HEST, 1, &hest);
-
-       if (ACPI_SUCCESS(status)) {
-               if (acpi_hest_firmware_first(hest, pci)) {
-                       return 1;
-               }
-       }
-       return 0;
-}
-EXPORT_SYMBOL_GPL(acpi_hest_firmware_first_pci);
index 4bc1c41..78418ce 100644 (file)
@@ -1207,6 +1207,15 @@ int acpi_check_mem_region(resource_size_t start, resource_size_t n,
 EXPORT_SYMBOL(acpi_check_mem_region);
 
 /*
+ * Let drivers know whether the resource checks are effective
+ */
+int acpi_resources_are_enforced(void)
+{
+       return acpi_enforce_resources == ENFORCE_RESOURCES_STRICT;
+}
+EXPORT_SYMBOL(acpi_resources_are_enforced);
+
+/*
  * Acquire a spinlock.
  *
  * handle is a pointer to the spinlock_t.
index aefce33..4eac593 100644 (file)
@@ -120,7 +120,8 @@ acpi_handle acpi_get_pci_rootbridge_handle(unsigned int seg, unsigned int bus)
        struct acpi_pci_root *root;
        
        list_for_each_entry(root, &acpi_pci_roots, node)
-               if ((root->segment == (u16) seg) && (root->bus_nr == (u16) bus))
+               if ((root->segment == (u16) seg) &&
+                   (root->secondary.start == (u16) bus))
                        return root->device->handle;
        return NULL;            
 }
@@ -154,7 +155,7 @@ EXPORT_SYMBOL_GPL(acpi_is_root_bridge);
 static acpi_status
 get_root_bridge_busnr_callback(struct acpi_resource *resource, void *data)
 {
-       int *busnr = data;
+       struct resource *res = data;
        struct acpi_resource_address64 address;
 
        if (resource->type != ACPI_RESOURCE_TYPE_ADDRESS16 &&
@@ -164,28 +165,27 @@ get_root_bridge_busnr_callback(struct acpi_resource *resource, void *data)
 
        acpi_resource_to_address64(resource, &address);
        if ((address.address_length > 0) &&
-           (address.resource_type == ACPI_BUS_NUMBER_RANGE))
-               *busnr = address.minimum;
+           (address.resource_type == ACPI_BUS_NUMBER_RANGE)) {
+               res->start = address.minimum;
+               res->end = address.minimum + address.address_length - 1;
+       }
 
        return AE_OK;
 }
 
 static acpi_status try_get_root_bridge_busnr(acpi_handle handle,
-                                            unsigned long long *bus)
+                                            struct resource *res)
 {
        acpi_status status;
-       int busnum;
 
-       busnum = -1;
+       res->start = -1;
        status =
            acpi_walk_resources(handle, METHOD_NAME__CRS,
-                               get_root_bridge_busnr_callback, &busnum);
+                               get_root_bridge_busnr_callback, res);
        if (ACPI_FAILURE(status))
                return status;
-       /* Check if we really get a bus number from _CRS */
-       if (busnum == -1)
+       if (res->start == -1)
                return AE_ERROR;
-       *bus = busnum;
        return AE_OK;
 }
 
@@ -429,34 +429,47 @@ static int __devinit acpi_pci_root_add(struct acpi_device *device)
        struct acpi_device *child;
        u32 flags, base_flags;
 
+       root = kzalloc(sizeof(struct acpi_pci_root), GFP_KERNEL);
+       if (!root)
+               return -ENOMEM;
+
        segment = 0;
        status = acpi_evaluate_integer(device->handle, METHOD_NAME__SEG, NULL,
                                       &segment);
        if (ACPI_FAILURE(status) && status != AE_NOT_FOUND) {
                printk(KERN_ERR PREFIX "can't evaluate _SEG\n");
-               return -ENODEV;
+               result = -ENODEV;
+               goto end;
        }
 
        /* Check _CRS first, then _BBN.  If no _BBN, default to zero. */
-       bus = 0;
-       status = try_get_root_bridge_busnr(device->handle, &bus);
+       root->secondary.flags = IORESOURCE_BUS;
+       status = try_get_root_bridge_busnr(device->handle, &root->secondary);
        if (ACPI_FAILURE(status)) {
+               /*
+                * We need both the start and end of the downstream bus range
+                * to interpret _CBA (MMCONFIG base address), so it really is
+                * supposed to be in _CRS.  If we don't find it there, all we
+                * can do is assume [_BBN-0xFF] or [0-0xFF].
+                */
+               root->secondary.end = 0xFF;
+               printk(KERN_WARNING FW_BUG PREFIX
+                      "no secondary bus range in _CRS\n");
                status = acpi_evaluate_integer(device->handle, METHOD_NAME__BBN,                                               NULL, &bus);
-               if (ACPI_FAILURE(status) && status != AE_NOT_FOUND) {
-                       printk(KERN_ERR PREFIX
-                            "no bus number in _CRS and can't evaluate _BBN\n");
-                       return -ENODEV;
+               if (ACPI_SUCCESS(status))
+                       root->secondary.start = bus;
+               else if (status == AE_NOT_FOUND)
+                       root->secondary.start = 0;
+               else {
+                       printk(KERN_ERR PREFIX "can't evaluate _BBN\n");
+                       result = -ENODEV;
+                       goto end;
                }
        }
 
-       root = kzalloc(sizeof(struct acpi_pci_root), GFP_KERNEL);
-       if (!root)
-               return -ENOMEM;
-
        INIT_LIST_HEAD(&root->node);
        root->device = device;
        root->segment = segment & 0xFFFF;
-       root->bus_nr = bus & 0xFF;
        strcpy(acpi_device_name(device), ACPI_PCI_ROOT_DEVICE_NAME);
        strcpy(acpi_device_class(device), ACPI_PCI_ROOT_CLASS);
        device->driver_data = root;
@@ -475,9 +488,9 @@ static int __devinit acpi_pci_root_add(struct acpi_device *device)
        /* TBD: Locking */
        list_add_tail(&root->node, &acpi_pci_roots);
 
-       printk(KERN_INFO PREFIX "%s [%s] (%04x:%02x)\n",
+       printk(KERN_INFO PREFIX "%s [%s] (domain %04x %pR)\n",
               acpi_device_name(device), acpi_device_bid(device),
-              root->segment, root->bus_nr);
+              root->segment, &root->secondary);
 
        /*
         * Scan the Root Bridge
@@ -486,11 +499,11 @@ static int __devinit acpi_pci_root_add(struct acpi_device *device)
         * PCI namespace does not get created until this call is made (and 
         * thus the root bridge's pci_dev does not exist).
         */
-       root->bus = pci_acpi_scan_root(device, segment, bus);
+       root->bus = pci_acpi_scan_root(root);
        if (!root->bus) {
                printk(KERN_ERR PREFIX
                            "Bus %04x:%02x not present in PCI namespace\n",
-                           root->segment, root->bus_nr);
+                           root->segment, (unsigned int)root->secondary.start);
                result = -ENODEV;
                goto end;
        }
index 5675d97..b1034a9 100644 (file)
@@ -616,7 +616,8 @@ static int __cpuinit acpi_processor_add(struct acpi_device *device)
        acpi_processor_get_limit_info(pr);
 
 
-       acpi_processor_power_init(pr, device);
+       if (cpuidle_get_driver() == &acpi_idle_driver)
+               acpi_processor_power_init(pr, device);
 
        pr->cdev = thermal_cooling_device_register("Processor", device,
                                                &processor_cooling_ops);
@@ -920,9 +921,14 @@ static int __init acpi_processor_init(void)
        if (!acpi_processor_dir)
                return -ENOMEM;
 #endif
-       result = cpuidle_register_driver(&acpi_idle_driver);
-       if (result < 0)
-               goto out_proc;
+
+       if (!cpuidle_register_driver(&acpi_idle_driver)) {
+               printk(KERN_DEBUG "ACPI: %s registered with cpuidle\n",
+                       acpi_idle_driver.name);
+       } else {
+               printk(KERN_DEBUG "ACPI: acpi_idle yielding to %s",
+                       cpuidle_get_driver()->name);
+       }
 
        result = acpi_bus_register_driver(&acpi_processor_driver);
        if (result < 0)
@@ -941,7 +947,6 @@ static int __init acpi_processor_init(void)
 out_cpuidle:
        cpuidle_unregister_driver(&acpi_idle_driver);
 
-out_proc:
 #ifdef CONFIG_ACPI_PROCFS
        remove_proc_entry(ACPI_PROCESSOR_CLASS, acpi_root_dir);
 #endif
index c3817e1..2e8c27d 100644 (file)
@@ -727,19 +727,9 @@ static int acpi_processor_power_seq_show(struct seq_file *seq, void *offset)
                        break;
                }
 
-               if (pr->power.states[i].promotion.state)
-                       seq_printf(seq, "promotion[C%zd] ",
-                                  (pr->power.states[i].promotion.state -
-                                   pr->power.states));
-               else
-                       seq_puts(seq, "promotion[--] ");
-
-               if (pr->power.states[i].demotion.state)
-                       seq_printf(seq, "demotion[C%zd] ",
-                                  (pr->power.states[i].demotion.state -
-                                   pr->power.states));
-               else
-                       seq_puts(seq, "demotion[--] ");
+               seq_puts(seq, "promotion[--] ");
+
+               seq_puts(seq, "demotion[--] ");
 
                seq_printf(seq, "latency[%03d] usage[%08d] duration[%020llu]\n",
                           pr->power.states[i].latency,
@@ -869,6 +859,7 @@ static int acpi_idle_enter_simple(struct cpuidle_device *dev,
        struct acpi_processor *pr;
        struct acpi_processor_cx *cx = cpuidle_get_statedata(state);
        ktime_t  kt1, kt2;
+       s64 idle_time_ns;
        s64 idle_time;
        s64 sleep_ticks = 0;
 
@@ -881,6 +872,7 @@ static int acpi_idle_enter_simple(struct cpuidle_device *dev,
                return(acpi_idle_enter_c1(dev, state));
 
        local_irq_disable();
+
        if (cx->entry_method != ACPI_CSTATE_FFH) {
                current_thread_info()->status &= ~TS_POLLING;
                /*
@@ -888,12 +880,12 @@ static int acpi_idle_enter_simple(struct cpuidle_device *dev,
                 * NEED_RESCHED:
                 */
                smp_mb();
-       }
 
-       if (unlikely(need_resched())) {
-               current_thread_info()->status |= TS_POLLING;
-               local_irq_enable();
-               return 0;
+               if (unlikely(need_resched())) {
+                       current_thread_info()->status |= TS_POLLING;
+                       local_irq_enable();
+                       return 0;
+               }
        }
 
        /*
@@ -910,15 +902,18 @@ static int acpi_idle_enter_simple(struct cpuidle_device *dev,
        sched_clock_idle_sleep_event();
        acpi_idle_do_entry(cx);
        kt2 = ktime_get_real();
-       idle_time =  ktime_to_us(ktime_sub(kt2, kt1));
+       idle_time_ns = ktime_to_ns(ktime_sub(kt2, kt1));
+       idle_time = idle_time_ns;
+       do_div(idle_time, NSEC_PER_USEC);
 
        sleep_ticks = us_to_pm_timer_ticks(idle_time);
 
        /* Tell the scheduler how much we idled: */
-       sched_clock_idle_wakeup_event(sleep_ticks*PM_TIMER_TICK_NS);
+       sched_clock_idle_wakeup_event(idle_time_ns);
 
        local_irq_enable();
-       current_thread_info()->status |= TS_POLLING;
+       if (cx->entry_method != ACPI_CSTATE_FFH)
+               current_thread_info()->status |= TS_POLLING;
 
        cx->usage++;
 
@@ -943,6 +938,7 @@ static int acpi_idle_enter_bm(struct cpuidle_device *dev,
        struct acpi_processor *pr;
        struct acpi_processor_cx *cx = cpuidle_get_statedata(state);
        ktime_t  kt1, kt2;
+       s64 idle_time_ns;
        s64 idle_time;
        s64 sleep_ticks = 0;
 
@@ -968,6 +964,7 @@ static int acpi_idle_enter_bm(struct cpuidle_device *dev,
        }
 
        local_irq_disable();
+
        if (cx->entry_method != ACPI_CSTATE_FFH) {
                current_thread_info()->status &= ~TS_POLLING;
                /*
@@ -975,12 +972,12 @@ static int acpi_idle_enter_bm(struct cpuidle_device *dev,
                 * NEED_RESCHED:
                 */
                smp_mb();
-       }
 
-       if (unlikely(need_resched())) {
-               current_thread_info()->status |= TS_POLLING;
-               local_irq_enable();
-               return 0;
+               if (unlikely(need_resched())) {
+                       current_thread_info()->status |= TS_POLLING;
+                       local_irq_enable();
+                       return 0;
+               }
        }
 
        acpi_unlazy_tlb(smp_processor_id());
@@ -1025,14 +1022,17 @@ static int acpi_idle_enter_bm(struct cpuidle_device *dev,
                spin_unlock(&c3_lock);
        }
        kt2 = ktime_get_real();
-       idle_time =  ktime_to_us(ktime_sub(kt2, kt1));
+       idle_time_ns = ktime_to_us(ktime_sub(kt2, kt1));
+       idle_time = idle_time_ns;
+       do_div(idle_time, NSEC_PER_USEC);
 
        sleep_ticks = us_to_pm_timer_ticks(idle_time);
        /* Tell the scheduler how much we idled: */
-       sched_clock_idle_wakeup_event(sleep_ticks*PM_TIMER_TICK_NS);
+       sched_clock_idle_wakeup_event(idle_time_ns);
 
        local_irq_enable();
-       current_thread_info()->status |= TS_POLLING;
+       if (cx->entry_method != ACPI_CSTATE_FFH)
+               current_thread_info()->status |= TS_POLLING;
 
        cx->usage++;
 
index baa76bb..4ab2275 100644 (file)
@@ -80,22 +80,6 @@ static int acpi_sleep_prepare(u32 acpi_state)
 
 #ifdef CONFIG_ACPI_SLEEP
 static u32 acpi_target_sleep_state = ACPI_STATE_S0;
-/*
- * According to the ACPI specification the BIOS should make sure that ACPI is
- * enabled and SCI_EN bit is set on wake-up from S1 - S3 sleep states.  Still,
- * some BIOSes don't do that and therefore we use acpi_enable() to enable ACPI
- * on such systems during resume.  Unfortunately that doesn't help in
- * particularly pathological cases in which SCI_EN has to be set directly on
- * resume, although the specification states very clearly that this flag is
- * owned by the hardware.  The set_sci_en_on_resume variable will be set in such
- * cases.
- */
-static bool set_sci_en_on_resume;
-
-void __init acpi_set_sci_en_on_resume(void)
-{
-       set_sci_en_on_resume = true;
-}
 
 /*
  * ACPI 1.0 wants us to execute _PTS before suspending devices, so we allow the
@@ -253,11 +237,8 @@ static int acpi_suspend_enter(suspend_state_t pm_state)
                break;
        }
 
-       /* If ACPI is not enabled by the BIOS, we need to enable it here. */
-       if (set_sci_en_on_resume)
-               acpi_write_bit_register(ACPI_BITREG_SCI_ENABLE, 1);
-       else
-               acpi_enable();
+       /* This violates the spec but is required for bug compatibility. */
+       acpi_write_bit_register(ACPI_BITREG_SCI_ENABLE, 1);
 
        /* Reprogram control registers and execute _BFS */
        acpi_leave_sleep_state_prep(acpi_state);
@@ -346,12 +327,6 @@ static int __init init_old_suspend_ordering(const struct dmi_system_id *d)
        return 0;
 }
 
-static int __init init_set_sci_en_on_resume(const struct dmi_system_id *d)
-{
-       set_sci_en_on_resume = true;
-       return 0;
-}
-
 static struct dmi_system_id __initdata acpisleep_dmi_table[] = {
        {
        .callback = init_old_suspend_ordering,
@@ -370,22 +345,6 @@ static struct dmi_system_id __initdata acpisleep_dmi_table[] = {
                },
        },
        {
-       .callback = init_set_sci_en_on_resume,
-       .ident = "Apple MacBook 1,1",
-       .matches = {
-               DMI_MATCH(DMI_SYS_VENDOR, "Apple Computer, Inc."),
-               DMI_MATCH(DMI_PRODUCT_NAME, "MacBook1,1"),
-               },
-       },
-       {
-       .callback = init_set_sci_en_on_resume,
-       .ident = "Apple MacMini 1,1",
-       .matches = {
-               DMI_MATCH(DMI_SYS_VENDOR, "Apple Computer, Inc."),
-               DMI_MATCH(DMI_PRODUCT_NAME, "Macmini1,1"),
-               },
-       },
-       {
        .callback = init_old_suspend_ordering,
        .ident = "Asus Pundit P1-AH2 (M2N8L motherboard)",
        .matches = {
@@ -394,94 +353,6 @@ static struct dmi_system_id __initdata acpisleep_dmi_table[] = {
                },
        },
        {
-       .callback = init_set_sci_en_on_resume,
-       .ident = "Toshiba Satellite L300",
-       .matches = {
-               DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"),
-               DMI_MATCH(DMI_PRODUCT_NAME, "Satellite L300"),
-               },
-       },
-       {
-       .callback = init_set_sci_en_on_resume,
-       .ident = "Hewlett-Packard HP G7000 Notebook PC",
-       .matches = {
-               DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
-               DMI_MATCH(DMI_PRODUCT_NAME, "HP G7000 Notebook PC"),
-               },
-       },
-       {
-       .callback = init_set_sci_en_on_resume,
-       .ident = "Hewlett-Packard HP Pavilion dv3 Notebook PC",
-       .matches = {
-               DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
-               DMI_MATCH(DMI_PRODUCT_NAME, "HP Pavilion dv3 Notebook PC"),
-               },
-       },
-       {
-       .callback = init_set_sci_en_on_resume,
-       .ident = "Hewlett-Packard Pavilion dv4",
-       .matches = {
-               DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
-               DMI_MATCH(DMI_PRODUCT_NAME, "HP Pavilion dv4"),
-               },
-       },
-       {
-       .callback = init_set_sci_en_on_resume,
-       .ident = "Hewlett-Packard Pavilion dv7",
-       .matches = {
-               DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
-               DMI_MATCH(DMI_PRODUCT_NAME, "HP Pavilion dv7"),
-               },
-       },
-       {
-       .callback = init_set_sci_en_on_resume,
-       .ident = "Hewlett-Packard Compaq Presario C700 Notebook PC",
-       .matches = {
-               DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
-               DMI_MATCH(DMI_PRODUCT_NAME, "Compaq Presario C700 Notebook PC"),
-               },
-       },
-       {
-       .callback = init_set_sci_en_on_resume,
-       .ident = "Hewlett-Packard Compaq Presario CQ40 Notebook PC",
-       .matches = {
-               DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
-               DMI_MATCH(DMI_PRODUCT_NAME, "Compaq Presario CQ40 Notebook PC"),
-               },
-       },
-       {
-       .callback = init_set_sci_en_on_resume,
-       .ident = "Lenovo ThinkPad T410",
-       .matches = {
-               DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
-               DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad T410"),
-               },
-       },
-       {
-       .callback = init_set_sci_en_on_resume,
-       .ident = "Lenovo ThinkPad T510",
-       .matches = {
-               DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
-               DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad T510"),
-               },
-       },
-       {
-       .callback = init_set_sci_en_on_resume,
-       .ident = "Lenovo ThinkPad W510",
-       .matches = {
-               DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
-               DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad W510"),
-               },
-       },
-       {
-       .callback = init_set_sci_en_on_resume,
-       .ident = "Lenovo ThinkPad X201[s]",
-       .matches = {
-               DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
-               DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad X201"),
-               },
-       },
-       {
        .callback = init_old_suspend_ordering,
        .ident = "Panasonic CF51-2L",
        .matches = {
@@ -490,30 +361,6 @@ static struct dmi_system_id __initdata acpisleep_dmi_table[] = {
                DMI_MATCH(DMI_BOARD_NAME, "CF51-2L"),
                },
        },
-       {
-       .callback = init_set_sci_en_on_resume,
-       .ident = "Dell Studio 1558",
-       .matches = {
-               DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
-               DMI_MATCH(DMI_PRODUCT_NAME, "Studio 1558"),
-               },
-       },
-       {
-       .callback = init_set_sci_en_on_resume,
-       .ident = "Dell Studio 1557",
-       .matches = {
-               DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
-               DMI_MATCH(DMI_PRODUCT_NAME, "Studio 1557"),
-               },
-       },
-       {
-       .callback = init_set_sci_en_on_resume,
-       .ident = "Dell Studio 1555",
-       .matches = {
-               DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
-               DMI_MATCH(DMI_PRODUCT_NAME, "Studio 1555"),
-               },
-       },
        {},
 };
 #endif /* CONFIG_SUSPEND */
index 8a8f3b3..25b8bd1 100644 (file)
@@ -1,6 +1,6 @@
 
 extern u8 sleep_states[];
-extern int acpi_suspend (u32 state);
+extern int acpi_suspend(u32 state);
 
 extern void acpi_enable_wakeup_device_prep(u8 sleep_state);
 extern void acpi_enable_wakeup_device(u8 sleep_state);
index 8a0ed28..f336bca 100644 (file)
@@ -213,7 +213,7 @@ acpi_table_parse_entries(char *id,
        unsigned long table_end;
        acpi_size tbl_size;
 
-       if (acpi_disabled && !acpi_ht)
+       if (acpi_disabled)
                return -ENODEV;
 
        if (!handler)
@@ -280,7 +280,7 @@ int __init acpi_table_parse(char *id, acpi_table_handler handler)
        struct acpi_table_header *table = NULL;
        acpi_size tbl_size;
 
-       if (acpi_disabled && !acpi_ht)
+       if (acpi_disabled)
                return -ENODEV;
 
        if (!handler)
index a0c93b3..9865d46 100644 (file)
@@ -45,6 +45,7 @@
 #include <acpi/acpi_bus.h>
 #include <acpi/acpi_drivers.h>
 #include <linux/suspend.h>
+#include <acpi/video.h>
 
 #define PREFIX "ACPI: "
 
 
 #define MAX_NAME_LEN   20
 
-#define ACPI_VIDEO_DISPLAY_CRT 1
-#define ACPI_VIDEO_DISPLAY_TV  2
-#define ACPI_VIDEO_DISPLAY_DVI 3
-#define ACPI_VIDEO_DISPLAY_LCD 4
-
 #define _COMPONENT             ACPI_VIDEO_COMPONENT
 ACPI_MODULE_NAME("video");
 
@@ -1007,11 +1003,11 @@ static void acpi_video_device_find_cap(struct acpi_video_device *device)
                result = acpi_video_init_brightness(device);
                if (result)
                        return;
-               name = kzalloc(MAX_NAME_LEN, GFP_KERNEL);
+               name = kasprintf(GFP_KERNEL, "acpi_video%d", count);
                if (!name)
                        return;
+               count++;
 
-               sprintf(name, "acpi_video%d", count++);
                memset(&props, 0, sizeof(struct backlight_properties));
                props.max_brightness = device->brightness->count - 3;
                device->backlight = backlight_device_register(name, NULL, device,
@@ -1067,10 +1063,10 @@ static void acpi_video_device_find_cap(struct acpi_video_device *device)
                if (device->cap._DCS && device->cap._DSS) {
                        static int count;
                        char *name;
-                       name = kzalloc(MAX_NAME_LEN, GFP_KERNEL);
+                       name = kasprintf(GFP_KERNEL, "acpi_video%d", count);
                        if (!name)
                                return;
-                       sprintf(name, "acpi_video%d", count++);
+                       count++;
                        device->output_dev = video_output_register(name,
                                        NULL, device, &acpi_output_properties);
                        kfree(name);
@@ -1748,11 +1744,27 @@ acpi_video_get_device_attr(struct acpi_video_bus *video, unsigned long device_id
 }
 
 static int
+acpi_video_get_device_type(struct acpi_video_bus *video,
+                          unsigned long device_id)
+{
+       struct acpi_video_enumerated_device *ids;
+       int i;
+
+       for (i = 0; i < video->attached_count; i++) {
+               ids = &video->attached_array[i];
+               if ((ids->value.int_val & 0xffff) == device_id)
+                       return ids->value.int_val;
+       }
+
+       return 0;
+}
+
+static int
 acpi_video_bus_get_one_device(struct acpi_device *device,
                              struct acpi_video_bus *video)
 {
        unsigned long long device_id;
-       int status;
+       int status, device_type;
        struct acpi_video_device *data;
        struct acpi_video_device_attrib* attribute;
 
@@ -1797,8 +1809,25 @@ acpi_video_bus_get_one_device(struct acpi_device *device,
                        }
                        if(attribute->bios_can_detect)
                                data->flags.bios = 1;
-               } else
-                       data->flags.unknown = 1;
+               } else {
+                       /* Check for legacy IDs */
+                       device_type = acpi_video_get_device_type(video,
+                                                                device_id);
+                       /* Ignore bits 16 and 18-20 */
+                       switch (device_type & 0xffe2ffff) {
+                       case ACPI_VIDEO_DISPLAY_LEGACY_MONITOR:
+                               data->flags.crt = 1;
+                               break;
+                       case ACPI_VIDEO_DISPLAY_LEGACY_PANEL:
+                               data->flags.lcd = 1;
+                               break;
+                       case ACPI_VIDEO_DISPLAY_LEGACY_TV:
+                               data->flags.tvout = 1;
+                               break;
+                       default:
+                               data->flags.unknown = 1;
+                       }
+               }
 
                acpi_video_device_bind(video, data);
                acpi_video_device_find_cap(data);
@@ -2032,6 +2061,71 @@ out:
        return result;
 }
 
+int acpi_video_get_edid(struct acpi_device *device, int type, int device_id,
+                       void **edid)
+{
+       struct acpi_video_bus *video;
+       struct acpi_video_device *video_device;
+       union acpi_object *buffer = NULL;
+       acpi_status status;
+       int i, length;
+
+       if (!device || !acpi_driver_data(device))
+               return -EINVAL;
+
+       video = acpi_driver_data(device);
+
+       for (i = 0; i < video->attached_count; i++) {
+               video_device = video->attached_array[i].bind_info;
+               length = 256;
+
+               if (!video_device)
+                       continue;
+
+               if (type) {
+                       switch (type) {
+                       case ACPI_VIDEO_DISPLAY_CRT:
+                               if (!video_device->flags.crt)
+                                       continue;
+                               break;
+                       case ACPI_VIDEO_DISPLAY_TV:
+                               if (!video_device->flags.tvout)
+                                       continue;
+                               break;
+                       case ACPI_VIDEO_DISPLAY_DVI:
+                               if (!video_device->flags.dvi)
+                                       continue;
+                               break;
+                       case ACPI_VIDEO_DISPLAY_LCD:
+                               if (!video_device->flags.lcd)
+                                       continue;
+                               break;
+                       }
+               } else if (video_device->device_id != device_id) {
+                       continue;
+               }
+
+               status = acpi_video_device_EDID(video_device, &buffer, length);
+
+               if (ACPI_FAILURE(status) || !buffer ||
+                   buffer->type != ACPI_TYPE_BUFFER) {
+                       length = 128;
+                       status = acpi_video_device_EDID(video_device, &buffer,
+                                                       length);
+                       if (ACPI_FAILURE(status) || !buffer ||
+                           buffer->type != ACPI_TYPE_BUFFER) {
+                               continue;
+                       }
+               }
+
+               *edid = buffer->buffer.pointer;
+               return length;
+       }
+
+       return -ENODEV;
+}
+EXPORT_SYMBOL(acpi_video_get_edid);
+
 static int
 acpi_video_bus_get_devices(struct acpi_video_bus *video,
                           struct acpi_device *device)
index fc2f26b..c5fef01 100644 (file)
@@ -250,7 +250,7 @@ static int __init acpi_backlight(char *str)
                                ACPI_VIDEO_BACKLIGHT_FORCE_VENDOR;
                if (!strcmp("video", str))
                        acpi_video_support |=
-                               ACPI_VIDEO_OUTPUT_SWITCHING_FORCE_VIDEO;
+                               ACPI_VIDEO_BACKLIGHT_FORCE_VIDEO;
        }
        return 1;
 }
index e68541f..73f8833 100644 (file)
@@ -57,6 +57,8 @@ config SATA_PMP
          This option adds support for SATA Port Multipliers
          (the SATA version of an ethernet hub, or SAS expander).
 
+comment "Controllers with non-SFF native interface"
+
 config SATA_AHCI
        tristate "AHCI SATA support"
        depends on PCI
@@ -73,11 +75,12 @@ config SATA_AHCI_PLATFORM
 
          If unsure, say N.
 
-config SATA_SIL24
-       tristate "Silicon Image 3124/3132 SATA support"
-       depends on PCI
+config SATA_FSL
+       tristate "Freescale 3.0Gbps SATA support"
+       depends on FSL_SOC
        help
-         This option enables support for Silicon Image 3124/3132 Serial ATA.
+         This option enables support for Freescale 3.0Gbps SATA controller.
+         It can be found on MPC837x and MPC8315.
 
          If unsure, say N.
 
@@ -87,12 +90,11 @@ config SATA_INIC162X
        help
          This option enables support for Initio 162x Serial ATA.
 
-config SATA_FSL
-       tristate "Freescale 3.0Gbps SATA support"
-       depends on FSL_SOC
+config SATA_SIL24
+       tristate "Silicon Image 3124/3132 SATA support"
+       depends on PCI
        help
-         This option enables support for Freescale 3.0Gbps SATA controller.
-         It can be found on MPC837x and MPC8315.
+         This option enables support for Silicon Image 3124/3132 Serial ATA.
 
          If unsure, say N.
 
@@ -116,15 +118,65 @@ config ATA_SFF
 
 if ATA_SFF
 
-config SATA_SVW
-       tristate "ServerWorks Frodo / Apple K2 SATA support"
+comment "SFF controllers with custom DMA interface"
+
+config PDC_ADMA
+       tristate "Pacific Digital ADMA support"
        depends on PCI
        help
-         This option enables support for Broadcom/Serverworks/Apple K2
-         SATA support.
+         This option enables support for Pacific Digital ADMA controllers
+
+         If unsure, say N.
+
+config PATA_MPC52xx
+       tristate "Freescale MPC52xx SoC internal IDE"
+       depends on PPC_MPC52xx && PPC_BESTCOMM
+       select PPC_BESTCOMM_ATA
+       help
+         This option enables support for integrated IDE controller
+         of the Freescale MPC52xx SoC.
+
+         If unsure, say N.
+
+config PATA_OCTEON_CF
+       tristate "OCTEON Boot Bus Compact Flash support"
+       depends on CPU_CAVIUM_OCTEON
+       help
+         This option enables a polled compact flash driver for use with
+         compact flash cards attached to the OCTEON boot bus.
+
+         If unsure, say N.
+
+config SATA_QSTOR
+       tristate "Pacific Digital SATA QStor support"
+       depends on PCI
+       help
+         This option enables support for Pacific Digital Serial ATA QStor.
+
+         If unsure, say N.
+
+config SATA_SX4
+       tristate "Promise SATA SX4 support (Experimental)"
+       depends on PCI && EXPERIMENTAL
+       help
+         This option enables support for Promise Serial ATA SX4.
 
          If unsure, say N.
 
+config ATA_BMDMA
+       bool "ATA BMDMA support"
+       default y
+       help
+         This option adds support for SFF ATA controllers with BMDMA
+         capability.  BMDMA stands for bus-master DMA and the
+         de-facto DMA interface for SFF controllers.
+
+         If unuser, say Y.
+
+if ATA_BMDMA
+
+comment "SATA SFF controllers with BMDMA"
+
 config ATA_PIIX
        tristate "Intel ESB, ICH, PIIX3, PIIX4 PATA/SATA support"
        depends on PCI
@@ -152,22 +204,6 @@ config SATA_NV
 
          If unsure, say N.
 
-config PDC_ADMA
-       tristate "Pacific Digital ADMA support"
-       depends on PCI
-       help
-         This option enables support for Pacific Digital ADMA controllers
-
-         If unsure, say N.
-
-config SATA_QSTOR
-       tristate "Pacific Digital SATA QStor support"
-       depends on PCI
-       help
-         This option enables support for Pacific Digital Serial ATA QStor.
-
-         If unsure, say N.
-
 config SATA_PROMISE
        tristate "Promise SATA TX2/TX4 support"
        depends on PCI
@@ -176,14 +212,6 @@ config SATA_PROMISE
 
          If unsure, say N.
 
-config SATA_SX4
-       tristate "Promise SATA SX4 support (Experimental)"
-       depends on PCI && EXPERIMENTAL
-       help
-         This option enables support for Promise Serial ATA SX4.
-
-         If unsure, say N.
-
 config SATA_SIL
        tristate "Silicon Image SATA support"
        depends on PCI
@@ -203,6 +231,15 @@ config SATA_SIS
          enable the PATA_SIS driver in the config.
          If unsure, say N.
 
+config SATA_SVW
+       tristate "ServerWorks Frodo / Apple K2 SATA support"
+       depends on PCI
+       help
+         This option enables support for Broadcom/Serverworks/Apple K2
+         SATA support.
+
+         If unsure, say N.
+
 config SATA_ULI
        tristate "ULi Electronics SATA support"
        depends on PCI
@@ -227,14 +264,7 @@ config SATA_VITESSE
 
          If unsure, say N.
 
-config PATA_ACPI
-       tristate "ACPI firmware driver for PATA"
-       depends on ATA_ACPI
-       help
-         This option enables an ACPI method driver which drives
-         motherboard PATA controller interfaces through the ACPI
-         firmware in the BIOS. This driver can sometimes handle
-         otherwise unsupported hardware.
+comment "PATA SFF controllers with BMDMA"
 
 config PATA_ALI
        tristate "ALi PATA support"
@@ -262,40 +292,30 @@ config PATA_ARTOP
 
          If unsure, say N.
 
-config PATA_ATP867X
-       tristate "ARTOP/Acard ATP867X PATA support"
+config PATA_ATIIXP
+       tristate "ATI PATA support"
        depends on PCI
        help
-         This option enables support for ARTOP/Acard ATP867X PATA
-         controllers.
-
-         If unsure, say N.
-
-config PATA_AT32
-       tristate "Atmel AVR32 PATA support (Experimental)"
-       depends on AVR32 && PLATFORM_AT32AP && EXPERIMENTAL
-       help
-         This option enables support for the IDE devices on the
-         Atmel AT32AP platform.
+         This option enables support for the ATI ATA interfaces
+         found on the many ATI chipsets.
 
          If unsure, say N.
 
-config PATA_ATIIXP
-       tristate "ATI PATA support"
+config PATA_ATP867X
+       tristate "ARTOP/Acard ATP867X PATA support"
        depends on PCI
        help
-         This option enables support for the ATI ATA interfaces
-         found on the many ATI chipsets.
+         This option enables support for ARTOP/Acard ATP867X PATA
+         controllers.
 
          If unsure, say N.
 
-config PATA_CMD640_PCI
-       tristate "CMD640 PCI PATA support (Experimental)"
-       depends on PCI && EXPERIMENTAL
+config PATA_BF54X
+       tristate "Blackfin 54x ATAPI support"
+       depends on BF542 || BF548 || BF549
        help
-         This option enables support for the CMD640 PCI IDE
-         interface chip. Only the primary channel is currently
-         supported.
+         This option enables support for the built-in ATAPI controller on
+         Blackfin 54x family chips.
 
          If unsure, say N.
 
@@ -362,15 +382,6 @@ config PATA_EFAR
 
          If unsure, say N.
 
-config ATA_GENERIC
-       tristate "Generic ATA support"
-       depends on PCI
-       help
-         This option enables support for generic BIOS configured
-         ATA controllers via the new ATA layer
-
-         If unsure, say N.
-
 config PATA_HPT366
        tristate "HPT 366/368 PATA support"
        depends on PCI
@@ -415,12 +426,20 @@ config PATA_HPT3X3_DMA
          controllers. Enable with care as there are still some
          problems with DMA on this chipset.
 
-config PATA_ISAPNP
-       tristate "ISA Plug and Play PATA support"
-       depends on ISAPNP
+config PATA_ICSIDE
+       tristate "Acorn ICS PATA support"
+       depends on ARM && ARCH_ACORN
        help
-         This option enables support for ISA plug & play ATA
-         controllers such as those found on old soundcards.
+         On Acorn systems, say Y here if you wish to use the ICS PATA
+         interface card.  This is not required for ICS partition support.
+         If you are unsure, say N to this.
+
+config PATA_IT8213
+       tristate "IT8213 PATA support (Experimental)"
+       depends on PCI && EXPERIMENTAL
+       help
+         This option enables support for the ITE 821 PATA
+          controllers via the new ATA layer.
 
          If unsure, say N.
 
@@ -434,15 +453,6 @@ config PATA_IT821X
 
          If unsure, say N.
 
-config PATA_IT8213
-       tristate "IT8213 PATA support (Experimental)"
-       depends on PCI && EXPERIMENTAL
-       help
-         This option enables support for the ITE 821 PATA
-          controllers via the new ATA layer.
-
-         If unsure, say N.
-
 config PATA_JMICRON
        tristate "JMicron PATA support"
        depends on PCI
@@ -452,23 +462,14 @@ config PATA_JMICRON
 
          If unsure, say N.
 
-config PATA_LEGACY
-       tristate "Legacy ISA PATA support (Experimental)"
-       depends on (ISA || PCI)  && EXPERIMENTAL
-       help
-         This option enables support for ISA/VLB/PCI bus legacy PATA
-         ports and allows them to be accessed via the new ATA layer.
-
-         If unsure, say N.
-
-config PATA_TRIFLEX
-       tristate "Compaq Triflex PATA support"
-       depends on PCI
+config PATA_MACIO
+       tristate "Apple PowerMac/PowerBook internal 'MacIO' IDE"
+       depends on PPC_PMAC
        help
-         Enable support for the Compaq 'Triflex' IDE controller as found
-         on many Compaq Pentium-Pro systems, via the new ATA layer.
-
-         If unsure, say N.
+         Most IDE capable PowerMacs have IDE busses driven by a variant
+          of this controller which is part of the Apple chipset used on
+          most PowerMac models. Some models have multiple busses using
+          different chipsets, though generally, MacIO is one of them.
 
 config PATA_MARVELL
        tristate "Marvell PATA support via legacy mode"
@@ -481,32 +482,6 @@ config PATA_MARVELL
 
          If unsure, say N.
 
-config PATA_MPC52xx
-       tristate "Freescale MPC52xx SoC internal IDE"
-       depends on PPC_MPC52xx && PPC_BESTCOMM
-       select PPC_BESTCOMM_ATA
-       help
-         This option enables support for integrated IDE controller
-         of the Freescale MPC52xx SoC.
-
-         If unsure, say N.
-
-config PATA_MPIIX
-       tristate "Intel PATA MPIIX support"
-       depends on PCI
-       help
-         This option enables support for MPIIX PATA support.
-
-         If unsure, say N.
-
-config PATA_OLDPIIX
-       tristate "Intel PATA old PIIX support"
-       depends on PCI
-       help
-         This option enables support for early PIIX PATA support.
-
-         If unsure, say N.
-
 config PATA_NETCELL
        tristate "NETCELL Revolution RAID support"
        depends on PCI
@@ -525,15 +500,6 @@ config PATA_NINJA32
 
          If unsure, say N.
 
-config PATA_NS87410
-       tristate "Nat Semi NS87410 PATA support"
-       depends on PCI
-       help
-         This option enables support for the National Semiconductor
-         NS87410 PCI-IDE controller.
-
-         If unsure, say N.
-
 config PATA_NS87415
        tristate "Nat Semi NS87415 PATA support"
        depends on PCI
@@ -543,12 +509,11 @@ config PATA_NS87415
 
          If unsure, say N.
 
-config PATA_OPTI
-       tristate "OPTI621/6215 PATA support (Very Experimental)"
-       depends on PCI && EXPERIMENTAL
+config PATA_OLDPIIX
+       tristate "Intel PATA old PIIX support"
+       depends on PCI
        help
-         This option enables full PIO support for the early Opti ATA
-         controllers found on some old motherboards.
+         This option enables support for early PIIX PATA support.
 
          If unsure, say N.
 
@@ -562,24 +527,6 @@ config PATA_OPTIDMA
 
          If unsure, say N.
 
-config PATA_PALMLD
-       tristate "Palm LifeDrive PATA support"
-       depends on MACH_PALMLD
-       help
-         This option enables support for Palm LifeDrive's internal ATA
-         port via the new ATA layer.
-
-         If unsure, say N.
-
-config PATA_PCMCIA
-       tristate "PCMCIA PATA support"
-       depends on PCMCIA
-       help
-         This option enables support for PCMCIA ATA interfaces, including
-         compact flash card adapters via the new ATA layer.
-
-         If unsure, say N.
-
 config PATA_PDC2027X
        tristate "Promise PATA 2027x support"
        depends on PCI
@@ -597,12 +544,6 @@ config PATA_PDC_OLD
 
          If unsure, say N.
 
-config PATA_QDI
-       tristate "QDI VLB PATA support"
-       depends on ISA
-       help
-         Support for QDI 6500 and 6580 PATA controllers on VESA local bus.
-
 config PATA_RADISYS
        tristate "RADISYS 82600 PATA support (Experimental)"
        depends on PCI && EXPERIMENTAL
@@ -612,15 +553,6 @@ config PATA_RADISYS
 
          If unsure, say N.
 
-config PATA_RB532
-       tristate "RouterBoard 532 PATA CompactFlash support"
-       depends on MIKROTIK_RB532
-       help
-         This option enables support for the RouterBoard 532
-         PATA CompactFlash controller.
-
-         If unsure, say N.
-
 config PATA_RDC
        tristate "RDC PATA support"
        depends on PCI
@@ -631,21 +563,30 @@ config PATA_RDC
 
          If unsure, say N.
 
-config PATA_RZ1000
-       tristate "PC Tech RZ1000 PATA support"
+config PATA_SC1200
+       tristate "SC1200 PATA support"
        depends on PCI
        help
-         This option enables basic support for the PC Tech RZ1000/1
-         PATA controllers via the new ATA layer
+         This option enables support for the NatSemi/AMD SC1200 SoC
+         companion chip used with the Geode processor family.
 
          If unsure, say N.
 
-config PATA_SC1200
-       tristate "SC1200 PATA support"
+config PATA_SCC
+       tristate "Toshiba's Cell Reference Set IDE support"
+       depends on PCI && PPC_CELLEB
+       help
+         This option enables support for the built-in IDE controller on
+         Toshiba Cell Reference Board.
+
+         If unsure, say N.
+
+config PATA_SCH
+       tristate "Intel SCH PATA support"
        depends on PCI
        help
-         This option enables support for the NatSemi/AMD SC1200 SoC
-         companion chip used with the Geode processor family.
+         This option enables support for Intel SCH PATA on the Intel
+         SCH (US15W, US15L, UL11L) series host controllers.
 
          If unsure, say N.
 
@@ -683,6 +624,15 @@ config PATA_TOSHIBA
 
          If unsure, say N.
 
+config PATA_TRIFLEX
+       tristate "Compaq Triflex PATA support"
+       depends on PCI
+       help
+         Enable support for the Compaq 'Triflex' IDE controller as found
+         on many Compaq Pentium-Pro systems, via the new ATA layer.
+
+         If unsure, say N.
+
 config PATA_VIA
        tristate "VIA PATA support"
        depends on PCI
@@ -701,12 +651,99 @@ config PATA_WINBOND
 
          If unsure, say N.
 
-config PATA_WINBOND_VLB
-       tristate "Winbond W83759A VLB PATA support (Experimental)"
-       depends on ISA && EXPERIMENTAL
+endif # ATA_BMDMA
+
+comment "PIO-only SFF controllers"
+
+config PATA_AT32
+       tristate "Atmel AVR32 PATA support (Experimental)"
+       depends on AVR32 && PLATFORM_AT32AP && EXPERIMENTAL
        help
-         Support for the Winbond W83759A controller on Vesa Local Bus
-         systems.
+         This option enables support for the IDE devices on the
+         Atmel AT32AP platform.
+
+         If unsure, say N.
+
+config PATA_AT91
+       tristate "PATA support for AT91SAM9260"
+       depends on ARM && ARCH_AT91
+       help
+         This option enables support for IDE devices on the Atmel AT91SAM9260 SoC.
+
+         If unsure, say N.
+
+config PATA_CMD640_PCI
+       tristate "CMD640 PCI PATA support (Experimental)"
+       depends on PCI && EXPERIMENTAL
+       help
+         This option enables support for the CMD640 PCI IDE
+         interface chip. Only the primary channel is currently
+         supported.
+
+         If unsure, say N.
+
+config PATA_ISAPNP
+       tristate "ISA Plug and Play PATA support"
+       depends on ISAPNP
+       help
+         This option enables support for ISA plug & play ATA
+         controllers such as those found on old soundcards.
+
+         If unsure, say N.
+
+config PATA_IXP4XX_CF
+       tristate "IXP4XX Compact Flash support"
+       depends on ARCH_IXP4XX
+       help
+         This option enables support for a Compact Flash connected on
+         the ixp4xx expansion bus. This driver had been written for
+         Loft/Avila boards in mind but can work with others.
+
+         If unsure, say N.
+
+config PATA_MPIIX
+       tristate "Intel PATA MPIIX support"
+       depends on PCI
+       help
+         This option enables support for MPIIX PATA support.
+
+         If unsure, say N.
+
+config PATA_NS87410
+       tristate "Nat Semi NS87410 PATA support"
+       depends on PCI
+       help
+         This option enables support for the National Semiconductor
+         NS87410 PCI-IDE controller.
+
+         If unsure, say N.
+
+config PATA_OPTI
+       tristate "OPTI621/6215 PATA support (Very Experimental)"
+       depends on PCI && EXPERIMENTAL
+       help
+         This option enables full PIO support for the early Opti ATA
+         controllers found on some old motherboards.
+
+         If unsure, say N.
+
+config PATA_PALMLD
+       tristate "Palm LifeDrive PATA support"
+       depends on MACH_PALMLD
+       help
+         This option enables support for Palm LifeDrive's internal ATA
+         port via the new ATA layer.
+
+         If unsure, say N.
+
+config PATA_PCMCIA
+       tristate "PCMCIA PATA support"
+       depends on PCMCIA
+       help
+         This option enables support for PCMCIA ATA interfaces, including
+         compact flash card adapters via the new ATA layer.
+
+         If unsure, say N.
 
 config HAVE_PATA_PLATFORM
        bool
@@ -725,14 +762,6 @@ config PATA_PLATFORM
 
          If unsure, say N.
 
-config PATA_AT91
-       tristate "PATA support for AT91SAM9260"
-       depends on ARM && ARCH_AT91
-       help
-         This option enables support for IDE devices on the Atmel AT91SAM9260 SoC.
-
-         If unsure, say N.
-
 config PATA_OF_PLATFORM
        tristate "OpenFirmware platform device PATA support"
        depends on PATA_PLATFORM && PPC_OF
@@ -743,69 +772,65 @@ config PATA_OF_PLATFORM
 
          If unsure, say N.
 
-config PATA_ICSIDE
-       tristate "Acorn ICS PATA support"
-       depends on ARM && ARCH_ACORN
+config PATA_QDI
+       tristate "QDI VLB PATA support"
+       depends on ISA
        help
-         On Acorn systems, say Y here if you wish to use the ICS PATA
-         interface card.  This is not required for ICS partition support.
-         If you are unsure, say N to this.
+         Support for QDI 6500 and 6580 PATA controllers on VESA local bus.
 
-config PATA_IXP4XX_CF
-       tristate "IXP4XX Compact Flash support"
-       depends on ARCH_IXP4XX
+config PATA_RB532
+       tristate "RouterBoard 532 PATA CompactFlash support"
+       depends on MIKROTIK_RB532
        help
-         This option enables support for a Compact Flash connected on
-         the ixp4xx expansion bus. This driver had been written for
-         Loft/Avila boards in mind but can work with others.
+         This option enables support for the RouterBoard 532
+         PATA CompactFlash controller.
 
          If unsure, say N.
 
-config PATA_OCTEON_CF
-       tristate "OCTEON Boot Bus Compact Flash support"
-       depends on CPU_CAVIUM_OCTEON
+config PATA_RZ1000
+       tristate "PC Tech RZ1000 PATA support"
+       depends on PCI
        help
-         This option enables a polled compact flash driver for use with
-         compact flash cards attached to the OCTEON boot bus.
+         This option enables basic support for the PC Tech RZ1000/1
+         PATA controllers via the new ATA layer
 
          If unsure, say N.
 
-config PATA_SCC
-       tristate "Toshiba's Cell Reference Set IDE support"
-       depends on PCI && PPC_CELLEB
+config PATA_WINBOND_VLB
+       tristate "Winbond W83759A VLB PATA support (Experimental)"
+       depends on ISA && EXPERIMENTAL
        help
-         This option enables support for the built-in IDE controller on
-         Toshiba Cell Reference Board.
+         Support for the Winbond W83759A controller on Vesa Local Bus
+         systems.
 
-         If unsure, say N.
+comment "Generic fallback / legacy drivers"
 
-config PATA_SCH
-       tristate "Intel SCH PATA support"
-       depends on PCI
+config PATA_ACPI
+       tristate "ACPI firmware driver for PATA"
+       depends on ATA_ACPI && ATA_BMDMA
        help
-         This option enables support for Intel SCH PATA on the Intel
-         SCH (US15W, US15L, UL11L) series host controllers.
-
-         If unsure, say N.
+         This option enables an ACPI method driver which drives
+         motherboard PATA controller interfaces through the ACPI
+         firmware in the BIOS. This driver can sometimes handle
+         otherwise unsupported hardware.
 
-config PATA_BF54X
-       tristate "Blackfin 54x ATAPI support"
-       depends on BF542 || BF548 || BF549
+config ATA_GENERIC
+       tristate "Generic ATA support"
+       depends on PCI && ATA_BMDMA
        help
-         This option enables support for the built-in ATAPI controller on
-         Blackfin 54x family chips.
+         This option enables support for generic BIOS configured
+         ATA controllers via the new ATA layer
 
          If unsure, say N.
 
-config PATA_MACIO
-       tristate "Apple PowerMac/PowerBook internal 'MacIO' IDE"
-       depends on PPC_PMAC
+config PATA_LEGACY
+       tristate "Legacy ISA PATA support (Experimental)"
+       depends on (ISA || PCI) && EXPERIMENTAL
        help
-         Most IDE capable PowerMacs have IDE busses driven by a variant
-          of this controller which is part of the Apple chipset used on
-          most PowerMac models. Some models have multiple busses using
-          different chipsets, though generally, MacIO is one of them.
+         This option enables support for ISA/VLB/PCI bus legacy PATA
+         ports and allows them to be accessed via the new ATA layer.
 
+         If unsure, say N.
 
 endif # ATA_SFF
 endif # ATA
index d0a93c4..7ef89d7 100644 (file)
@@ -1,33 +1,39 @@
 
 obj-$(CONFIG_ATA)              += libata.o
 
+# non-SFF interface
 obj-$(CONFIG_SATA_AHCI)                += ahci.o libahci.o
 obj-$(CONFIG_SATA_AHCI_PLATFORM) += ahci_platform.o libahci.o
-obj-$(CONFIG_SATA_SVW)         += sata_svw.o
+obj-$(CONFIG_SATA_FSL)         += sata_fsl.o
+obj-$(CONFIG_SATA_INIC162X)    += sata_inic162x.o
+obj-$(CONFIG_SATA_SIL24)       += sata_sil24.o
+
+# SFF w/ custom DMA
+obj-$(CONFIG_PDC_ADMA)         += pdc_adma.o
+obj-$(CONFIG_PATA_MPC52xx)     += pata_mpc52xx.o
+obj-$(CONFIG_PATA_OCTEON_CF)   += pata_octeon_cf.o
+obj-$(CONFIG_SATA_QSTOR)       += sata_qstor.o
+obj-$(CONFIG_SATA_SX4)         += sata_sx4.o
+
+# SFF SATA w/ BMDMA
 obj-$(CONFIG_ATA_PIIX)         += ata_piix.o
+obj-$(CONFIG_SATA_MV)          += sata_mv.o
+obj-$(CONFIG_SATA_NV)          += sata_nv.o
 obj-$(CONFIG_SATA_PROMISE)     += sata_promise.o
-obj-$(CONFIG_SATA_QSTOR)       += sata_qstor.o
 obj-$(CONFIG_SATA_SIL)         += sata_sil.o
-obj-$(CONFIG_SATA_SIL24)       += sata_sil24.o
-obj-$(CONFIG_SATA_VIA)         += sata_via.o
-obj-$(CONFIG_SATA_VITESSE)     += sata_vsc.o
 obj-$(CONFIG_SATA_SIS)         += sata_sis.o
-obj-$(CONFIG_SATA_SX4)         += sata_sx4.o
-obj-$(CONFIG_SATA_NV)          += sata_nv.o
+obj-$(CONFIG_SATA_SVW)         += sata_svw.o
 obj-$(CONFIG_SATA_ULI)         += sata_uli.o
-obj-$(CONFIG_SATA_MV)          += sata_mv.o
-obj-$(CONFIG_SATA_INIC162X)    += sata_inic162x.o
-obj-$(CONFIG_PDC_ADMA)         += pdc_adma.o
-obj-$(CONFIG_SATA_FSL)         += sata_fsl.o
-obj-$(CONFIG_PATA_MACIO)       += pata_macio.o
+obj-$(CONFIG_SATA_VIA)         += sata_via.o
+obj-$(CONFIG_SATA_VITESSE)     += sata_vsc.o
 
+# SFF PATA w/ BMDMA
 obj-$(CONFIG_PATA_ALI)         += pata_ali.o
 obj-$(CONFIG_PATA_AMD)         += pata_amd.o
 obj-$(CONFIG_PATA_ARTOP)       += pata_artop.o
-obj-$(CONFIG_PATA_ATP867X)     += pata_atp867x.o
-obj-$(CONFIG_PATA_AT32)                += pata_at32.o
 obj-$(CONFIG_PATA_ATIIXP)      += pata_atiixp.o
-obj-$(CONFIG_PATA_CMD640_PCI)  += pata_cmd640.o
+obj-$(CONFIG_PATA_ATP867X)     += pata_atp867x.o
+obj-$(CONFIG_PATA_BF54X)       += pata_bf54x.o
 obj-$(CONFIG_PATA_CMD64X)      += pata_cmd64x.o
 obj-$(CONFIG_PATA_CS5520)      += pata_cs5520.o
 obj-$(CONFIG_PATA_CS5530)      += pata_cs5530.o
@@ -39,47 +45,50 @@ obj-$(CONFIG_PATA_HPT366)   += pata_hpt366.o
 obj-$(CONFIG_PATA_HPT37X)      += pata_hpt37x.o
 obj-$(CONFIG_PATA_HPT3X2N)     += pata_hpt3x2n.o
 obj-$(CONFIG_PATA_HPT3X3)      += pata_hpt3x3.o
-obj-$(CONFIG_PATA_ISAPNP)      += pata_isapnp.o
-obj-$(CONFIG_PATA_IT821X)      += pata_it821x.o
+obj-$(CONFIG_PATA_ICSIDE)      += pata_icside.o
 obj-$(CONFIG_PATA_IT8213)      += pata_it8213.o
+obj-$(CONFIG_PATA_IT821X)      += pata_it821x.o
 obj-$(CONFIG_PATA_JMICRON)     += pata_jmicron.o
+obj-$(CONFIG_PATA_MACIO)       += pata_macio.o
+obj-$(CONFIG_PATA_MARVELL)     += pata_marvell.o
 obj-$(CONFIG_PATA_NETCELL)     += pata_netcell.o
 obj-$(CONFIG_PATA_NINJA32)     += pata_ninja32.o
-obj-$(CONFIG_PATA_NS87410)     += pata_ns87410.o
 obj-$(CONFIG_PATA_NS87415)     += pata_ns87415.o
-obj-$(CONFIG_PATA_OPTI)                += pata_opti.o
-obj-$(CONFIG_PATA_OPTIDMA)     += pata_optidma.o
-obj-$(CONFIG_PATA_MPC52xx)     += pata_mpc52xx.o
-obj-$(CONFIG_PATA_MARVELL)     += pata_marvell.o
-obj-$(CONFIG_PATA_MPIIX)       += pata_mpiix.o
 obj-$(CONFIG_PATA_OLDPIIX)     += pata_oldpiix.o
-obj-$(CONFIG_PATA_PALMLD)      += pata_palmld.o
-obj-$(CONFIG_PATA_PCMCIA)      += pata_pcmcia.o
+obj-$(CONFIG_PATA_OPTIDMA)     += pata_optidma.o
 obj-$(CONFIG_PATA_PDC2027X)    += pata_pdc2027x.o
 obj-$(CONFIG_PATA_PDC_OLD)     += pata_pdc202xx_old.o
-obj-$(CONFIG_PATA_QDI)         += pata_qdi.o
 obj-$(CONFIG_PATA_RADISYS)     += pata_radisys.o
-obj-$(CONFIG_PATA_RB532)       += pata_rb532_cf.o
 obj-$(CONFIG_PATA_RDC)         += pata_rdc.o
-obj-$(CONFIG_PATA_RZ1000)      += pata_rz1000.o
 obj-$(CONFIG_PATA_SC1200)      += pata_sc1200.o
+obj-$(CONFIG_PATA_SCC)         += pata_scc.o
+obj-$(CONFIG_PATA_SCH)         += pata_sch.o
 obj-$(CONFIG_PATA_SERVERWORKS) += pata_serverworks.o
 obj-$(CONFIG_PATA_SIL680)      += pata_sil680.o
+obj-$(CONFIG_PATA_SIS)         += pata_sis.o
 obj-$(CONFIG_PATA_TOSHIBA)     += pata_piccolo.o
+obj-$(CONFIG_PATA_TRIFLEX)     += pata_triflex.o
 obj-$(CONFIG_PATA_VIA)         += pata_via.o
 obj-$(CONFIG_PATA_WINBOND)     += pata_sl82c105.o
-obj-$(CONFIG_PATA_WINBOND_VLB) += pata_winbond.o
-obj-$(CONFIG_PATA_SIS)         += pata_sis.o
-obj-$(CONFIG_PATA_TRIFLEX)     += pata_triflex.o
+
+# SFF PIO only
+obj-$(CONFIG_PATA_AT32)                += pata_at32.o
+obj-$(CONFIG_PATA_AT91)                += pata_at91.o
+obj-$(CONFIG_PATA_CMD640_PCI)  += pata_cmd640.o
+obj-$(CONFIG_PATA_ISAPNP)      += pata_isapnp.o
 obj-$(CONFIG_PATA_IXP4XX_CF)   += pata_ixp4xx_cf.o
-obj-$(CONFIG_PATA_SCC)         += pata_scc.o
-obj-$(CONFIG_PATA_SCH)         += pata_sch.o
-obj-$(CONFIG_PATA_BF54X)       += pata_bf54x.o
-obj-$(CONFIG_PATA_OCTEON_CF)   += pata_octeon_cf.o
+obj-$(CONFIG_PATA_MPIIX)       += pata_mpiix.o
+obj-$(CONFIG_PATA_NS87410)     += pata_ns87410.o
+obj-$(CONFIG_PATA_OPTI)                += pata_opti.o
+obj-$(CONFIG_PATA_PCMCIA)      += pata_pcmcia.o
+obj-$(CONFIG_PATA_PALMLD)      += pata_palmld.o
 obj-$(CONFIG_PATA_PLATFORM)    += pata_platform.o
-obj-$(CONFIG_PATA_AT91)        += pata_at91.o
 obj-$(CONFIG_PATA_OF_PLATFORM) += pata_of_platform.o
-obj-$(CONFIG_PATA_ICSIDE)      += pata_icside.o
+obj-$(CONFIG_PATA_QDI)         += pata_qdi.o
+obj-$(CONFIG_PATA_RB532)       += pata_rb532_cf.o
+obj-$(CONFIG_PATA_RZ1000)      += pata_rz1000.o
+obj-$(CONFIG_PATA_WINBOND_VLB) += pata_winbond.o
+
 # Should be last but two libata driver
 obj-$(CONFIG_PATA_ACPI)                += pata_acpi.o
 # Should be last but one libata driver
index 33fb614..573158a 100644 (file)
@@ -155,7 +155,7 @@ static int ata_generic_init_one(struct pci_dev *dev, const struct pci_device_id
                        return rc;
                pcim_pin_device(dev);
        }
-       return ata_pci_sff_init_one(dev, ppi, &generic_sht, NULL, 0);
+       return ata_pci_bmdma_init_one(dev, ppi, &generic_sht, NULL, 0);
 }
 
 static struct pci_device_id ata_generic[] = {
index ec52fc6..7409f98 100644 (file)
@@ -1589,7 +1589,7 @@ static int __devinit piix_init_one(struct pci_dev *pdev,
                hpriv->map = piix_init_sata_map(pdev, port_info,
                                        piix_map_db_table[ent->driver_data]);
 
-       rc = ata_pci_sff_prepare_host(pdev, ppi, &host);
+       rc = ata_pci_bmdma_prepare_host(pdev, ppi, &host);
        if (rc)
                return rc;
        host->private_data = hpriv;
@@ -1626,7 +1626,7 @@ static int __devinit piix_init_one(struct pci_dev *pdev,
        host->flags |= ATA_HOST_PARALLEL_SCAN;
 
        pci_set_master(pdev);
-       return ata_pci_sff_activate_host(host, ata_sff_interrupt, &piix_sht);
+       return ata_pci_sff_activate_host(host, ata_bmdma_interrupt, &piix_sht);
 }
 
 static void piix_remove_one(struct pci_dev *pdev)
index c47373f..06b7e49 100644 (file)
@@ -160,6 +160,10 @@ int libata_allow_tpm = 0;
 module_param_named(allow_tpm, libata_allow_tpm, int, 0444);
 MODULE_PARM_DESC(allow_tpm, "Permit the use of TPM commands (0=off [default], 1=on)");
 
+static int atapi_an;
+module_param(atapi_an, int, 0444);
+MODULE_PARM_DESC(atapi_an, "Enable ATAPI AN media presence notification (0=0ff [default], 1=on)");
+
 MODULE_AUTHOR("Jeff Garzik");
 MODULE_DESCRIPTION("Library module for ATA devices");
 MODULE_LICENSE("GPL");
@@ -2122,6 +2126,14 @@ retry:
                goto err_out;
        }
 
+       if (dev->horkage & ATA_HORKAGE_DUMP_ID) {
+               ata_dev_printk(dev, KERN_DEBUG, "dumping IDENTIFY data, "
+                              "class=%d may_fallback=%d tried_spinup=%d\n",
+                              class, may_fallback, tried_spinup);
+               print_hex_dump(KERN_DEBUG, "", DUMP_PREFIX_OFFSET,
+                              16, 2, id, ATA_ID_WORDS * sizeof(*id), true);
+       }
+
        /* Falling back doesn't make sense if ID data was read
         * successfully at least once.
         */
@@ -2510,7 +2522,8 @@ int ata_dev_configure(struct ata_device *dev)
                 * to enable ATAPI AN to discern between PHY status
                 * changed notifications and ATAPI ANs.
                 */
-               if ((ap->flags & ATA_FLAG_AN) && ata_id_has_atapi_AN(id) &&
+               if (atapi_an &&
+                   (ap->flags & ATA_FLAG_AN) && ata_id_has_atapi_AN(id) &&
                    (!sata_pmp_attached(ap) ||
                     sata_scr_read(&ap->link, SCR_NOTIFICATION, &sntf) == 0)) {
                        unsigned int err_mask;
@@ -6372,6 +6385,7 @@ static int __init ata_parse_force_one(char **cur,
                { "3.0Gbps",    .spd_limit      = 2 },
                { "noncq",      .horkage_on     = ATA_HORKAGE_NONCQ },
                { "ncq",        .horkage_off    = ATA_HORKAGE_NONCQ },
+               { "dump_id",    .horkage_on     = ATA_HORKAGE_DUMP_ID },
                { "pio0",       .xfer_mask      = 1 << (ATA_SHIFT_PIO + 0) },
                { "pio1",       .xfer_mask      = 1 << (ATA_SHIFT_PIO + 1) },
                { "pio2",       .xfer_mask      = 1 << (ATA_SHIFT_PIO + 2) },
index 19ddf92..efa4a18 100644 (file)
@@ -63,7 +63,6 @@ const struct ata_port_operations ata_sff_port_ops = {
        .sff_tf_read            = ata_sff_tf_read,
        .sff_exec_command       = ata_sff_exec_command,
        .sff_data_xfer          = ata_sff_data_xfer,
-       .sff_irq_clear          = ata_sff_irq_clear,
        .sff_drain_fifo         = ata_sff_drain_fifo,
 
        .lost_interrupt         = ata_sff_lost_interrupt,
@@ -395,33 +394,12 @@ void ata_sff_irq_on(struct ata_port *ap)
                ata_sff_set_devctl(ap, ap->ctl);
        ata_wait_idle(ap);
 
-       ap->ops->sff_irq_clear(ap);
+       if (ap->ops->sff_irq_clear)
+               ap->ops->sff_irq_clear(ap);
 }
 EXPORT_SYMBOL_GPL(ata_sff_irq_on);
 
 /**
- *     ata_sff_irq_clear - Clear PCI IDE BMDMA interrupt.
- *     @ap: Port associated with this ATA transaction.
- *
- *     Clear interrupt and error flags in DMA status register.
- *
- *     May be used as the irq_clear() entry in ata_port_operations.
- *
- *     LOCKING:
- *     spin_lock_irqsave(host lock)
- */
-void ata_sff_irq_clear(struct ata_port *ap)
-{
-       void __iomem *mmio = ap->ioaddr.bmdma_addr;
-
-       if (!mmio)
-               return;
-
-       iowrite8(ioread8(mmio + ATA_DMA_STATUS), mmio + ATA_DMA_STATUS);
-}
-EXPORT_SYMBOL_GPL(ata_sff_irq_clear);
-
-/**
  *     ata_sff_tf_load - send taskfile registers to host controller
  *     @ap: Port to which output is sent
  *     @tf: ATA taskfile register set
@@ -820,11 +798,15 @@ static void atapi_send_cdb(struct ata_port *ap, struct ata_queued_cmd *qc)
        case ATAPI_PROT_NODATA:
                ap->hsm_task_state = HSM_ST_LAST;
                break;
+#ifdef CONFIG_ATA_BMDMA
        case ATAPI_PROT_DMA:
                ap->hsm_task_state = HSM_ST_LAST;
                /* initiate bmdma */
                ap->ops->bmdma_start(qc);
                break;
+#endif /* CONFIG_ATA_BMDMA */
+       default:
+               BUG();
        }
 }
 
@@ -1491,27 +1473,27 @@ bool ata_sff_qc_fill_rtf(struct ata_queued_cmd *qc)
 }
 EXPORT_SYMBOL_GPL(ata_sff_qc_fill_rtf);
 
-/**
- *     ata_sff_host_intr - Handle host interrupt for given (port, task)
- *     @ap: Port on which interrupt arrived (possibly...)
- *     @qc: Taskfile currently active in engine
- *
- *     Handle host interrupt for given queued command.  Currently,
- *     only DMA interrupts are handled.  All other commands are
- *     handled via polling with interrupts disabled (nIEN bit).
- *
- *     LOCKING:
- *     spin_lock_irqsave(host lock)
- *
- *     RETURNS:
- *     One if interrupt was handled, zero if not (shared irq).
- */
-unsigned int ata_sff_host_intr(struct ata_port *ap,
-                                     struct ata_queued_cmd *qc)
+static unsigned int ata_sff_idle_irq(struct ata_port *ap)
 {
-       struct ata_eh_info *ehi = &ap->link.eh_info;
-       u8 status, host_stat = 0;
-       bool bmdma_stopped = false;
+       ap->stats.idle_irq++;
+
+#ifdef ATA_IRQ_TRAP
+       if ((ap->stats.idle_irq % 1000) == 0) {
+               ap->ops->sff_check_status(ap);
+               if (ap->ops->sff_irq_clear)
+                       ap->ops->sff_irq_clear(ap);
+               ata_port_printk(ap, KERN_WARNING, "irq trap\n");
+               return 1;
+       }
+#endif
+       return 0;       /* irq not handled */
+}
+
+static unsigned int __ata_sff_port_intr(struct ata_port *ap,
+                                       struct ata_queued_cmd *qc,
+                                       bool hsmv_on_idle)
+{
+       u8 status;
 
        VPRINTK("ata%u: protocol %d task_state %d\n",
                ap->print_id, qc->tf.protocol, ap->hsm_task_state);
@@ -1528,90 +1510,56 @@ unsigned int ata_sff_host_intr(struct ata_port *ap,
                 * need to check ata_is_atapi(qc->tf.protocol) again.
                 */
                if (!(qc->dev->flags & ATA_DFLAG_CDB_INTR))
-                       goto idle_irq;
-               break;
-       case HSM_ST_LAST:
-               if (qc->tf.protocol == ATA_PROT_DMA ||
-                   qc->tf.protocol == ATAPI_PROT_DMA) {
-                       /* check status of DMA engine */
-                       host_stat = ap->ops->bmdma_status(ap);
-                       VPRINTK("ata%u: host_stat 0x%X\n",
-                               ap->print_id, host_stat);
-
-                       /* if it's not our irq... */
-                       if (!(host_stat & ATA_DMA_INTR))
-                               goto idle_irq;
-
-                       /* before we do anything else, clear DMA-Start bit */
-                       ap->ops->bmdma_stop(qc);
-                       bmdma_stopped = true;
-
-                       if (unlikely(host_stat & ATA_DMA_ERR)) {
-                               /* error when transfering data to/from memory */
-                               qc->err_mask |= AC_ERR_HOST_BUS;
-                               ap->hsm_task_state = HSM_ST_ERR;
-                       }
-               }
+                       return ata_sff_idle_irq(ap);
                break;
        case HSM_ST:
+       case HSM_ST_LAST:
                break;
        default:
-               goto idle_irq;
+               return ata_sff_idle_irq(ap);
        }
 
-
        /* check main status, clearing INTRQ if needed */
        status = ata_sff_irq_status(ap);
        if (status & ATA_BUSY) {
-               if (bmdma_stopped) {
+               if (hsmv_on_idle) {
                        /* BMDMA engine is already stopped, we're screwed */
                        qc->err_mask |= AC_ERR_HSM;
                        ap->hsm_task_state = HSM_ST_ERR;
                } else
-                       goto idle_irq;
+                       return ata_sff_idle_irq(ap);
        }
 
        /* clear irq events */
-       ap->ops->sff_irq_clear(ap);
+       if (ap->ops->sff_irq_clear)
+               ap->ops->sff_irq_clear(ap);
 
        ata_sff_hsm_move(ap, qc, status, 0);
 
-       if (unlikely(qc->err_mask) && (qc->tf.protocol == ATA_PROT_DMA ||
-                                      qc->tf.protocol == ATAPI_PROT_DMA))
-               ata_ehi_push_desc(ehi, "BMDMA stat 0x%x", host_stat);
-
        return 1;       /* irq handled */
-
-idle_irq:
-       ap->stats.idle_irq++;
-
-#ifdef ATA_IRQ_TRAP
-       if ((ap->stats.idle_irq % 1000) == 0) {
-               ap->ops->sff_check_status(ap);
-               ap->ops->sff_irq_clear(ap);
-               ata_port_printk(ap, KERN_WARNING, "irq trap\n");
-               return 1;
-       }
-#endif
-       return 0;       /* irq not handled */
 }
-EXPORT_SYMBOL_GPL(ata_sff_host_intr);
 
 /**
- *     ata_sff_interrupt - Default ATA host interrupt handler
- *     @irq: irq line (unused)
- *     @dev_instance: pointer to our ata_host information structure
+ *     ata_sff_port_intr - Handle SFF port interrupt
+ *     @ap: Port on which interrupt arrived (possibly...)
+ *     @qc: Taskfile currently active in engine
  *
- *     Default interrupt handler for PCI IDE devices.  Calls
- *     ata_sff_host_intr() for each port that is not disabled.
+ *     Handle port interrupt for given queued command.
  *
  *     LOCKING:
- *     Obtains host lock during operation.
+ *     spin_lock_irqsave(host lock)
  *
  *     RETURNS:
- *     IRQ_NONE or IRQ_HANDLED.
+ *     One if interrupt was handled, zero if not (shared irq).
  */
-irqreturn_t ata_sff_interrupt(int irq, void *dev_instance)
+unsigned int ata_sff_port_intr(struct ata_port *ap, struct ata_queued_cmd *qc)
+{
+       return __ata_sff_port_intr(ap, qc, false);
+}
+EXPORT_SYMBOL_GPL(ata_sff_port_intr);
+
+static inline irqreturn_t __ata_sff_interrupt(int irq, void *dev_instance,
+       unsigned int (*port_intr)(struct ata_port *, struct ata_queued_cmd *))
 {
        struct ata_host *host = dev_instance;
        bool retried = false;
@@ -1631,7 +1579,7 @@ retry:
                qc = ata_qc_from_tag(ap, ap->link.active_tag);
                if (qc) {
                        if (!(qc->tf.flags & ATA_TFLAG_POLLING))
-                               handled |= ata_sff_host_intr(ap, qc);
+                               handled |= port_intr(ap, qc);
                        else
                                polling |= 1 << i;
                } else
@@ -1658,7 +1606,8 @@ retry:
 
                        if (idle & (1 << i)) {
                                ap->ops->sff_check_status(ap);
-                               ap->ops->sff_irq_clear(ap);
+                               if (ap->ops->sff_irq_clear)
+                                       ap->ops->sff_irq_clear(ap);
                        } else {
                                /* clear INTRQ and check if BUSY cleared */
                                if (!(ap->ops->sff_check_status(ap) & ATA_BUSY))
@@ -1680,6 +1629,25 @@ retry:
 
        return IRQ_RETVAL(handled);
 }
+
+/**
+ *     ata_sff_interrupt - Default SFF ATA host interrupt handler
+ *     @irq: irq line (unused)
+ *     @dev_instance: pointer to our ata_host information structure
+ *
+ *     Default interrupt handler for PCI IDE devices.  Calls
+ *     ata_sff_port_intr() for each port that is not disabled.
+ *
+ *     LOCKING:
+ *     Obtains host lock during operation.
+ *
+ *     RETURNS:
+ *     IRQ_NONE or IRQ_HANDLED.
+ */
+irqreturn_t ata_sff_interrupt(int irq, void *dev_instance)
+{
+       return __ata_sff_interrupt(irq, dev_instance, ata_sff_port_intr);
+}
 EXPORT_SYMBOL_GPL(ata_sff_interrupt);
 
 /**
@@ -1717,7 +1685,7 @@ void ata_sff_lost_interrupt(struct ata_port *ap)
                                                                status);
        /* Run the host interrupt logic as if the interrupt had not been
           lost */
-       ata_sff_host_intr(ap, qc);
+       ata_sff_port_intr(ap, qc);
 }
 EXPORT_SYMBOL_GPL(ata_sff_lost_interrupt);
 
@@ -1744,7 +1712,8 @@ void ata_sff_freeze(struct ata_port *ap)
         */
        ap->ops->sff_check_status(ap);
 
-       ap->ops->sff_irq_clear(ap);
+       if (ap->ops->sff_irq_clear)
+               ap->ops->sff_irq_clear(ap);
 }
 EXPORT_SYMBOL_GPL(ata_sff_freeze);
 
@@ -1761,7 +1730,8 @@ void ata_sff_thaw(struct ata_port *ap)
 {
        /* clear & re-enable interrupts */
        ap->ops->sff_check_status(ap);
-       ap->ops->sff_irq_clear(ap);
+       if (ap->ops->sff_irq_clear)
+               ap->ops->sff_irq_clear(ap);
        ata_sff_irq_on(ap);
 }
 EXPORT_SYMBOL_GPL(ata_sff_thaw);
@@ -2349,13 +2319,13 @@ int ata_pci_sff_init_host(struct ata_host *host)
 EXPORT_SYMBOL_GPL(ata_pci_sff_init_host);
 
 /**
- *     ata_pci_sff_prepare_host - helper to prepare native PCI ATA host
+ *     ata_pci_sff_prepare_host - helper to prepare PCI PIO-only SFF ATA host
  *     @pdev: target PCI device
  *     @ppi: array of port_info, must be enough for two ports
  *     @r_host: out argument for the initialized ATA host
  *
- *     Helper to allocate ATA host for @pdev, acquire all native PCI
- *     resources and initialize it accordingly in one go.
+ *     Helper to allocate PIO-only SFF ATA host for @pdev, acquire
+ *     all PCI resources and initialize it accordingly in one go.
  *
  *     LOCKING:
  *     Inherited from calling layer (may sleep).
@@ -2385,9 +2355,6 @@ int ata_pci_sff_prepare_host(struct pci_dev *pdev,
        if (rc)
                goto err_out;
 
-       /* init DMA related stuff */
-       ata_pci_bmdma_init(host);
-
        devres_remove_group(&pdev->dev, NULL);
        *r_host = host;
        return 0;
@@ -2492,8 +2459,21 @@ out:
 }
 EXPORT_SYMBOL_GPL(ata_pci_sff_activate_host);
 
+static const struct ata_port_info *ata_sff_find_valid_pi(
+                                       const struct ata_port_info * const *ppi)
+{
+       int i;
+
+       /* look up the first valid port_info */
+       for (i = 0; i < 2 && ppi[i]; i++)
+               if (ppi[i]->port_ops != &ata_dummy_port_ops)
+                       return ppi[i];
+
+       return NULL;
+}
+
 /**
- *     ata_pci_sff_init_one - Initialize/register PCI IDE host controller
+ *     ata_pci_sff_init_one - Initialize/register PIO-only PCI IDE controller
  *     @pdev: Controller to be initialized
  *     @ppi: array of port_info, must be enough for two ports
  *     @sht: scsi_host_template to use when registering the host
@@ -2502,11 +2482,7 @@ EXPORT_SYMBOL_GPL(ata_pci_sff_activate_host);
  *
  *     This is a helper function which can be called from a driver's
  *     xxx_init_one() probe function if the hardware uses traditional
- *     IDE taskfile registers.
- *
- *     This function calls pci_enable_device(), reserves its register
- *     regions, sets the dma mask, enables bus master mode, and calls
- *     ata_device_add()
+ *     IDE taskfile registers and is PIO only.
  *
  *     ASSUMPTION:
  *     Nobody makes a single channel controller that appears solely as
@@ -2523,20 +2499,13 @@ int ata_pci_sff_init_one(struct pci_dev *pdev,
                 struct scsi_host_template *sht, void *host_priv, int hflag)
 {
        struct device *dev = &pdev->dev;
-       const struct ata_port_info *pi = NULL;
+       const struct ata_port_info *pi;
        struct ata_host *host = NULL;
-       int i, rc;
+       int rc;
 
        DPRINTK("ENTER\n");
 
-       /* look up the first valid port_info */
-       for (i = 0; i < 2 && ppi[i]; i++) {
-               if (ppi[i]->port_ops != &ata_dummy_port_ops) {
-                       pi = ppi[i];
-                       break;
-               }
-       }
-
+       pi = ata_sff_find_valid_pi(ppi);
        if (!pi) {
                dev_printk(KERN_ERR, &pdev->dev,
                           "no valid port_info specified\n");
@@ -2557,7 +2526,6 @@ int ata_pci_sff_init_one(struct pci_dev *pdev,
        host->private_data = host_priv;
        host->flags |= hflag;
 
-       pci_set_master(pdev);
        rc = ata_pci_sff_activate_host(host, ata_sff_interrupt, sht);
 out:
        if (rc == 0)
@@ -2571,6 +2539,12 @@ EXPORT_SYMBOL_GPL(ata_pci_sff_init_one);
 
 #endif /* CONFIG_PCI */
 
+/*
+ *     BMDMA support
+ */
+
+#ifdef CONFIG_ATA_BMDMA
+
 const struct ata_port_operations ata_bmdma_port_ops = {
        .inherits               = &ata_sff_port_ops,
 
@@ -2580,6 +2554,7 @@ const struct ata_port_operations ata_bmdma_port_ops = {
        .qc_prep                = ata_bmdma_qc_prep,
        .qc_issue               = ata_bmdma_qc_issue,
 
+       .sff_irq_clear          = ata_bmdma_irq_clear,
        .bmdma_setup            = ata_bmdma_setup,
        .bmdma_start            = ata_bmdma_start,
        .bmdma_stop             = ata_bmdma_stop,
@@ -2804,6 +2779,75 @@ unsigned int ata_bmdma_qc_issue(struct ata_queued_cmd *qc)
 EXPORT_SYMBOL_GPL(ata_bmdma_qc_issue);
 
 /**
+ *     ata_bmdma_port_intr - Handle BMDMA port interrupt
+ *     @ap: Port on which interrupt arrived (possibly...)
+ *     @qc: Taskfile currently active in engine
+ *
+ *     Handle port interrupt for given queued command.
+ *
+ *     LOCKING:
+ *     spin_lock_irqsave(host lock)
+ *
+ *     RETURNS:
+ *     One if interrupt was handled, zero if not (shared irq).
+ */
+unsigned int ata_bmdma_port_intr(struct ata_port *ap, struct ata_queued_cmd *qc)
+{
+       struct ata_eh_info *ehi = &ap->link.eh_info;
+       u8 host_stat = 0;
+       bool bmdma_stopped = false;
+       unsigned int handled;
+
+       if (ap->hsm_task_state == HSM_ST_LAST && ata_is_dma(qc->tf.protocol)) {
+               /* check status of DMA engine */
+               host_stat = ap->ops->bmdma_status(ap);
+               VPRINTK("ata%u: host_stat 0x%X\n", ap->print_id, host_stat);
+
+               /* if it's not our irq... */
+               if (!(host_stat & ATA_DMA_INTR))
+                       return ata_sff_idle_irq(ap);
+
+               /* before we do anything else, clear DMA-Start bit */
+               ap->ops->bmdma_stop(qc);
+               bmdma_stopped = true;
+
+               if (unlikely(host_stat & ATA_DMA_ERR)) {
+                       /* error when transfering data to/from memory */
+                       qc->err_mask |= AC_ERR_HOST_BUS;
+                       ap->hsm_task_state = HSM_ST_ERR;
+               }
+       }
+
+       handled = __ata_sff_port_intr(ap, qc, bmdma_stopped);
+
+       if (unlikely(qc->err_mask) && ata_is_dma(qc->tf.protocol))
+               ata_ehi_push_desc(ehi, "BMDMA stat 0x%x", host_stat);
+
+       return handled;
+}
+EXPORT_SYMBOL_GPL(ata_bmdma_port_intr);
+
+/**
+ *     ata_bmdma_interrupt - Default BMDMA ATA host interrupt handler
+ *     @irq: irq line (unused)
+ *     @dev_instance: pointer to our ata_host information structure
+ *
+ *     Default interrupt handler for PCI IDE devices.  Calls
+ *     ata_bmdma_port_intr() for each port that is not disabled.
+ *
+ *     LOCKING:
+ *     Obtains host lock during operation.
+ *
+ *     RETURNS:
+ *     IRQ_NONE or IRQ_HANDLED.
+ */
+irqreturn_t ata_bmdma_interrupt(int irq, void *dev_instance)
+{
+       return __ata_sff_interrupt(irq, dev_instance, ata_bmdma_port_intr);
+}
+EXPORT_SYMBOL_GPL(ata_bmdma_interrupt);
+
+/**
  *     ata_bmdma_error_handler - Stock error handler for BMDMA controller
  *     @ap: port to handle error for
  *
@@ -2848,7 +2892,8 @@ void ata_bmdma_error_handler(struct ata_port *ap)
                /* if we're gonna thaw, make sure IRQ is clear */
                if (thaw) {
                        ap->ops->sff_check_status(ap);
-                       ap->ops->sff_irq_clear(ap);
+                       if (ap->ops->sff_irq_clear)
+                               ap->ops->sff_irq_clear(ap);
                }
        }
 
@@ -2882,6 +2927,28 @@ void ata_bmdma_post_internal_cmd(struct ata_queued_cmd *qc)
 EXPORT_SYMBOL_GPL(ata_bmdma_post_internal_cmd);
 
 /**
+ *     ata_bmdma_irq_clear - Clear PCI IDE BMDMA interrupt.
+ *     @ap: Port associated with this ATA transaction.
+ *
+ *     Clear interrupt and error flags in DMA status register.
+ *
+ *     May be used as the irq_clear() entry in ata_port_operations.
+ *
+ *     LOCKING:
+ *     spin_lock_irqsave(host lock)
+ */
+void ata_bmdma_irq_clear(struct ata_port *ap)
+{
+       void __iomem *mmio = ap->ioaddr.bmdma_addr;
+
+       if (!mmio)
+               return;
+
+       iowrite8(ioread8(mmio + ATA_DMA_STATUS), mmio + ATA_DMA_STATUS);
+}
+EXPORT_SYMBOL_GPL(ata_bmdma_irq_clear);
+
+/**
  *     ata_bmdma_setup - Set up PCI IDE BMDMA transaction
  *     @qc: Info associated with this ATA transaction.
  *
@@ -3137,7 +3204,100 @@ void ata_pci_bmdma_init(struct ata_host *host)
 }
 EXPORT_SYMBOL_GPL(ata_pci_bmdma_init);
 
+/**
+ *     ata_pci_bmdma_prepare_host - helper to prepare PCI BMDMA ATA host
+ *     @pdev: target PCI device
+ *     @ppi: array of port_info, must be enough for two ports
+ *     @r_host: out argument for the initialized ATA host
+ *
+ *     Helper to allocate BMDMA ATA host for @pdev, acquire all PCI
+ *     resources and initialize it accordingly in one go.
+ *
+ *     LOCKING:
+ *     Inherited from calling layer (may sleep).
+ *
+ *     RETURNS:
+ *     0 on success, -errno otherwise.
+ */
+int ata_pci_bmdma_prepare_host(struct pci_dev *pdev,
+                              const struct ata_port_info * const * ppi,
+                              struct ata_host **r_host)
+{
+       int rc;
+
+       rc = ata_pci_sff_prepare_host(pdev, ppi, r_host);
+       if (rc)
+               return rc;
+
+       ata_pci_bmdma_init(*r_host);
+       return 0;
+}
+EXPORT_SYMBOL_GPL(ata_pci_bmdma_prepare_host);
+
+/**
+ *     ata_pci_bmdma_init_one - Initialize/register BMDMA PCI IDE controller
+ *     @pdev: Controller to be initialized
+ *     @ppi: array of port_info, must be enough for two ports
+ *     @sht: scsi_host_template to use when registering the host
+ *     @host_priv: host private_data
+ *     @hflags: host flags
+ *
+ *     This function is similar to ata_pci_sff_init_one() but also
+ *     takes care of BMDMA initialization.
+ *
+ *     LOCKING:
+ *     Inherited from PCI layer (may sleep).
+ *
+ *     RETURNS:
+ *     Zero on success, negative on errno-based value on error.
+ */
+int ata_pci_bmdma_init_one(struct pci_dev *pdev,
+                          const struct ata_port_info * const * ppi,
+                          struct scsi_host_template *sht, void *host_priv,
+                          int hflags)
+{
+       struct device *dev = &pdev->dev;
+       const struct ata_port_info *pi;
+       struct ata_host *host = NULL;
+       int rc;
+
+       DPRINTK("ENTER\n");
+
+       pi = ata_sff_find_valid_pi(ppi);
+       if (!pi) {
+               dev_printk(KERN_ERR, &pdev->dev,
+                          "no valid port_info specified\n");
+               return -EINVAL;
+       }
+
+       if (!devres_open_group(dev, NULL, GFP_KERNEL))
+               return -ENOMEM;
+
+       rc = pcim_enable_device(pdev);
+       if (rc)
+               goto out;
+
+       /* prepare and activate BMDMA host */
+       rc = ata_pci_bmdma_prepare_host(pdev, ppi, &host);
+       if (rc)
+               goto out;
+       host->private_data = host_priv;
+       host->flags |= hflags;
+
+       pci_set_master(pdev);
+       rc = ata_pci_sff_activate_host(host, ata_bmdma_interrupt, sht);
+ out:
+       if (rc == 0)
+               devres_remove_group(&pdev->dev, NULL);
+       else
+               devres_release_group(&pdev->dev, NULL);
+
+       return rc;
+}
+EXPORT_SYMBOL_GPL(ata_pci_bmdma_init_one);
+
 #endif /* CONFIG_PCI */
+#endif /* CONFIG_ATA_BMDMA */
 
 /**
  *     ata_sff_port_init - Initialize SFF/BMDMA ATA port
index 066b9f3..c8d4703 100644 (file)
@@ -260,7 +260,7 @@ static int pacpi_init_one (struct pci_dev *pdev, const struct pci_device_id *id)
                        return rc;
                pcim_pin_device(pdev);
        }
-       return ata_pci_sff_init_one(pdev, ppi, &pacpi_sht, NULL, 0);
+       return ata_pci_bmdma_init_one(pdev, ppi, &pacpi_sht, NULL, 0);
 }
 
 static const struct pci_device_id pacpi_pci_tbl[] = {
index f306e10..794ec6e 100644 (file)
@@ -583,7 +583,10 @@ static int ali_init_one(struct pci_dev *pdev, const struct pci_device_id *id)
                        ppi[0] = &info_20_udma;
        }
 
-       return ata_pci_sff_init_one(pdev, ppi, &ali_sht, NULL, 0);
+       if (!ppi[0]->mwdma_mask && !ppi[0]->udma_mask)
+               return ata_pci_sff_init_one(pdev, ppi, &ali_sht, NULL, 0);
+       else
+               return ata_pci_bmdma_init_one(pdev, ppi, &ali_sht, NULL, 0);
 }
 
 #ifdef CONFIG_PM
index d95eca9..620a07c 100644 (file)
@@ -574,7 +574,7 @@ static int amd_init_one(struct pci_dev *pdev, const struct pci_device_id *id)
        }
 
        /* And fire it up */
-       return ata_pci_sff_init_one(pdev, ppi, &amd_sht, hpriv, 0);
+       return ata_pci_bmdma_init_one(pdev, ppi, &amd_sht, hpriv, 0);
 }
 
 #ifdef CONFIG_PM
index 4d066d6..ba43f0f 100644 (file)
@@ -421,7 +421,7 @@ static int artop_init_one (struct pci_dev *pdev, const struct pci_device_id *id)
 
        BUG_ON(ppi[0] == NULL);
 
-       return ata_pci_sff_init_one(pdev, ppi, &artop_sht, NULL, 0);
+       return ata_pci_bmdma_init_one(pdev, ppi, &artop_sht, NULL, 0);
 }
 
 static const struct pci_device_id artop_pci_tbl[] = {
index 44d88b3..4375561 100644 (file)
@@ -246,8 +246,8 @@ static int atiixp_init_one(struct pci_dev *pdev, const struct pci_device_id *id)
                if (!pci_test_config_bits(pdev, &atiixp_enable_bits[i]))
                        ppi[i] = &ata_dummy_port_info;
 
-       return ata_pci_sff_init_one(pdev, ppi, &atiixp_sht, NULL,
-                                               ATA_HOST_PARALLEL_SCAN);
+       return ata_pci_bmdma_init_one(pdev, ppi, &atiixp_sht, NULL,
+                                     ATA_HOST_PARALLEL_SCAN);
 }
 
 static const struct pci_device_id atiixp[] = {
index bb6e074..9529593 100644 (file)
@@ -525,7 +525,7 @@ static int atp867x_init_one(struct pci_dev *pdev,
 
        pci_set_master(pdev);
 
-       rc = ata_host_activate(host, pdev->irq, ata_sff_interrupt,
+       rc = ata_host_activate(host, pdev->irq, ata_bmdma_interrupt,
                                IRQF_SHARED, &atp867x_sht);
        if (rc)
                dev_printk(KERN_ERR, &pdev->dev, "failed to activate host\n");
index 6422cfd..9cae65d 100644 (file)
@@ -1214,7 +1214,7 @@ static unsigned int bfin_data_xfer(struct ata_device *dev, unsigned char *buf,
  *     bfin_irq_clear - Clear ATAPI interrupt.
  *     @ap: Port associated with this ATA transaction.
  *
- *     Note: Original code is ata_sff_irq_clear().
+ *     Note: Original code is ata_bmdma_irq_clear().
  */
 
 static void bfin_irq_clear(struct ata_port *ap)
index 4c81a71..9f5da1c 100644 (file)
@@ -367,7 +367,7 @@ static int cmd64x_init_one(struct pci_dev *pdev, const struct pci_device_id *id)
        pci_write_config_byte(pdev, UDIDETCR0, 0xF0);
 #endif
 
-       return ata_pci_sff_init_one(pdev, ppi, &cmd64x_sht, NULL, 0);
+       return ata_pci_bmdma_init_one(pdev, ppi, &cmd64x_sht, NULL, 0);
 }
 
 #ifdef CONFIG_PM
index 17c5f34..030952f 100644 (file)
@@ -221,7 +221,7 @@ static int __devinit cs5520_init_one(struct pci_dev *pdev, const struct pci_devi
                        continue;
 
                rc = devm_request_irq(&pdev->dev, irq[ap->port_no],
-                                     ata_sff_interrupt, 0, DRV_NAME, host);
+                                     ata_bmdma_interrupt, 0, DRV_NAME, host);
                if (rc)
                        return rc;
 
index e809a42..f792330 100644 (file)
@@ -324,7 +324,7 @@ static int cs5530_init_one(struct pci_dev *pdev, const struct pci_device_id *id)
                ppi[1] = &info_palmax_secondary;
 
        /* Now kick off ATA set up */
-       return ata_pci_sff_init_one(pdev, ppi, &cs5530_sht, NULL, 0);
+       return ata_pci_bmdma_init_one(pdev, ppi, &cs5530_sht, NULL, 0);
 }
 
 #ifdef CONFIG_PM
index a02e645..03a9318 100644 (file)
@@ -198,7 +198,7 @@ static int cs5535_init_one(struct pci_dev *dev, const struct pci_device_id *id)
        rdmsr(ATAC_CH0D1_PIO, timings, dummy);
        if (CS5535_BAD_PIO(timings))
                wrmsr(ATAC_CH0D1_PIO, 0xF7F4F7F4UL, 0);
-       return ata_pci_sff_init_one(dev, ppi, &cs5535_sht, NULL, 0);
+       return ata_pci_bmdma_init_one(dev, ppi, &cs5535_sht, NULL, 0);
 }
 
 static const struct pci_device_id cs5535[] = {
index 914ae35..21ee23f 100644 (file)
@@ -260,7 +260,7 @@ static int cs5536_init_one(struct pci_dev *dev, const struct pci_device_id *id)
                return -ENODEV;
        }
 
-       return ata_pci_sff_init_one(dev, ppi, &cs5536_sht, NULL, 0);
+       return ata_pci_bmdma_init_one(dev, ppi, &cs5536_sht, NULL, 0);
 }
 
 static const struct pci_device_id cs5536[] = {
index 0fcc096..6d915b0 100644 (file)
@@ -138,7 +138,7 @@ static int cy82c693_init_one(struct pci_dev *pdev, const struct pci_device_id *i
        if (PCI_FUNC(pdev->devfn) != 1)
                return -ENODEV;
 
-       return ata_pci_sff_init_one(pdev, ppi, &cy82c693_sht, NULL, 0);
+       return ata_pci_bmdma_init_one(pdev, ppi, &cy82c693_sht, NULL, 0);
 }
 
 static const struct pci_device_id cy82c693[] = {
index 3bac0e0..a088347 100644 (file)
@@ -277,8 +277,8 @@ static int efar_init_one (struct pci_dev *pdev, const struct pci_device_id *ent)
                dev_printk(KERN_DEBUG, &pdev->dev,
                           "version " DRV_VERSION "\n");
 
-       return ata_pci_sff_init_one(pdev, ppi, &efar_sht, NULL,
-                                       ATA_HOST_PARALLEL_SCAN);
+       return ata_pci_bmdma_init_one(pdev, ppi, &efar_sht, NULL,
+                                     ATA_HOST_PARALLEL_SCAN);
 }
 
 static const struct pci_device_id efar_pci_tbl[] = {
index 8580eb3..7688868 100644 (file)
@@ -361,7 +361,7 @@ static int hpt36x_init_one(struct pci_dev *dev, const struct pci_device_id *id)
                        break;
        }
        /* Now kick off ATA set up */
-       return ata_pci_sff_init_one(dev, ppi, &hpt36x_sht, hpriv, 0);
+       return ata_pci_bmdma_init_one(dev, ppi, &hpt36x_sht, hpriv, 0);
 }
 
 #ifdef CONFIG_PM
index 98b498b..9ae4c08 100644 (file)
@@ -987,7 +987,7 @@ static int hpt37x_init_one(struct pci_dev *dev, const struct pci_device_id *id)
        }
 
        /* Now kick off ATA set up */
-       return ata_pci_sff_init_one(dev, ppi, &hpt37x_sht, private_data, 0);
+       return ata_pci_bmdma_init_one(dev, ppi, &hpt37x_sht, private_data, 0);
 }
 
 static const struct pci_device_id hpt37x[] = {
index 8b95aeb..32f3463 100644 (file)
@@ -548,7 +548,7 @@ static int hpt3x2n_init_one(struct pci_dev *dev, const struct pci_device_id *id)
                outb(inb(iobase + 0x9c) | 0x04, iobase + 0x9c);
 
        /* Now kick off ATA set up */
-       return ata_pci_sff_init_one(dev, ppi, &hpt3x2n_sht, hpriv, 0);
+       return ata_pci_bmdma_init_one(dev, ppi, &hpt3x2n_sht, hpriv, 0);
 }
 
 static const struct pci_device_id hpt3x2n[] = {
index 727a81c..b63d5e2 100644 (file)
@@ -248,7 +248,7 @@ static int hpt3x3_init_one(struct pci_dev *pdev, const struct pci_device_id *id)
                ata_port_pbar_desc(ap, 4, offset_cmd[i], "cmd");
        }
        pci_set_master(pdev);
-       return ata_host_activate(host, pdev->irq, ata_sff_interrupt,
+       return ata_host_activate(host, pdev->irq, ata_bmdma_interrupt,
                                 IRQF_SHARED, &hpt3x3_sht);
 }
 
index b56e8f7..9f2889f 100644 (file)
@@ -470,7 +470,7 @@ static int __devinit pata_icside_add_ports(struct pata_icside_info *info)
                pata_icside_setup_ioaddr(ap, info->base, info, info->port[i]);
        }
 
-       return ata_host_activate(host, ec->irq, ata_sff_interrupt, 0,
+       return ata_host_activate(host, ec->irq, ata_bmdma_interrupt, 0,
                                 &pata_icside_sht);
 }
 
index f971f0d..4d142a2 100644 (file)
@@ -273,7 +273,7 @@ static int it8213_init_one (struct pci_dev *pdev, const struct pci_device_id *en
                dev_printk(KERN_DEBUG, &pdev->dev,
                           "version " DRV_VERSION "\n");
 
-       return ata_pci_sff_init_one(pdev, ppi, &it8213_sht, NULL, 0);
+       return ata_pci_bmdma_init_one(pdev, ppi, &it8213_sht, NULL, 0);
 }
 
 static const struct pci_device_id it8213_pci_tbl[] = {
index 2bd2b00..bf88f71 100644 (file)
@@ -933,7 +933,7 @@ static int it821x_init_one(struct pci_dev *pdev, const struct pci_device_id *id)
                else
                        ppi[0] = &info_smart;
        }
-       return ata_pci_sff_init_one(pdev, ppi, &it821x_sht, NULL, 0);
+       return ata_pci_bmdma_init_one(pdev, ppi, &it821x_sht, NULL, 0);
 }
 
 #ifdef CONFIG_PM
index 565e01e..cb3babb 100644 (file)
@@ -144,7 +144,7 @@ static int jmicron_init_one (struct pci_dev *pdev, const struct pci_device_id *i
        };
        const struct ata_port_info *ppi[] = { &info, NULL };
 
-       return ata_pci_sff_init_one(pdev, ppi, &jmicron_sht, NULL, 0);
+       return ata_pci_bmdma_init_one(pdev, ppi, &jmicron_sht, NULL, 0);
 }
 
 static const struct pci_device_id jmicron_pci_tbl[] = {
index b5b48e7..76640ac 100644 (file)
@@ -1110,7 +1110,7 @@ static int __devinit pata_macio_common_init(struct pata_macio_priv        *priv,
 
        /* Start it up */
        priv->irq = irq;
-       return ata_host_activate(priv->host, irq, ata_sff_interrupt, 0,
+       return ata_host_activate(priv->host, irq, ata_bmdma_interrupt, 0,
                                 &pata_macio_sht);
 }
 
index e8ca02e..dd38083 100644 (file)
@@ -153,7 +153,7 @@ static int marvell_init_one (struct pci_dev *pdev, const struct pci_device_id *i
                return -ENODEV;
        }
 #endif
-       return ata_pci_sff_init_one(pdev, ppi, &marvell_sht, NULL, 0);
+       return ata_pci_bmdma_init_one(pdev, ppi, &marvell_sht, NULL, 0);
 }
 
 static const struct pci_device_id marvell_pci_tbl[] = {
index 36afe2c..f087ab5 100644 (file)
@@ -659,7 +659,7 @@ mpc52xx_ata_init_one(struct device *dev, struct mpc52xx_ata_priv *priv,
        ata_port_desc(ap, "ata_regs 0x%lx", raw_ata_regs);
 
        /* activate host */
-       return ata_host_activate(host, priv->ata_irq, ata_sff_interrupt, 0,
+       return ata_host_activate(host, priv->ata_irq, ata_bmdma_interrupt, 0,
                                 &mpc52xx_ata_sht);
 }
 
index 94f979a..3eb921c 100644 (file)
@@ -82,7 +82,7 @@ static int netcell_init_one (struct pci_dev *pdev, const struct pci_device_id *e
        ata_pci_bmdma_clear_simplex(pdev);
 
        /* And let the library code do the work */
-       return ata_pci_sff_init_one(pdev, port_info, &netcell_sht, NULL, 0);
+       return ata_pci_bmdma_init_one(pdev, port_info, &netcell_sht, NULL, 0);
 }
 
 static const struct pci_device_id netcell_pci_tbl[] = {
index dd53a66..cc50bd0 100644 (file)
@@ -149,7 +149,7 @@ static int ninja32_init_one(struct pci_dev *dev, const struct pci_device_id *id)
 
        ninja32_program(base);
        /* FIXME: Should we disable them at remove ? */
-       return ata_host_activate(host, dev->irq, ata_sff_interrupt,
+       return ata_host_activate(host, dev->irq, ata_bmdma_interrupt,
                                 IRQF_SHARED, &ninja32_sht);
 }
 
index fdbba2d..605f198 100644 (file)
@@ -380,7 +380,7 @@ static int ns87415_init_one (struct pci_dev *pdev, const struct pci_device_id *e
 
        ns87415_fixup(pdev);
 
-       return ata_pci_sff_init_one(pdev, ppi, &ns87415_sht, NULL, 0);
+       return ata_pci_bmdma_init_one(pdev, ppi, &ns87415_sht, NULL, 0);
 }
 
 static const struct pci_device_id ns87415_pci_tbl[] = {
index 3001109..06ddd91 100644 (file)
@@ -750,20 +750,6 @@ static void octeon_cf_dev_config(struct ata_device *dev)
 }
 
 /*
- * Trap if driver tries to do standard bmdma commands.  They are not
- * supported.
- */
-static void unreachable_qc(struct ata_queued_cmd *qc)
-{
-       BUG();
-}
-
-static u8 unreachable_port(struct ata_port *ap)
-{
-       BUG();
-}
-
-/*
  * We don't do ATAPI DMA so return 0.
  */
 static int octeon_cf_check_atapi_dma(struct ata_queued_cmd *qc)
@@ -804,10 +790,6 @@ static struct ata_port_operations octeon_cf_ops = {
        .sff_dev_select         = octeon_cf_dev_select,
        .sff_irq_on             = octeon_cf_irq_on,
        .sff_irq_clear          = octeon_cf_irq_clear,
-       .bmdma_setup            = unreachable_qc,
-       .bmdma_start            = unreachable_qc,
-       .bmdma_stop             = unreachable_qc,
-       .bmdma_status           = unreachable_port,
        .cable_detect           = ata_cable_40wire,
        .set_piomode            = octeon_cf_set_piomode,
        .set_dmamode            = octeon_cf_set_dmamode,
index 988ef26..b811c16 100644 (file)
@@ -248,7 +248,7 @@ static int oldpiix_init_one (struct pci_dev *pdev, const struct pci_device_id *e
                dev_printk(KERN_DEBUG, &pdev->dev,
                           "version " DRV_VERSION "\n");
 
-       return ata_pci_sff_init_one(pdev, ppi, &oldpiix_sht, NULL, 0);
+       return ata_pci_bmdma_init_one(pdev, ppi, &oldpiix_sht, NULL, 0);
 }
 
 static const struct pci_device_id oldpiix_pci_tbl[] = {
index 76b7d12..0852cd0 100644 (file)
@@ -429,7 +429,7 @@ static int optidma_init_one(struct pci_dev *dev, const struct pci_device_id *id)
        if (optiplus_with_udma(dev))
                ppi[0] = &info_82c700_udma;
 
-       return ata_pci_sff_init_one(dev, ppi, &optidma_sht, NULL, 0);
+       return ata_pci_bmdma_init_one(dev, ppi, &optidma_sht, NULL, 0);
 }
 
 static const struct pci_device_id optidma[] = {
index 09f1f22..b183511 100644 (file)
@@ -754,7 +754,7 @@ static int __devinit pdc2027x_init_one(struct pci_dev *pdev, const struct pci_de
                return -EIO;
 
        pci_set_master(pdev);
-       return ata_host_activate(host, pdev->irq, ata_sff_interrupt,
+       return ata_host_activate(host, pdev->irq, ata_bmdma_interrupt,
                                 IRQF_SHARED, &pdc2027x_sht);
 }
 
index fa1e2f3..c39f213 100644 (file)
@@ -337,7 +337,7 @@ static int pdc202xx_init_one(struct pci_dev *dev, const struct pci_device_id *id
                                return -ENODEV;
                }
        }
-       return ata_pci_sff_init_one(dev, ppi, &pdc202xx_sht, NULL, 0);
+       return ata_pci_bmdma_init_one(dev, ppi, &pdc202xx_sht, NULL, 0);
 }
 
 static const struct pci_device_id pdc202xx[] = {
index 9816154..cb01bf9 100644 (file)
@@ -95,7 +95,7 @@ static int ata_tosh_init_one(struct pci_dev *dev, const struct pci_device_id *id
        };
        const struct ata_port_info *ppi[] = { &info, &ata_dummy_port_info };
        /* Just one port for the moment */
-       return ata_pci_sff_init_one(dev, ppi, &tosh_sht, NULL, 0);
+       return ata_pci_bmdma_init_one(dev, ppi, &tosh_sht, NULL, 0);
 }
 
 static struct pci_device_id ata_tosh[] = {
index a5fa388..8574b31 100644 (file)
@@ -227,7 +227,7 @@ static int radisys_init_one (struct pci_dev *pdev, const struct pci_device_id *e
                dev_printk(KERN_DEBUG, &pdev->dev,
                           "version " DRV_VERSION "\n");
 
-       return ata_pci_sff_init_one(pdev, ppi, &radisys_sht, NULL, 0);
+       return ata_pci_bmdma_init_one(pdev, ppi, &radisys_sht, NULL, 0);
 }
 
 static const struct pci_device_id radisys_pci_tbl[] = {
index 37092cf..5fbe9b1 100644 (file)
@@ -344,7 +344,7 @@ static int __devinit rdc_init_one(struct pci_dev *pdev,
         */
        pci_read_config_dword(pdev, 0x54, &hpriv->saved_iocfg);
 
-       rc = ata_pci_sff_prepare_host(pdev, ppi, &host);
+       rc = ata_pci_bmdma_prepare_host(pdev, ppi, &host);
        if (rc)
                return rc;
        host->private_data = hpriv;
@@ -354,7 +354,7 @@ static int __devinit rdc_init_one(struct pci_dev *pdev,
        host->flags |= ATA_HOST_PARALLEL_SCAN;
 
        pci_set_master(pdev);
-       return ata_pci_sff_activate_host(host, ata_sff_interrupt, &rdc_sht);
+       return ata_pci_sff_activate_host(host, ata_bmdma_interrupt, &rdc_sht);
 }
 
 static void rdc_remove_one(struct pci_dev *pdev)
index 6b5b63a..e2c1825 100644 (file)
@@ -237,7 +237,7 @@ static int sc1200_init_one(struct pci_dev *dev, const struct pci_device_id *id)
        };
        const struct ata_port_info *ppi[] = { &info, NULL };
 
-       return ata_pci_sff_init_one(dev, ppi, &sc1200_sht, NULL, 0);
+       return ata_pci_bmdma_init_one(dev, ppi, &sc1200_sht, NULL, 0);
 }
 
 static const struct pci_device_id sc1200[] = {
index 6f6193b..d9db3f8 100644 (file)
@@ -875,7 +875,7 @@ static void scc_postreset(struct ata_link *link, unsigned int *classes)
  *     scc_irq_clear - Clear PCI IDE BMDMA interrupt.
  *     @ap: Port associated with this ATA transaction.
  *
- *     Note: Original code is ata_sff_irq_clear().
+ *     Note: Original code is ata_bmdma_irq_clear().
  */
 
 static void scc_irq_clear (struct ata_port *ap)
@@ -1105,7 +1105,7 @@ static int scc_init_one (struct pci_dev *pdev, const struct pci_device_id *ent)
        if (rc)
                return rc;
 
-       return ata_host_activate(host, pdev->irq, ata_sff_interrupt,
+       return ata_host_activate(host, pdev->irq, ata_bmdma_interrupt,
                                 IRQF_SHARED, &scc_sht);
 }
 
index 86b3d01..e97b32f 100644 (file)
@@ -179,7 +179,7 @@ static int __devinit sch_init_one(struct pci_dev *pdev,
                dev_printk(KERN_DEBUG, &pdev->dev,
                           "version " DRV_VERSION "\n");
 
-       return ata_pci_sff_init_one(pdev, ppi, &sch_sht, NULL, 0);
+       return ata_pci_bmdma_init_one(pdev, ppi, &sch_sht, NULL, 0);
 }
 
 static int __init sch_init(void)
index 43ea389..86dd714 100644 (file)
@@ -460,7 +460,7 @@ static int serverworks_init_one(struct pci_dev *pdev, const struct pci_device_id
        if (pdev->device == PCI_DEVICE_ID_SERVERWORKS_CSB5IDE)
                ata_pci_bmdma_clear_simplex(pdev);
 
-       return ata_pci_sff_init_one(pdev, ppi, &serverworks_sht, NULL, 0);
+       return ata_pci_bmdma_init_one(pdev, ppi, &serverworks_sht, NULL, 0);
 }
 
 #ifdef CONFIG_PM
index 43faf10..d3190d7 100644 (file)
@@ -374,11 +374,11 @@ static int __devinit sil680_init_one(struct pci_dev *pdev,
        ata_sff_std_ports(&host->ports[1]->ioaddr);
 
        /* Register & activate */
-       return ata_host_activate(host, pdev->irq, ata_sff_interrupt,
+       return ata_host_activate(host, pdev->irq, ata_bmdma_interrupt,
                                 IRQF_SHARED, &sil680_sht);
 
 use_ioports:
-       return ata_pci_sff_init_one(pdev, ppi, &sil680_sht, NULL, 0);
+       return ata_pci_bmdma_init_one(pdev, ppi, &sil680_sht, NULL, 0);
 }
 
 #ifdef CONFIG_PM
index b670803..60cea13 100644 (file)
@@ -826,7 +826,7 @@ static int sis_init_one (struct pci_dev *pdev, const struct pci_device_id *ent)
 
        sis_fixup(pdev, chipset);
 
-       return ata_pci_sff_init_one(pdev, ppi, &sis_sht, chipset, 0);
+       return ata_pci_bmdma_init_one(pdev, ppi, &sis_sht, chipset, 0);
 }
 
 #ifdef CONFIG_PM
index 733b042..98548f6 100644 (file)
@@ -316,7 +316,7 @@ static int sl82c105_init_one(struct pci_dev *dev, const struct pci_device_id *id
        val |= CTRL_P0EN | CTRL_P0F16 | CTRL_P1F16;
        pci_write_config_dword(dev, 0x40, val);
 
-       return ata_pci_sff_init_one(dev, ppi, &sl82c105_sht, NULL, 0);
+       return ata_pci_bmdma_init_one(dev, ppi, &sl82c105_sht, NULL, 0);
 }
 
 static const struct pci_device_id sl82c105[] = {
index 48f5060..0d1f89e 100644 (file)
@@ -201,7 +201,7 @@ static int triflex_init_one(struct pci_dev *dev, const struct pci_device_id *id)
        if (!printed_version++)
                dev_printk(KERN_DEBUG, &dev->dev, "version " DRV_VERSION "\n");
 
-       return ata_pci_sff_init_one(dev, ppi, &triflex_sht, NULL, 0);
+       return ata_pci_bmdma_init_one(dev, ppi, &triflex_sht, NULL, 0);
 }
 
 static const struct pci_device_id triflex[] = {
index 7e3e0a5..5e65988 100644 (file)
@@ -627,7 +627,7 @@ static int via_init_one(struct pci_dev *pdev, const struct pci_device_id *id)
        }
 
        /* We have established the device type, now fire it up */
-       return ata_pci_sff_init_one(pdev, ppi, &via_sht, (void *)config, 0);
+       return ata_pci_bmdma_init_one(pdev, ppi, &via_sht, (void *)config, 0);
 }
 
 #ifdef CONFIG_PM
index f3471bc..a476cd9 100644 (file)
@@ -675,8 +675,6 @@ static struct ata_port_operations mv5_ops = {
        .freeze                 = mv_eh_freeze,
        .thaw                   = mv_eh_thaw,
        .hardreset              = mv_hardreset,
-       .error_handler          = ata_std_error_handler, /* avoid SFF EH */
-       .post_internal_cmd      = ATA_OP_NULL,
 
        .scr_read               = mv5_scr_read,
        .scr_write              = mv5_scr_write,
@@ -2813,7 +2811,7 @@ static void mv_port_intr(struct ata_port *ap, u32 port_cause)
        } else if (!edma_was_enabled) {
                struct ata_queued_cmd *qc = mv_get_active_qc(ap);
                if (qc)
-                       ata_sff_host_intr(ap, qc);
+                       ata_bmdma_port_intr(ap, qc);
                else
                        mv_unexpected_intr(ap, edma_was_enabled);
        }
index baa8f0d..6fd1147 100644 (file)
@@ -920,7 +920,7 @@ static int nv_host_intr(struct ata_port *ap, u8 irq_stat)
        }
 
        /* handle interrupt */
-       return ata_sff_host_intr(ap, qc);
+       return ata_bmdma_port_intr(ap, qc);
 }
 
 static irqreturn_t nv_adma_interrupt(int irq, void *dev_instance)
@@ -1100,7 +1100,7 @@ static void nv_adma_irq_clear(struct ata_port *ap)
        u32 notifier_clears[2];
 
        if (pp->flags & NV_ADMA_ATAPI_SETUP_COMPLETE) {
-               ata_sff_irq_clear(ap);
+               ata_bmdma_irq_clear(ap);
                return;
        }
 
@@ -1505,7 +1505,7 @@ static irqreturn_t nv_generic_interrupt(int irq, void *dev_instance)
 
                qc = ata_qc_from_tag(ap, ap->link.active_tag);
                if (qc && (!(qc->tf.flags & ATA_TFLAG_POLLING))) {
-                       handled += ata_sff_host_intr(ap, qc);
+                       handled += ata_bmdma_port_intr(ap, qc);
                } else {
                        /*
                         * No request pending?  Clear interrupt status
@@ -2430,7 +2430,7 @@ static int nv_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
 
        ppi[0] = &nv_port_info[type];
        ipriv = ppi[0]->private_data;
-       rc = ata_pci_sff_prepare_host(pdev, ppi, &host);
+       rc = ata_pci_bmdma_prepare_host(pdev, ppi, &host);
        if (rc)
                return rc;
 
index d533b3d..daeebf1 100644 (file)
@@ -120,8 +120,6 @@ static void qs_host_stop(struct ata_host *host);
 static void qs_qc_prep(struct ata_queued_cmd *qc);
 static unsigned int qs_qc_issue(struct ata_queued_cmd *qc);
 static int qs_check_atapi_dma(struct ata_queued_cmd *qc);
-static void qs_bmdma_stop(struct ata_queued_cmd *qc);
-static u8 qs_bmdma_status(struct ata_port *ap);
 static void qs_freeze(struct ata_port *ap);
 static void qs_thaw(struct ata_port *ap);
 static int qs_prereset(struct ata_link *link, unsigned long deadline);
@@ -137,8 +135,6 @@ static struct ata_port_operations qs_ata_ops = {
        .inherits               = &ata_sff_port_ops,
 
        .check_atapi_dma        = qs_check_atapi_dma,
-       .bmdma_stop             = qs_bmdma_stop,
-       .bmdma_status           = qs_bmdma_status,
        .qc_prep                = qs_qc_prep,
        .qc_issue               = qs_qc_issue,
 
@@ -190,16 +186,6 @@ static int qs_check_atapi_dma(struct ata_queued_cmd *qc)
        return 1;       /* ATAPI DMA not supported */
 }
 
-static void qs_bmdma_stop(struct ata_queued_cmd *qc)
-{
-       /* nothing */
-}
-
-static u8 qs_bmdma_status(struct ata_port *ap)
-{
-       return 0;
-}
-
 static inline void qs_enter_reg_mode(struct ata_port *ap)
 {
        u8 __iomem *chan = qs_mmio_base(ap->host) + (ap->port_no * 0x4000);
@@ -454,7 +440,7 @@ static inline unsigned int qs_intr_mmio(struct ata_host *host)
                if (!pp || pp->state != qs_state_mmio)
                        continue;
                if (!(qc->tf.flags & ATA_TFLAG_POLLING))
-                       handled |= ata_sff_host_intr(ap, qc);
+                       handled |= ata_sff_port_intr(ap, qc);
        }
        return handled;
 }
index 2dda312..3a4f842 100644 (file)
@@ -503,7 +503,7 @@ static void sil_host_intr(struct ata_port *ap, u32 bmdma2)
                goto err_hsm;
 
        /* ack bmdma irq events */
-       ata_sff_irq_clear(ap);
+       ata_bmdma_irq_clear(ap);
 
        /* kick HSM in the ass */
        ata_sff_hsm_move(ap, qc, status, 0);
@@ -584,7 +584,7 @@ static void sil_thaw(struct ata_port *ap)
 
        /* clear IRQ */
        ap->ops->sff_check_status(ap);
-       ata_sff_irq_clear(ap);
+       ata_bmdma_irq_clear(ap);
 
        /* turn on SATA IRQ if supported */
        if (!(ap->flags & SIL_FLAG_NO_SATA_IRQ))
index f8a91bf..2bfe3ae 100644 (file)
@@ -279,7 +279,7 @@ static int sis_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
                break;
        }
 
-       rc = ata_pci_sff_prepare_host(pdev, ppi, &host);
+       rc = ata_pci_bmdma_prepare_host(pdev, ppi, &host);
        if (rc)
                return rc;
 
@@ -308,7 +308,7 @@ static int sis_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
 
        pci_set_master(pdev);
        pci_intx(pdev, 1);
-       return ata_host_activate(host, pdev->irq, ata_sff_interrupt,
+       return ata_host_activate(host, pdev->irq, ata_bmdma_interrupt,
                                 IRQF_SHARED, &sis_sht);
 }
 
index 101fd6a..7d9db4a 100644 (file)
@@ -502,7 +502,7 @@ static int k2_sata_init_one(struct pci_dev *pdev, const struct pci_device_id *en
        writel(0x0, mmio_base + K2_SATA_SIM_OFFSET);
 
        pci_set_master(pdev);
-       return ata_host_activate(host, pdev->irq, ata_sff_interrupt,
+       return ata_host_activate(host, pdev->irq, ata_bmdma_interrupt,
                                 IRQF_SHARED, &k2_sata_sht);
 }
 
index d8dac17..b8578c3 100644 (file)
@@ -242,7 +242,7 @@ static int uli_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
 
        pci_set_master(pdev);
        pci_intx(pdev, 1);
-       return ata_host_activate(host, pdev->irq, ata_sff_interrupt,
+       return ata_host_activate(host, pdev->irq, ata_bmdma_interrupt,
                                 IRQF_SHARED, &uli_sht);
 }
 
index 08f6549..101d8c2 100644 (file)
@@ -308,7 +308,7 @@ static void svia_noop_freeze(struct ata_port *ap)
         * certain way.  Leave it alone and just clear pending IRQ.
         */
        ap->ops->sff_check_status(ap);
-       ata_sff_irq_clear(ap);
+       ata_bmdma_irq_clear(ap);
 }
 
 /**
@@ -463,7 +463,7 @@ static int vt6420_prepare_host(struct pci_dev *pdev, struct ata_host **r_host)
        struct ata_host *host;
        int rc;
 
-       rc = ata_pci_sff_prepare_host(pdev, ppi, &host);
+       rc = ata_pci_bmdma_prepare_host(pdev, ppi, &host);
        if (rc)
                return rc;
        *r_host = host;
@@ -520,7 +520,7 @@ static int vt8251_prepare_host(struct pci_dev *pdev, struct ata_host **r_host)
        struct ata_host *host;
        int i, rc;
 
-       rc = ata_pci_sff_prepare_host(pdev, ppi, &host);
+       rc = ata_pci_bmdma_prepare_host(pdev, ppi, &host);
        if (rc)
                return rc;
        *r_host = host;
@@ -628,7 +628,7 @@ static int svia_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
        svia_configure(pdev);
 
        pci_set_master(pdev);
-       return ata_host_activate(host, pdev->irq, ata_sff_interrupt,
+       return ata_host_activate(host, pdev->irq, ata_bmdma_interrupt,
                                 IRQF_SHARED, &svia_sht);
 }
 
index 2107952..b777176 100644 (file)
@@ -245,7 +245,7 @@ static void vsc_port_intr(u8 port_status, struct ata_port *ap)
 
        qc = ata_qc_from_tag(ap, ap->link.active_tag);
        if (qc && likely(!(qc->tf.flags & ATA_TFLAG_POLLING)))
-               handled = ata_sff_host_intr(ap, qc);
+               handled = ata_bmdma_port_intr(ap, qc);
 
        /* We received an interrupt during a polled command,
         * or some other spurious condition.  Interrupt reporting
index 606048b..85c004a 100644 (file)
@@ -305,8 +305,7 @@ static int ps3flash_flush(struct file *file, fl_owner_t id)
        return ps3flash_writeback(ps3flash_dev);
 }
 
-static int ps3flash_fsync(struct file *file, struct dentry *dentry,
-                         int datasync)
+static int ps3flash_fsync(struct file *file, int datasync)
 {
        return ps3flash_writeback(ps3flash_dev);
 }
index 12fdd39..1994885 100644 (file)
@@ -156,7 +156,7 @@ int cpuidle_enable_device(struct cpuidle_device *dev)
 
        if (dev->enabled)
                return 0;
-       if (!cpuidle_curr_driver || !cpuidle_curr_governor)
+       if (!cpuidle_get_driver() || !cpuidle_curr_governor)
                return -EIO;
        if (!dev->state_count)
                return -EINVAL;
@@ -207,7 +207,7 @@ void cpuidle_disable_device(struct cpuidle_device *dev)
 {
        if (!dev->enabled)
                return;
-       if (!cpuidle_curr_driver || !cpuidle_curr_governor)
+       if (!cpuidle_get_driver() || !cpuidle_curr_governor)
                return;
 
        dev->enabled = 0;
@@ -271,10 +271,11 @@ static int __cpuidle_register_device(struct cpuidle_device *dev)
 {
        int ret;
        struct sys_device *sys_dev = get_cpu_sysdev((unsigned long)dev->cpu);
+       struct cpuidle_driver *cpuidle_driver = cpuidle_get_driver();
 
        if (!sys_dev)
                return -EINVAL;
-       if (!try_module_get(cpuidle_curr_driver->owner))
+       if (!try_module_get(cpuidle_driver->owner))
                return -EINVAL;
 
        init_completion(&dev->kobj_unregister);
@@ -284,7 +285,7 @@ static int __cpuidle_register_device(struct cpuidle_device *dev)
        per_cpu(cpuidle_devices, dev->cpu) = dev;
        list_add(&dev->device_list, &cpuidle_detected_devices);
        if ((ret = cpuidle_add_sysfs(sys_dev))) {
-               module_put(cpuidle_curr_driver->owner);
+               module_put(cpuidle_driver->owner);
                return ret;
        }
 
@@ -325,6 +326,7 @@ EXPORT_SYMBOL_GPL(cpuidle_register_device);
 void cpuidle_unregister_device(struct cpuidle_device *dev)
 {
        struct sys_device *sys_dev = get_cpu_sysdev((unsigned long)dev->cpu);
+       struct cpuidle_driver *cpuidle_driver = cpuidle_get_driver();
 
        if (dev->registered == 0)
                return;
@@ -340,7 +342,7 @@ void cpuidle_unregister_device(struct cpuidle_device *dev)
 
        cpuidle_resume_and_unlock();
 
-       module_put(cpuidle_curr_driver->owner);
+       module_put(cpuidle_driver->owner);
 }
 
 EXPORT_SYMBOL_GPL(cpuidle_unregister_device);
index 9476ba3..33e50d5 100644 (file)
@@ -9,7 +9,6 @@
 
 /* For internal use only */
 extern struct cpuidle_governor *cpuidle_curr_governor;
-extern struct cpuidle_driver *cpuidle_curr_driver;
 extern struct list_head cpuidle_governors;
 extern struct list_head cpuidle_detected_devices;
 extern struct mutex cpuidle_lock;
index 2257004..fd1601e 100644 (file)
@@ -14,7 +14,7 @@
 
 #include "cpuidle.h"
 
-struct cpuidle_driver *cpuidle_curr_driver;
+static struct cpuidle_driver *cpuidle_curr_driver;
 DEFINE_SPINLOCK(cpuidle_driver_lock);
 
 /**
@@ -40,13 +40,25 @@ int cpuidle_register_driver(struct cpuidle_driver *drv)
 EXPORT_SYMBOL_GPL(cpuidle_register_driver);
 
 /**
+ * cpuidle_get_driver - return the current driver
+ */
+struct cpuidle_driver *cpuidle_get_driver(void)
+{
+       return cpuidle_curr_driver;
+}
+EXPORT_SYMBOL_GPL(cpuidle_get_driver);
+
+/**
  * cpuidle_unregister_driver - unregisters a driver
  * @drv: the driver
  */
 void cpuidle_unregister_driver(struct cpuidle_driver *drv)
 {
-       if (!drv)
+       if (drv != cpuidle_curr_driver) {
+               WARN(1, "invalid cpuidle_unregister_driver(%s)\n",
+                       drv->name);
                return;
+       }
 
        spin_lock(&cpuidle_driver_lock);
        cpuidle_curr_driver = NULL;
index 0ba9c8b..0310ffa 100644 (file)
@@ -47,10 +47,11 @@ static ssize_t show_current_driver(struct sysdev_class *class,
                                   char *buf)
 {
        ssize_t ret;
+       struct cpuidle_driver *cpuidle_driver = cpuidle_get_driver();
 
        spin_lock(&cpuidle_driver_lock);
-       if (cpuidle_curr_driver)
-               ret = sprintf(buf, "%s\n", cpuidle_curr_driver->name);
+       if (cpuidle_driver)
+               ret = sprintf(buf, "%s\n", cpuidle_driver->name);
        else
                ret = sprintf(buf, "none\n");
        spin_unlock(&cpuidle_driver_lock);
index 1b88779..9e01e96 100644 (file)
@@ -166,6 +166,15 @@ config TIMB_DMA
 config ARCH_HAS_ASYNC_TX_FIND_CHANNEL
        bool
 
+config PL330_DMA
+       tristate "DMA API Driver for PL330"
+       select DMA_ENGINE
+       depends on PL330
+       help
+         Select if your platform has one or more PL330 DMACs.
+         You need to provide platform specific settings via
+         platform_data for a dma-pl330 device.
+
 config DMA_ENGINE
        bool
 
index 2088142..0fe5ebb 100644 (file)
@@ -22,3 +22,4 @@ obj-$(CONFIG_COH901318) += coh901318.o coh901318_lli.o
 obj-$(CONFIG_AMCC_PPC440SPE_ADMA) += ppc4xx/
 obj-$(CONFIG_TIMB_DMA) += timb_dma.o
 obj-$(CONFIG_STE_DMA40) += ste_dma40.o ste_dma40_ll.o
+obj-$(CONFIG_PL330_DMA) += pl330.o
diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c
new file mode 100644 (file)
index 0000000..7c50f6d
--- /dev/null
@@ -0,0 +1,866 @@
+/* linux/drivers/dma/pl330.c
+ *
+ * Copyright (C) 2010 Samsung Electronics Co. Ltd.
+ *     Jaswinder Singh <jassi.brar@samsung.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/io.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/dmaengine.h>
+#include <linux/interrupt.h>
+#include <linux/amba/bus.h>
+#include <linux/amba/pl330.h>
+
+#define NR_DEFAULT_DESC        16
+
+enum desc_status {
+       /* In the DMAC pool */
+       FREE,
+       /*
+        * Allocted to some channel during prep_xxx
+        * Also may be sitting on the work_list.
+        */
+       PREP,
+       /*
+        * Sitting on the work_list and already submitted
+        * to the PL330 core. Not more than two descriptors
+        * of a channel can be BUSY at any time.
+        */
+       BUSY,
+       /*
+        * Sitting on the channel work_list but xfer done
+        * by PL330 core
+        */
+       DONE,
+};
+
+struct dma_pl330_chan {
+       /* Schedule desc completion */
+       struct tasklet_struct task;
+
+       /* DMA-Engine Channel */
+       struct dma_chan chan;
+
+       /* Last completed cookie */
+       dma_cookie_t completed;
+
+       /* List of to be xfered descriptors */
+       struct list_head work_list;
+
+       /* Pointer to the DMAC that manages this channel,
+        * NULL if the channel is available to be acquired.
+        * As the parent, this DMAC also provides descriptors
+        * to the channel.
+        */
+       struct dma_pl330_dmac *dmac;
+
+       /* To protect channel manipulation */
+       spinlock_t lock;
+
+       /* Token of a hardware channel thread of PL330 DMAC
+        * NULL if the channel is available to be acquired.
+        */
+       void *pl330_chid;
+};
+
+struct dma_pl330_dmac {
+       struct pl330_info pif;
+
+       /* DMA-Engine Device */
+       struct dma_device ddma;
+
+       /* Pool of descriptors available for the DMAC's channels */
+       struct list_head desc_pool;
+       /* To protect desc_pool manipulation */
+       spinlock_t pool_lock;
+
+       /* Peripheral channels connected to this DMAC */
+       struct dma_pl330_chan peripherals[0]; /* keep at end */
+};
+
+struct dma_pl330_desc {
+       /* To attach to a queue as child */
+       struct list_head node;
+
+       /* Descriptor for the DMA Engine API */
+       struct dma_async_tx_descriptor txd;
+
+       /* Xfer for PL330 core */
+       struct pl330_xfer px;
+
+       struct pl330_reqcfg rqcfg;
+       struct pl330_req req;
+
+       enum desc_status status;
+
+       /* The channel which currently holds this desc */
+       struct dma_pl330_chan *pchan;
+};
+
+static inline struct dma_pl330_chan *
+to_pchan(struct dma_chan *ch)
+{
+       if (!ch)
+               return NULL;
+
+       return container_of(ch, struct dma_pl330_chan, chan);
+}
+
+static inline struct dma_pl330_desc *
+to_desc(struct dma_async_tx_descriptor *tx)
+{
+       return container_of(tx, struct dma_pl330_desc, txd);
+}
+
+static inline void free_desc_list(struct list_head *list)
+{
+       struct dma_pl330_dmac *pdmac;
+       struct dma_pl330_desc *desc;
+       struct dma_pl330_chan *pch;
+       unsigned long flags;
+
+       if (list_empty(list))
+               return;
+
+       /* Finish off the work list */
+       list_for_each_entry(desc, list, node) {
+               dma_async_tx_callback callback;
+               void *param;
+
+               /* All desc in a list belong to same channel */
+               pch = desc->pchan;
+               callback = desc->txd.callback;
+               param = desc->txd.callback_param;
+
+               if (callback)
+                       callback(param);
+
+               desc->pchan = NULL;
+       }
+
+       pdmac = pch->dmac;
+
+       spin_lock_irqsave(&pdmac->pool_lock, flags);
+       list_splice_tail_init(list, &pdmac->desc_pool);
+       spin_unlock_irqrestore(&pdmac->pool_lock, flags);
+}
+
+static inline void fill_queue(struct dma_pl330_chan *pch)
+{
+       struct dma_pl330_desc *desc;
+       int ret;
+
+       list_for_each_entry(desc, &pch->work_list, node) {
+
+               /* If already submitted */
+               if (desc->status == BUSY)
+                       break;
+
+               ret = pl330_submit_req(pch->pl330_chid,
+                                               &desc->req);
+               if (!ret) {
+                       desc->status = BUSY;
+                       break;
+               } else if (ret == -EAGAIN) {
+                       /* QFull or DMAC Dying */
+                       break;
+               } else {
+                       /* Unacceptable request */
+                       desc->status = DONE;
+                       dev_err(pch->dmac->pif.dev, "%s:%d Bad Desc(%d)\n",
+                                       __func__, __LINE__, desc->txd.cookie);
+                       tasklet_schedule(&pch->task);
+               }
+       }
+}
+
+static void pl330_tasklet(unsigned long data)
+{
+       struct dma_pl330_chan *pch = (struct dma_pl330_chan *)data;
+       struct dma_pl330_desc *desc, *_dt;
+       unsigned long flags;
+       LIST_HEAD(list);
+
+       spin_lock_irqsave(&pch->lock, flags);
+
+       /* Pick up ripe tomatoes */
+       list_for_each_entry_safe(desc, _dt, &pch->work_list, node)
+               if (desc->status == DONE) {
+                       pch->completed = desc->txd.cookie;
+                       list_move_tail(&desc->node, &list);
+               }
+
+       /* Try to submit a req imm. next to the last completed cookie */
+       fill_queue(pch);
+
+       /* Make sure the PL330 Channel thread is active */
+       pl330_chan_ctrl(pch->pl330_chid, PL330_OP_START);
+
+       spin_unlock_irqrestore(&pch->lock, flags);
+
+       free_desc_list(&list);
+}
+
+static void dma_pl330_rqcb(void *token, enum pl330_op_err err)
+{
+       struct dma_pl330_desc *desc = token;
+       struct dma_pl330_chan *pch = desc->pchan;
+       unsigned long flags;
+
+       /* If desc aborted */
+       if (!pch)
+               return;
+
+       spin_lock_irqsave(&pch->lock, flags);
+
+       desc->status = DONE;
+
+       spin_unlock_irqrestore(&pch->lock, flags);
+
+       tasklet_schedule(&pch->task);
+}
+
+static int pl330_alloc_chan_resources(struct dma_chan *chan)
+{
+       struct dma_pl330_chan *pch = to_pchan(chan);
+       struct dma_pl330_dmac *pdmac = pch->dmac;
+       unsigned long flags;
+
+       spin_lock_irqsave(&pch->lock, flags);
+
+       pch->completed = chan->cookie = 1;
+
+       pch->pl330_chid = pl330_request_channel(&pdmac->pif);
+       if (!pch->pl330_chid) {
+               spin_unlock_irqrestore(&pch->lock, flags);
+               return 0;
+       }
+
+       tasklet_init(&pch->task, pl330_tasklet, (unsigned long) pch);
+
+       spin_unlock_irqrestore(&pch->lock, flags);
+
+       return 1;
+}
+
+static int pl330_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, unsigned long arg)
+{
+       struct dma_pl330_chan *pch = to_pchan(chan);
+       struct dma_pl330_desc *desc;
+       unsigned long flags;
+
+       /* Only supports DMA_TERMINATE_ALL */
+       if (cmd != DMA_TERMINATE_ALL)
+               return -ENXIO;
+
+       spin_lock_irqsave(&pch->lock, flags);
+
+       /* FLUSH the PL330 Channel thread */
+       pl330_chan_ctrl(pch->pl330_chid, PL330_OP_FLUSH);
+
+       /* Mark all desc done */
+       list_for_each_entry(desc, &pch->work_list, node)
+               desc->status = DONE;
+
+       spin_unlock_irqrestore(&pch->lock, flags);
+
+       pl330_tasklet((unsigned long) pch);
+
+       return 0;
+}
+
+static void pl330_free_chan_resources(struct dma_chan *chan)
+{
+       struct dma_pl330_chan *pch = to_pchan(chan);
+       unsigned long flags;
+
+       spin_lock_irqsave(&pch->lock, flags);
+
+       tasklet_kill(&pch->task);
+
+       pl330_release_channel(pch->pl330_chid);
+       pch->pl330_chid = NULL;
+
+       spin_unlock_irqrestore(&pch->lock, flags);
+}
+
+static enum dma_status
+pl330_tx_status(struct dma_chan *chan, dma_cookie_t cookie,
+                struct dma_tx_state *txstate)
+{
+       struct dma_pl330_chan *pch = to_pchan(chan);
+       dma_cookie_t last_done, last_used;
+       int ret;
+
+       last_done = pch->completed;
+       last_used = chan->cookie;
+
+       ret = dma_async_is_complete(cookie, last_done, last_used);
+
+       dma_set_tx_state(txstate, last_done, last_used, 0);
+
+       return ret;
+}
+
+static void pl330_issue_pending(struct dma_chan *chan)
+{
+       pl330_tasklet((unsigned long) to_pchan(chan));
+}
+
+/*
+ * We returned the last one of the circular list of descriptor(s)
+ * from prep_xxx, so the argument to submit corresponds to the last
+ * descriptor of the list.
+ */
+static dma_cookie_t pl330_tx_submit(struct dma_async_tx_descriptor *tx)
+{
+       struct dma_pl330_desc *desc, *last = to_desc(tx);
+       struct dma_pl330_chan *pch = to_pchan(tx->chan);
+       dma_cookie_t cookie;
+       unsigned long flags;
+
+       spin_lock_irqsave(&pch->lock, flags);
+
+       /* Assign cookies to all nodes */
+       cookie = tx->chan->cookie;
+
+       while (!list_empty(&last->node)) {
+               desc = list_entry(last->node.next, struct dma_pl330_desc, node);
+
+               if (++cookie < 0)
+                       cookie = 1;
+               desc->txd.cookie = cookie;
+
+               list_move_tail(&desc->node, &pch->work_list);
+       }
+
+       if (++cookie < 0)
+               cookie = 1;
+       last->txd.cookie = cookie;
+
+       list_add_tail(&last->node, &pch->work_list);
+
+       tx->chan->cookie = cookie;
+
+       spin_unlock_irqrestore(&pch->lock, flags);
+
+       return cookie;
+}
+
+static inline void _init_desc(struct dma_pl330_desc *desc)
+{
+       desc->pchan = NULL;
+       desc->req.x = &desc->px;
+       desc->req.token = desc;
+       desc->rqcfg.swap = SWAP_NO;
+       desc->rqcfg.privileged = 0;
+       desc->rqcfg.insnaccess = 0;
+       desc->rqcfg.scctl = SCCTRL0;
+       desc->rqcfg.dcctl = DCCTRL0;
+       desc->req.cfg = &desc->rqcfg;
+       desc->req.xfer_cb = dma_pl330_rqcb;
+       desc->txd.tx_submit = pl330_tx_submit;
+
+       INIT_LIST_HEAD(&desc->node);
+}
+
+/* Returns the number of descriptors added to the DMAC pool */
+int add_desc(struct dma_pl330_dmac *pdmac, gfp_t flg, int count)
+{
+       struct dma_pl330_desc *desc;
+       unsigned long flags;
+       int i;
+
+       if (!pdmac)
+               return 0;
+
+       desc = kmalloc(count * sizeof(*desc), flg);
+       if (!desc)
+               return 0;
+
+       spin_lock_irqsave(&pdmac->pool_lock, flags);
+
+       for (i = 0; i < count; i++) {
+               _init_desc(&desc[i]);
+               list_add_tail(&desc[i].node, &pdmac->desc_pool);
+       }
+
+       spin_unlock_irqrestore(&pdmac->pool_lock, flags);
+
+       return count;
+}
+
+static struct dma_pl330_desc *
+pluck_desc(struct dma_pl330_dmac *pdmac)
+{
+       struct dma_pl330_desc *desc = NULL;
+       unsigned long flags;
+
+       if (!pdmac)
+               return NULL;
+
+       spin_lock_irqsave(&pdmac->pool_lock, flags);
+
+       if (!list_empty(&pdmac->desc_pool)) {
+               desc = list_entry(pdmac->desc_pool.next,
+                               struct dma_pl330_desc, node);
+
+               list_del_init(&desc->node);
+
+               desc->status = PREP;
+               desc->txd.callback = NULL;
+       }
+
+       spin_unlock_irqrestore(&pdmac->pool_lock, flags);
+
+       return desc;
+}
+
+static struct dma_pl330_desc *pl330_get_desc(struct dma_pl330_chan *pch)
+{
+       struct dma_pl330_dmac *pdmac = pch->dmac;
+       struct dma_pl330_peri *peri = pch->chan.private;
+       struct dma_pl330_desc *desc;
+
+       /* Pluck one desc from the pool of DMAC */
+       desc = pluck_desc(pdmac);
+
+       /* If the DMAC pool is empty, alloc new */
+       if (!desc) {
+               if (!add_desc(pdmac, GFP_ATOMIC, 1))
+                       return NULL;
+
+               /* Try again */
+               desc = pluck_desc(pdmac);
+               if (!desc) {
+                       dev_err(pch->dmac->pif.dev,
+                               "%s:%d ALERT!\n", __func__, __LINE__);
+                       return NULL;
+               }
+       }
+
+       /* Initialize the descriptor */
+       desc->pchan = pch;
+       desc->txd.cookie = 0;
+       async_tx_ack(&desc->txd);
+
+       desc->req.rqtype = peri->rqtype;
+       desc->req.peri = peri->peri_id;
+
+       dma_async_tx_descriptor_init(&desc->txd, &pch->chan);
+
+       return desc;
+}
+
+static inline void fill_px(struct pl330_xfer *px,
+               dma_addr_t dst, dma_addr_t src, size_t len)
+{
+       px->next = NULL;
+       px->bytes = len;
+       px->dst_addr = dst;
+       px->src_addr = src;
+}
+
+static struct dma_pl330_desc *
+__pl330_prep_dma_memcpy(struct dma_pl330_chan *pch, dma_addr_t dst,
+               dma_addr_t src, size_t len)
+{
+       struct dma_pl330_desc *desc = pl330_get_desc(pch);
+
+       if (!desc) {
+               dev_err(pch->dmac->pif.dev, "%s:%d Unable to fetch desc\n",
+                       __func__, __LINE__);
+               return NULL;
+       }
+
+       /*
+        * Ideally we should lookout for reqs bigger than
+        * those that can be programmed with 256 bytes of
+        * MC buffer, but considering a req size is seldom
+        * going to be word-unaligned and more than 200MB,
+        * we take it easy.
+        * Also, should the limit is reached we'd rather
+        * have the platform increase MC buffer size than
+        * complicating this API driver.
+        */
+       fill_px(&desc->px, dst, src, len);
+
+       return desc;
+}
+
+/* Call after fixing burst size */
+static inline int get_burst_len(struct dma_pl330_desc *desc, size_t len)
+{
+       struct dma_pl330_chan *pch = desc->pchan;
+       struct pl330_info *pi = &pch->dmac->pif;
+       int burst_len;
+
+       burst_len = pi->pcfg.data_bus_width / 8;
+       burst_len *= pi->pcfg.data_buf_dep;
+       burst_len >>= desc->rqcfg.brst_size;
+
+       /* src/dst_burst_len can't be more than 16 */
+       if (burst_len > 16)
+               burst_len = 16;
+
+       while (burst_len > 1) {
+               if (!(len % (burst_len << desc->rqcfg.brst_size)))
+                       break;
+               burst_len--;
+       }
+
+       return burst_len;
+}
+
+static struct dma_async_tx_descriptor *
+pl330_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dst,
+               dma_addr_t src, size_t len, unsigned long flags)
+{
+       struct dma_pl330_desc *desc;
+       struct dma_pl330_chan *pch = to_pchan(chan);
+       struct dma_pl330_peri *peri = chan->private;
+       struct pl330_info *pi;
+       int burst;
+
+       if (unlikely(!pch || !len || !peri))
+               return NULL;
+
+       if (peri->rqtype != MEMTOMEM)
+               return NULL;
+
+       pi = &pch->dmac->pif;
+
+       desc = __pl330_prep_dma_memcpy(pch, dst, src, len);
+       if (!desc)
+               return NULL;
+
+       desc->rqcfg.src_inc = 1;
+       desc->rqcfg.dst_inc = 1;
+
+       /* Select max possible burst size */
+       burst = pi->pcfg.data_bus_width / 8;
+
+       while (burst > 1) {
+               if (!(len % burst))
+                       break;
+               burst /= 2;
+       }
+
+       desc->rqcfg.brst_size = 0;
+       while (burst != (1 << desc->rqcfg.brst_size))
+               desc->rqcfg.brst_size++;
+
+       desc->rqcfg.brst_len = get_burst_len(desc, len);
+
+       desc->txd.flags = flags;
+
+       return &desc->txd;
+}
+
+static struct dma_async_tx_descriptor *
+pl330_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
+               unsigned int sg_len, enum dma_data_direction direction,
+               unsigned long flg)
+{
+       struct dma_pl330_desc *first, *desc = NULL;
+       struct dma_pl330_chan *pch = to_pchan(chan);
+       struct dma_pl330_peri *peri = chan->private;
+       struct scatterlist *sg;
+       unsigned long flags;
+       int i, burst_size;
+       dma_addr_t addr;
+
+       if (unlikely(!pch || !sgl || !sg_len))
+               return NULL;
+
+       /* Make sure the direction is consistent */
+       if ((direction == DMA_TO_DEVICE &&
+                               peri->rqtype != MEMTODEV) ||
+                       (direction == DMA_FROM_DEVICE &&
+                               peri->rqtype != DEVTOMEM)) {
+               dev_err(pch->dmac->pif.dev, "%s:%d Invalid Direction\n",
+                               __func__, __LINE__);
+               return NULL;
+       }
+
+       addr = peri->fifo_addr;
+       burst_size = peri->burst_sz;
+
+       first = NULL;
+
+       for_each_sg(sgl, sg, sg_len, i) {
+
+               desc = pl330_get_desc(pch);
+               if (!desc) {
+                       struct dma_pl330_dmac *pdmac = pch->dmac;
+
+                       dev_err(pch->dmac->pif.dev,
+                               "%s:%d Unable to fetch desc\n",
+                               __func__, __LINE__);
+                       if (!first)
+                               return NULL;
+
+                       spin_lock_irqsave(&pdmac->pool_lock, flags);
+
+                       while (!list_empty(&first->node)) {
+                               desc = list_entry(first->node.next,
+                                               struct dma_pl330_desc, node);
+                               list_move_tail(&desc->node, &pdmac->desc_pool);
+                       }
+
+                       list_move_tail(&first->node, &pdmac->desc_pool);
+
+                       spin_unlock_irqrestore(&pdmac->pool_lock, flags);
+
+                       return NULL;
+               }
+
+               if (!first)
+                       first = desc;
+               else
+                       list_add_tail(&desc->node, &first->node);
+
+               if (direction == DMA_TO_DEVICE) {
+                       desc->rqcfg.src_inc = 1;
+                       desc->rqcfg.dst_inc = 0;
+                       fill_px(&desc->px,
+                               addr, sg_dma_address(sg), sg_dma_len(sg));
+               } else {
+                       desc->rqcfg.src_inc = 0;
+                       desc->rqcfg.dst_inc = 1;
+                       fill_px(&desc->px,
+                               sg_dma_address(sg), addr, sg_dma_len(sg));
+               }
+
+               desc->rqcfg.brst_size = burst_size;
+               desc->rqcfg.brst_len = 1;
+       }
+
+       /* Return the last desc in the chain */
+       desc->txd.flags = flg;
+       return &desc->txd;
+}
+
+static irqreturn_t pl330_irq_handler(int irq, void *data)
+{
+       if (pl330_update(data))
+               return IRQ_HANDLED;
+       else
+               return IRQ_NONE;
+}
+
+static int __devinit
+pl330_probe(struct amba_device *adev, struct amba_id *id)
+{
+       struct dma_pl330_platdata *pdat;
+       struct dma_pl330_dmac *pdmac;
+       struct dma_pl330_chan *pch;
+       struct pl330_info *pi;
+       struct dma_device *pd;
+       struct resource *res;
+       int i, ret, irq;
+
+       pdat = adev->dev.platform_data;
+
+       if (!pdat || !pdat->nr_valid_peri) {
+               dev_err(&adev->dev, "platform data missing\n");
+               return -ENODEV;
+       }
+
+       /* Allocate a new DMAC and its Channels */
+       pdmac = kzalloc(pdat->nr_valid_peri * sizeof(*pch)
+                               + sizeof(*pdmac), GFP_KERNEL);
+       if (!pdmac) {
+               dev_err(&adev->dev, "unable to allocate mem\n");
+               return -ENOMEM;
+       }
+
+       pi = &pdmac->pif;
+       pi->dev = &adev->dev;
+       pi->pl330_data = NULL;
+       pi->mcbufsz = pdat->mcbuf_sz;
+
+       res = &adev->res;
+       request_mem_region(res->start, resource_size(res), "dma-pl330");
+
+       pi->base = ioremap(res->start, resource_size(res));
+       if (!pi->base) {
+               ret = -ENXIO;
+               goto probe_err1;
+       }
+
+       irq = adev->irq[0];
+       ret = request_irq(irq, pl330_irq_handler, 0,
+                       dev_name(&adev->dev), pi);
+       if (ret)
+               goto probe_err2;
+
+       ret = pl330_add(pi);
+       if (ret)
+               goto probe_err3;
+
+       INIT_LIST_HEAD(&pdmac->desc_pool);
+       spin_lock_init(&pdmac->pool_lock);
+
+       /* Create a descriptor pool of default size */
+       if (!add_desc(pdmac, GFP_KERNEL, NR_DEFAULT_DESC))
+               dev_warn(&adev->dev, "unable to allocate desc\n");
+
+       pd = &pdmac->ddma;
+       INIT_LIST_HEAD(&pd->channels);
+
+       /* Initialize channel parameters */
+       for (i = 0; i < pdat->nr_valid_peri; i++) {
+               struct dma_pl330_peri *peri = &pdat->peri[i];
+               pch = &pdmac->peripherals[i];
+
+               switch (peri->rqtype) {
+               case MEMTOMEM:
+                       dma_cap_set(DMA_MEMCPY, pd->cap_mask);
+                       break;
+               case MEMTODEV:
+               case DEVTOMEM:
+                       dma_cap_set(DMA_SLAVE, pd->cap_mask);
+                       break;
+               default:
+                       dev_err(&adev->dev, "DEVTODEV Not Supported\n");
+                       continue;
+               }
+
+               INIT_LIST_HEAD(&pch->work_list);
+               spin_lock_init(&pch->lock);
+               pch->pl330_chid = NULL;
+               pch->chan.private = peri;
+               pch->chan.device = pd;
+               pch->chan.chan_id = i;
+               pch->dmac = pdmac;
+
+               /* Add the channel to the DMAC list */
+               pd->chancnt++;
+               list_add_tail(&pch->chan.device_node, &pd->channels);
+       }
+
+       pd->dev = &adev->dev;
+
+       pd->device_alloc_chan_resources = pl330_alloc_chan_resources;
+       pd->device_free_chan_resources = pl330_free_chan_resources;
+       pd->device_prep_dma_memcpy = pl330_prep_dma_memcpy;
+       pd->device_tx_status = pl330_tx_status;
+       pd->device_prep_slave_sg = pl330_prep_slave_sg;
+       pd->device_control = pl330_control;
+       pd->device_issue_pending = pl330_issue_pending;
+
+       ret = dma_async_device_register(pd);
+       if (ret) {
+               dev_err(&adev->dev, "unable to register DMAC\n");
+               goto probe_err4;
+       }
+
+       amba_set_drvdata(adev, pdmac);
+
+       dev_info(&adev->dev,
+               "Loaded driver for PL330 DMAC-%d\n", adev->periphid);
+       dev_info(&adev->dev,
+               "\tDBUFF-%ux%ubytes Num_Chans-%u Num_Peri-%u Num_Events-%u\n",
+               pi->pcfg.data_buf_dep,
+               pi->pcfg.data_bus_width / 8, pi->pcfg.num_chan,
+               pi->pcfg.num_peri, pi->pcfg.num_events);
+
+       return 0;
+
+probe_err4:
+       pl330_del(pi);
+probe_err3:
+       free_irq(irq, pi);
+probe_err2:
+       iounmap(pi->base);
+probe_err1:
+       release_mem_region(res->start, resource_size(res));
+       kfree(pdmac);
+
+       return ret;
+}
+
+static int __devexit pl330_remove(struct amba_device *adev)
+{
+       struct dma_pl330_dmac *pdmac = amba_get_drvdata(adev);
+       struct dma_pl330_chan *pch, *_p;
+       struct pl330_info *pi;
+       struct resource *res;
+       int irq;
+
+       if (!pdmac)
+               return 0;
+
+       amba_set_drvdata(adev, NULL);
+
+       /* Idle the DMAC */
+       list_for_each_entry_safe(pch, _p, &pdmac->ddma.channels,
+                       chan.device_node) {
+
+               /* Remove the channel */
+               list_del(&pch->chan.device_node);
+
+               /* Flush the channel */
+               pl330_control(&pch->chan, DMA_TERMINATE_ALL, 0);
+               pl330_free_chan_resources(&pch->chan);
+       }
+
+       pi = &pdmac->pif;
+
+       pl330_del(pi);
+
+       irq = adev->irq[0];
+       free_irq(irq, pi);
+
+       iounmap(pi->base);
+
+       res = &adev->res;
+       release_mem_region(res->start, resource_size(res));
+
+       kfree(pdmac);
+
+       return 0;
+}
+
+static struct amba_id pl330_ids[] = {
+       {
+               .id     = 0x00041330,
+               .mask   = 0x000fffff,
+       },
+       { 0, 0 },
+};
+
+static struct amba_driver pl330_driver = {
+       .drv = {
+               .owner = THIS_MODULE,
+               .name = "dma-pl330",
+       },
+       .id_table = pl330_ids,
+       .probe = pl330_probe,
+       .remove = pl330_remove,
+};
+
+static int __init pl330_init(void)
+{
+       return amba_driver_register(&pl330_driver);
+}
+module_init(pl330_init);
+
+static void __exit pl330_exit(void)
+{
+       amba_driver_unregister(&pl330_driver);
+       return;
+}
+module_exit(pl330_exit);
+
+MODULE_AUTHOR("Jaswinder Singh <jassi.brar@samsung.com>");
+MODULE_DESCRIPTION("API Driver for PL330 DMAC");
+MODULE_LICENSE("GPL");
index 4fd0f27..724038d 100644 (file)
@@ -195,6 +195,13 @@ config GPIO_PCF857X
          This driver provides an in-kernel interface to those GPIOs using
          platform-neutral GPIO calls.
 
+config GPIO_TC35892
+       bool "TC35892 GPIOs"
+       depends on MFD_TC35892
+       help
+         This enables support for the GPIOs found on the TC35892
+         I/O Expander.
+
 config GPIO_TWL4030
        tristate "TWL4030, TWL5030, and TPS659x0 GPIOs"
        depends on TWL4030_CORE
@@ -282,6 +289,15 @@ config GPIO_TIMBERDALE
        ---help---
        Add support for the GPIO IP in the timberdale FPGA.
 
+config GPIO_RDC321X
+       tristate "RDC R-321x GPIO support"
+       depends on PCI && GPIOLIB
+       select MFD_CORE
+       select MFD_RDC321X
+       help
+         Support for the RDC R321x SoC GPIOs over southbridge
+         PCI configuration space.
+
 comment "SPI GPIO expanders:"
 
 config GPIO_MAX7301
@@ -317,4 +333,14 @@ config GPIO_UCB1400
          To compile this driver as a module, choose M here: the
          module will be called ucb1400_gpio.
 
+comment "MODULbus GPIO expanders:"
+
+config GPIO_JANZ_TTL
+       tristate "Janz VMOD-TTL Digital IO Module"
+       depends on MFD_JANZ_CMODIO
+       help
+         This enables support for the Janz VMOD-TTL Digital IO module.
+         This driver provides support for driving the pins in output
+         mode only. Input mode is not supported.
+
 endif
index 10f3f8d..51c3cdd 100644 (file)
@@ -16,6 +16,7 @@ obj-$(CONFIG_GPIO_MCP23S08)   += mcp23s08.o
 obj-$(CONFIG_GPIO_PCA953X)     += pca953x.o
 obj-$(CONFIG_GPIO_PCF857X)     += pcf857x.o
 obj-$(CONFIG_GPIO_PL061)       += pl061.o
+obj-$(CONFIG_GPIO_TC35892)     += tc35892-gpio.o
 obj-$(CONFIG_GPIO_TIMBERDALE)  += timbgpio.o
 obj-$(CONFIG_GPIO_TWL4030)     += twl4030-gpio.o
 obj-$(CONFIG_GPIO_UCB1400)     += ucb1400_gpio.o
@@ -27,4 +28,6 @@ obj-$(CONFIG_GPIO_VR41XX)     += vr41xx_giu.o
 obj-$(CONFIG_GPIO_WM831X)      += wm831x-gpio.o
 obj-$(CONFIG_GPIO_WM8350)      += wm8350-gpiolib.o
 obj-$(CONFIG_GPIO_WM8994)      += wm8994-gpio.o
-obj-$(CONFIG_GPIO_SCH)         += sch_gpio.o
\ No newline at end of file
+obj-$(CONFIG_GPIO_SCH)         += sch_gpio.o
+obj-$(CONFIG_GPIO_RDC321X)     += rdc321x-gpio.o
+obj-$(CONFIG_GPIO_JANZ_TTL)    += janz-ttl.o
diff --git a/drivers/gpio/janz-ttl.c b/drivers/gpio/janz-ttl.c
new file mode 100644 (file)
index 0000000..813ac07
--- /dev/null
@@ -0,0 +1,258 @@
+/*
+ * Janz MODULbus VMOD-TTL GPIO Driver
+ *
+ * Copyright (c) 2010 Ira W. Snyder <iws@ovro.caltech.edu>
+ *
+ * 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/interrupt.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/gpio.h>
+#include <linux/slab.h>
+
+#include <linux/mfd/janz.h>
+
+#define DRV_NAME "janz-ttl"
+
+#define PORTA_DIRECTION                0x23
+#define PORTB_DIRECTION                0x2B
+#define PORTC_DIRECTION                0x06
+#define PORTA_IOCTL            0x24
+#define PORTB_IOCTL            0x2C
+#define PORTC_IOCTL            0x07
+
+#define MASTER_INT_CTL         0x00
+#define MASTER_CONF_CTL                0x01
+
+#define CONF_PAE               (1 << 2)
+#define CONF_PBE               (1 << 7)
+#define CONF_PCE               (1 << 4)
+
+struct ttl_control_regs {
+       __be16 portc;
+       __be16 portb;
+       __be16 porta;
+       __be16 control;
+};
+
+struct ttl_module {
+       struct gpio_chip gpio;
+
+       /* base address of registers */
+       struct ttl_control_regs __iomem *regs;
+
+       u8 portc_shadow;
+       u8 portb_shadow;
+       u8 porta_shadow;
+
+       spinlock_t lock;
+};
+
+static int ttl_get_value(struct gpio_chip *gpio, unsigned offset)
+{
+       struct ttl_module *mod = dev_get_drvdata(gpio->dev);
+       u8 *shadow;
+       int ret;
+
+       if (offset < 8) {
+               shadow = &mod->porta_shadow;
+       } else if (offset < 16) {
+               shadow = &mod->portb_shadow;
+               offset -= 8;
+       } else {
+               shadow = &mod->portc_shadow;
+               offset -= 16;
+       }
+
+       spin_lock(&mod->lock);
+       ret = *shadow & (1 << offset);
+       spin_unlock(&mod->lock);
+       return ret;
+}
+
+static void ttl_set_value(struct gpio_chip *gpio, unsigned offset, int value)
+{
+       struct ttl_module *mod = dev_get_drvdata(gpio->dev);
+       void __iomem *port;
+       u8 *shadow;
+
+       if (offset < 8) {
+               port = &mod->regs->porta;
+               shadow = &mod->porta_shadow;
+       } else if (offset < 16) {
+               port = &mod->regs->portb;
+               shadow = &mod->portb_shadow;
+               offset -= 8;
+       } else {
+               port = &mod->regs->portc;
+               shadow = &mod->portc_shadow;
+               offset -= 16;
+       }
+
+       spin_lock(&mod->lock);
+       if (value)
+               *shadow |= (1 << offset);
+       else
+               *shadow &= ~(1 << offset);
+
+       iowrite16be(*shadow, port);
+       spin_unlock(&mod->lock);
+}
+
+static void __devinit ttl_write_reg(struct ttl_module *mod, u8 reg, u16 val)
+{
+       iowrite16be(reg, &mod->regs->control);
+       iowrite16be(val, &mod->regs->control);
+}
+
+static void __devinit ttl_setup_device(struct ttl_module *mod)
+{
+       /* reset the device to a known state */
+       iowrite16be(0x0000, &mod->regs->control);
+       iowrite16be(0x0001, &mod->regs->control);
+       iowrite16be(0x0000, &mod->regs->control);
+
+       /* put all ports in open-drain mode */
+       ttl_write_reg(mod, PORTA_IOCTL, 0x00ff);
+       ttl_write_reg(mod, PORTB_IOCTL, 0x00ff);
+       ttl_write_reg(mod, PORTC_IOCTL, 0x000f);
+
+       /* set all ports as outputs */
+       ttl_write_reg(mod, PORTA_DIRECTION, 0x0000);
+       ttl_write_reg(mod, PORTB_DIRECTION, 0x0000);
+       ttl_write_reg(mod, PORTC_DIRECTION, 0x0000);
+
+       /* set all ports to drive zeroes */
+       iowrite16be(0x0000, &mod->regs->porta);
+       iowrite16be(0x0000, &mod->regs->portb);
+       iowrite16be(0x0000, &mod->regs->portc);
+
+       /* enable all ports */
+       ttl_write_reg(mod, MASTER_CONF_CTL, CONF_PAE | CONF_PBE | CONF_PCE);
+}
+
+static int __devinit ttl_probe(struct platform_device *pdev)
+{
+       struct janz_platform_data *pdata;
+       struct device *dev = &pdev->dev;
+       struct ttl_module *mod;
+       struct gpio_chip *gpio;
+       struct resource *res;
+       int ret;
+
+       pdata = pdev->dev.platform_data;
+       if (!pdata) {
+               dev_err(dev, "no platform data\n");
+               ret = -ENXIO;
+               goto out_return;
+       }
+
+       mod = kzalloc(sizeof(*mod), GFP_KERNEL);
+       if (!mod) {
+               dev_err(dev, "unable to allocate private data\n");
+               ret = -ENOMEM;
+               goto out_return;
+       }
+
+       platform_set_drvdata(pdev, mod);
+       spin_lock_init(&mod->lock);
+
+       /* get access to the MODULbus registers for this module */
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (!res) {
+               dev_err(dev, "MODULbus registers not found\n");
+               ret = -ENODEV;
+               goto out_free_mod;
+       }
+
+       mod->regs = ioremap(res->start, resource_size(res));
+       if (!mod->regs) {
+               dev_err(dev, "MODULbus registers not ioremap\n");
+               ret = -ENOMEM;
+               goto out_free_mod;
+       }
+
+       ttl_setup_device(mod);
+
+       /* Initialize the GPIO data structures */
+       gpio = &mod->gpio;
+       gpio->dev = &pdev->dev;
+       gpio->label = pdev->name;
+       gpio->get = ttl_get_value;
+       gpio->set = ttl_set_value;
+       gpio->owner = THIS_MODULE;
+
+       /* request dynamic allocation */
+       gpio->base = -1;
+       gpio->ngpio = 20;
+
+       ret = gpiochip_add(gpio);
+       if (ret) {
+               dev_err(dev, "unable to add GPIO chip\n");
+               goto out_iounmap_regs;
+       }
+
+       dev_info(&pdev->dev, "module %d: registered GPIO device\n",
+                            pdata->modno);
+       return 0;
+
+out_iounmap_regs:
+       iounmap(mod->regs);
+out_free_mod:
+       kfree(mod);
+out_return:
+       return ret;
+}
+
+static int __devexit ttl_remove(struct platform_device *pdev)
+{
+       struct ttl_module *mod = platform_get_drvdata(pdev);
+       struct device *dev = &pdev->dev;
+       int ret;
+
+       ret = gpiochip_remove(&mod->gpio);
+       if (ret) {
+               dev_err(dev, "unable to remove GPIO chip\n");
+               return ret;
+       }
+
+       iounmap(mod->regs);
+       kfree(mod);
+       return 0;
+}
+
+static struct platform_driver ttl_driver = {
+       .driver         = {
+               .name   = DRV_NAME,
+               .owner  = THIS_MODULE,
+       },
+       .probe          = ttl_probe,
+       .remove         = __devexit_p(ttl_remove),
+};
+
+static int __init ttl_init(void)
+{
+       return platform_driver_register(&ttl_driver);
+}
+
+static void __exit ttl_exit(void)
+{
+       platform_driver_unregister(&ttl_driver);
+}
+
+MODULE_AUTHOR("Ira W. Snyder <iws@ovro.caltech.edu>");
+MODULE_DESCRIPTION("Janz MODULbus VMOD-TTL Driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:janz-ttl");
+
+module_init(ttl_init);
+module_exit(ttl_exit);
diff --git a/drivers/gpio/rdc321x-gpio.c b/drivers/gpio/rdc321x-gpio.c
new file mode 100644 (file)
index 0000000..2762698
--- /dev/null
@@ -0,0 +1,246 @@
+/*
+ * RDC321x GPIO driver
+ *
+ * Copyright (C) 2008, Volker Weiss <dev@tintuc.de>
+ * Copyright (C) 2007-2010 Florian Fainelli <florian@openwrt.org>
+ *
+ * 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.
+ *
+ */
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/spinlock.h>
+#include <linux/platform_device.h>
+#include <linux/pci.h>
+#include <linux/gpio.h>
+#include <linux/mfd/rdc321x.h>
+#include <linux/slab.h>
+
+struct rdc321x_gpio {
+       spinlock_t              lock;
+       struct pci_dev          *sb_pdev;
+       u32                     data_reg[2];
+       int                     reg1_ctrl_base;
+       int                     reg1_data_base;
+       int                     reg2_ctrl_base;
+       int                     reg2_data_base;
+       struct gpio_chip        chip;
+};
+
+/* read GPIO pin */
+static int rdc_gpio_get_value(struct gpio_chip *chip, unsigned gpio)
+{
+       struct rdc321x_gpio *gpch;
+       u32 value = 0;
+       int reg;
+
+       gpch = container_of(chip, struct rdc321x_gpio, chip);
+       reg = gpio < 32 ? gpch->reg1_data_base : gpch->reg2_data_base;
+
+       spin_lock(&gpch->lock);
+       pci_write_config_dword(gpch->sb_pdev, reg,
+                                       gpch->data_reg[gpio < 32 ? 0 : 1]);
+       pci_read_config_dword(gpch->sb_pdev, reg, &value);
+       spin_unlock(&gpch->lock);
+
+       return (1 << (gpio & 0x1f)) & value ? 1 : 0;
+}
+
+static void rdc_gpio_set_value_impl(struct gpio_chip *chip,
+                               unsigned gpio, int value)
+{
+       struct rdc321x_gpio *gpch;
+       int reg = (gpio < 32) ? 0 : 1;
+
+       gpch = container_of(chip, struct rdc321x_gpio, chip);
+
+       if (value)
+               gpch->data_reg[reg] |= 1 << (gpio & 0x1f);
+       else
+               gpch->data_reg[reg] &= ~(1 << (gpio & 0x1f));
+
+       pci_write_config_dword(gpch->sb_pdev,
+                       reg ? gpch->reg2_data_base : gpch->reg1_data_base,
+                       gpch->data_reg[reg]);
+}
+
+/* set GPIO pin to value */
+static void rdc_gpio_set_value(struct gpio_chip *chip,
+                               unsigned gpio, int value)
+{
+       struct rdc321x_gpio *gpch;
+
+       gpch = container_of(chip, struct rdc321x_gpio, chip);
+       spin_lock(&gpch->lock);
+       rdc_gpio_set_value_impl(chip, gpio, value);
+       spin_unlock(&gpch->lock);
+}
+
+static int rdc_gpio_config(struct gpio_chip *chip,
+                               unsigned gpio, int value)
+{
+       struct rdc321x_gpio *gpch;
+       int err;
+       u32 reg;
+
+       gpch = container_of(chip, struct rdc321x_gpio, chip);
+
+       spin_lock(&gpch->lock);
+       err = pci_read_config_dword(gpch->sb_pdev, gpio < 32 ?
+                       gpch->reg1_ctrl_base : gpch->reg2_ctrl_base, &reg);
+       if (err)
+               goto unlock;
+
+       reg |= 1 << (gpio & 0x1f);
+
+       err = pci_write_config_dword(gpch->sb_pdev, gpio < 32 ?
+                       gpch->reg1_ctrl_base : gpch->reg2_ctrl_base, reg);
+       if (err)
+               goto unlock;
+
+       rdc_gpio_set_value_impl(chip, gpio, value);
+
+unlock:
+       spin_unlock(&gpch->lock);
+
+       return err;
+}
+
+/* configure GPIO pin as input */
+static int rdc_gpio_direction_input(struct gpio_chip *chip, unsigned gpio)
+{
+       return rdc_gpio_config(chip, gpio, 1);
+}
+
+/*
+ * Cache the initial value of both GPIO data registers
+ */
+static int __devinit rdc321x_gpio_probe(struct platform_device *pdev)
+{
+       int err;
+       struct resource *r;
+       struct rdc321x_gpio *rdc321x_gpio_dev;
+       struct rdc321x_gpio_pdata *pdata;
+
+       pdata = pdev->dev.platform_data;
+       if (!pdata) {
+               dev_err(&pdev->dev, "no platform data supplied\n");
+               return -ENODEV;
+       }
+
+       rdc321x_gpio_dev = kzalloc(sizeof(struct rdc321x_gpio), GFP_KERNEL);
+       if (!rdc321x_gpio_dev) {
+               dev_err(&pdev->dev, "failed to allocate private data\n");
+               return -ENOMEM;
+       }
+
+       r = platform_get_resource_byname(pdev, IORESOURCE_IO, "gpio-reg1");
+       if (!r) {
+               dev_err(&pdev->dev, "failed to get gpio-reg1 resource\n");
+               err = -ENODEV;
+               goto out_free;
+       }
+
+       spin_lock_init(&rdc321x_gpio_dev->lock);
+       rdc321x_gpio_dev->sb_pdev = pdata->sb_pdev;
+       rdc321x_gpio_dev->reg1_ctrl_base = r->start;
+       rdc321x_gpio_dev->reg1_data_base = r->start + 0x4;
+
+       r = platform_get_resource_byname(pdev, IORESOURCE_IO, "gpio-reg2");
+       if (!r) {
+               dev_err(&pdev->dev, "failed to get gpio-reg2 resource\n");
+               err = -ENODEV;
+               goto out_free;
+       }
+
+       rdc321x_gpio_dev->reg2_ctrl_base = r->start;
+       rdc321x_gpio_dev->reg2_data_base = r->start + 0x4;
+
+       rdc321x_gpio_dev->chip.label = "rdc321x-gpio";
+       rdc321x_gpio_dev->chip.direction_input = rdc_gpio_direction_input;
+       rdc321x_gpio_dev->chip.direction_output = rdc_gpio_config;
+       rdc321x_gpio_dev->chip.get = rdc_gpio_get_value;
+       rdc321x_gpio_dev->chip.set = rdc_gpio_set_value;
+       rdc321x_gpio_dev->chip.base = 0;
+       rdc321x_gpio_dev->chip.ngpio = pdata->max_gpios;
+
+       platform_set_drvdata(pdev, rdc321x_gpio_dev);
+
+       /* This might not be, what others (BIOS, bootloader, etc.)
+          wrote to these registers before, but it's a good guess. Still
+          better than just using 0xffffffff. */
+       err = pci_read_config_dword(rdc321x_gpio_dev->sb_pdev,
+                                       rdc321x_gpio_dev->reg1_data_base,
+                                       &rdc321x_gpio_dev->data_reg[0]);
+       if (err)
+               goto out_drvdata;
+
+       err = pci_read_config_dword(rdc321x_gpio_dev->sb_pdev,
+                                       rdc321x_gpio_dev->reg2_data_base,
+                                       &rdc321x_gpio_dev->data_reg[1]);
+       if (err)
+               goto out_drvdata;
+
+       dev_info(&pdev->dev, "registering %d GPIOs\n",
+                                       rdc321x_gpio_dev->chip.ngpio);
+       return gpiochip_add(&rdc321x_gpio_dev->chip);
+
+out_drvdata:
+       platform_set_drvdata(pdev, NULL);
+out_free:
+       kfree(rdc321x_gpio_dev);
+       return err;
+}
+
+static int __devexit rdc321x_gpio_remove(struct platform_device *pdev)
+{
+       int ret;
+       struct rdc321x_gpio *rdc321x_gpio_dev = platform_get_drvdata(pdev);
+
+       ret = gpiochip_remove(&rdc321x_gpio_dev->chip);
+       if (ret)
+               dev_err(&pdev->dev, "failed to unregister chip\n");
+
+       kfree(rdc321x_gpio_dev);
+       platform_set_drvdata(pdev, NULL);
+
+       return ret;
+}
+
+static struct platform_driver rdc321x_gpio_driver = {
+       .driver.name    = "rdc321x-gpio",
+       .driver.owner   = THIS_MODULE,
+       .probe          = rdc321x_gpio_probe,
+       .remove         = __devexit_p(rdc321x_gpio_remove),
+};
+
+static int __init rdc321x_gpio_init(void)
+{
+       return platform_driver_register(&rdc321x_gpio_driver);
+}
+
+static void __exit rdc321x_gpio_exit(void)
+{
+       platform_driver_unregister(&rdc321x_gpio_driver);
+}
+
+module_init(rdc321x_gpio_init);
+module_exit(rdc321x_gpio_exit);
+
+MODULE_AUTHOR("Florian Fainelli <florian@openwrt.org>");
+MODULE_DESCRIPTION("RDC321x GPIO driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:rdc321x-gpio");
diff --git a/drivers/gpio/tc35892-gpio.c b/drivers/gpio/tc35892-gpio.c
new file mode 100644 (file)
index 0000000..1be6288
--- /dev/null
@@ -0,0 +1,381 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ *
+ * License Terms: GNU General Public License, version 2
+ * Author: Hanumath Prasad <hanumath.prasad@stericsson.com> for ST-Ericsson
+ * Author: Rabin Vincent <rabin.vincent@stericsson.com> for ST-Ericsson
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/gpio.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/mfd/tc35892.h>
+
+/*
+ * These registers are modified under the irq bus lock and cached to avoid
+ * unnecessary writes in bus_sync_unlock.
+ */
+enum { REG_IBE, REG_IEV, REG_IS, REG_IE };
+
+#define CACHE_NR_REGS  4
+#define CACHE_NR_BANKS 3
+
+struct tc35892_gpio {
+       struct gpio_chip chip;
+       struct tc35892 *tc35892;
+       struct device *dev;
+       struct mutex irq_lock;
+
+       int irq_base;
+
+       /* Caches of interrupt control registers for bus_lock */
+       u8 regs[CACHE_NR_REGS][CACHE_NR_BANKS];
+       u8 oldregs[CACHE_NR_REGS][CACHE_NR_BANKS];
+};
+
+static inline struct tc35892_gpio *to_tc35892_gpio(struct gpio_chip *chip)
+{
+       return container_of(chip, struct tc35892_gpio, chip);
+}
+
+static int tc35892_gpio_get(struct gpio_chip *chip, unsigned offset)
+{
+       struct tc35892_gpio *tc35892_gpio = to_tc35892_gpio(chip);
+       struct tc35892 *tc35892 = tc35892_gpio->tc35892;
+       u8 reg = TC35892_GPIODATA0 + (offset / 8) * 2;
+       u8 mask = 1 << (offset % 8);
+       int ret;
+
+       ret = tc35892_reg_read(tc35892, reg);
+       if (ret < 0)
+               return ret;
+
+       return ret & mask;
+}
+
+static void tc35892_gpio_set(struct gpio_chip *chip, unsigned offset, int val)
+{
+       struct tc35892_gpio *tc35892_gpio = to_tc35892_gpio(chip);
+       struct tc35892 *tc35892 = tc35892_gpio->tc35892;
+       u8 reg = TC35892_GPIODATA0 + (offset / 8) * 2;
+       unsigned pos = offset % 8;
+       u8 data[] = {!!val << pos, 1 << pos};
+
+       tc35892_block_write(tc35892, reg, ARRAY_SIZE(data), data);
+}
+
+static int tc35892_gpio_direction_output(struct gpio_chip *chip,
+                                        unsigned offset, int val)
+{
+       struct tc35892_gpio *tc35892_gpio = to_tc35892_gpio(chip);
+       struct tc35892 *tc35892 = tc35892_gpio->tc35892;
+       u8 reg = TC35892_GPIODIR0 + offset / 8;
+       unsigned pos = offset % 8;
+
+       tc35892_gpio_set(chip, offset, val);
+
+       return tc35892_set_bits(tc35892, reg, 1 << pos, 1 << pos);
+}
+
+static int tc35892_gpio_direction_input(struct gpio_chip *chip,
+                                       unsigned offset)
+{
+       struct tc35892_gpio *tc35892_gpio = to_tc35892_gpio(chip);
+       struct tc35892 *tc35892 = tc35892_gpio->tc35892;
+       u8 reg = TC35892_GPIODIR0 + offset / 8;
+       unsigned pos = offset % 8;
+
+       return tc35892_set_bits(tc35892, reg, 1 << pos, 0);
+}
+
+static int tc35892_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
+{
+       struct tc35892_gpio *tc35892_gpio = to_tc35892_gpio(chip);
+
+       return tc35892_gpio->irq_base + offset;
+}
+
+static struct gpio_chip template_chip = {
+       .label                  = "tc35892",
+       .owner                  = THIS_MODULE,
+       .direction_input        = tc35892_gpio_direction_input,
+       .get                    = tc35892_gpio_get,
+       .direction_output       = tc35892_gpio_direction_output,
+       .set                    = tc35892_gpio_set,
+       .to_irq                 = tc35892_gpio_to_irq,
+       .can_sleep              = 1,
+};
+
+static int tc35892_gpio_irq_set_type(unsigned int irq, unsigned int type)
+{
+       struct tc35892_gpio *tc35892_gpio = get_irq_chip_data(irq);
+       int offset = irq - tc35892_gpio->irq_base;
+       int regoffset = offset / 8;
+       int mask = 1 << (offset % 8);
+
+       if (type == IRQ_TYPE_EDGE_BOTH) {
+               tc35892_gpio->regs[REG_IBE][regoffset] |= mask;
+               return 0;
+       }
+
+       tc35892_gpio->regs[REG_IBE][regoffset] &= ~mask;
+
+       if (type == IRQ_TYPE_LEVEL_LOW || type == IRQ_TYPE_LEVEL_HIGH)
+               tc35892_gpio->regs[REG_IS][regoffset] |= mask;
+       else
+               tc35892_gpio->regs[REG_IS][regoffset] &= ~mask;
+
+       if (type == IRQ_TYPE_EDGE_RISING || type == IRQ_TYPE_LEVEL_HIGH)
+               tc35892_gpio->regs[REG_IEV][regoffset] |= mask;
+       else
+               tc35892_gpio->regs[REG_IEV][regoffset] &= ~mask;
+
+       return 0;
+}
+
+static void tc35892_gpio_irq_lock(unsigned int irq)
+{
+       struct tc35892_gpio *tc35892_gpio = get_irq_chip_data(irq);
+
+       mutex_lock(&tc35892_gpio->irq_lock);
+}
+
+static void tc35892_gpio_irq_sync_unlock(unsigned int irq)
+{
+       struct tc35892_gpio *tc35892_gpio = get_irq_chip_data(irq);
+       struct tc35892 *tc35892 = tc35892_gpio->tc35892;
+       static const u8 regmap[] = {
+               [REG_IBE]       = TC35892_GPIOIBE0,
+               [REG_IEV]       = TC35892_GPIOIEV0,
+               [REG_IS]        = TC35892_GPIOIS0,
+               [REG_IE]        = TC35892_GPIOIE0,
+       };
+       int i, j;
+
+       for (i = 0; i < CACHE_NR_REGS; i++) {
+               for (j = 0; j < CACHE_NR_BANKS; j++) {
+                       u8 old = tc35892_gpio->oldregs[i][j];
+                       u8 new = tc35892_gpio->regs[i][j];
+
+                       if (new == old)
+                               continue;
+
+                       tc35892_gpio->oldregs[i][j] = new;
+                       tc35892_reg_write(tc35892, regmap[i] + j * 8, new);
+               }
+       }
+
+       mutex_unlock(&tc35892_gpio->irq_lock);
+}
+
+static void tc35892_gpio_irq_mask(unsigned int irq)
+{
+       struct tc35892_gpio *tc35892_gpio = get_irq_chip_data(irq);
+       int offset = irq - tc35892_gpio->irq_base;
+       int regoffset = offset / 8;
+       int mask = 1 << (offset % 8);
+
+       tc35892_gpio->regs[REG_IE][regoffset] &= ~mask;
+}
+
+static void tc35892_gpio_irq_unmask(unsigned int irq)
+{
+       struct tc35892_gpio *tc35892_gpio = get_irq_chip_data(irq);
+       int offset = irq - tc35892_gpio->irq_base;
+       int regoffset = offset / 8;
+       int mask = 1 << (offset % 8);
+
+       tc35892_gpio->regs[REG_IE][regoffset] |= mask;
+}
+
+static struct irq_chip tc35892_gpio_irq_chip = {
+       .name                   = "tc35892-gpio",
+       .bus_lock               = tc35892_gpio_irq_lock,
+       .bus_sync_unlock        = tc35892_gpio_irq_sync_unlock,
+       .mask                   = tc35892_gpio_irq_mask,
+       .unmask                 = tc35892_gpio_irq_unmask,
+       .set_type               = tc35892_gpio_irq_set_type,
+};
+
+static irqreturn_t tc35892_gpio_irq(int irq, void *dev)
+{
+       struct tc35892_gpio *tc35892_gpio = dev;
+       struct tc35892 *tc35892 = tc35892_gpio->tc35892;
+       u8 status[CACHE_NR_BANKS];
+       int ret;
+       int i;
+
+       ret = tc35892_block_read(tc35892, TC35892_GPIOMIS0,
+                                ARRAY_SIZE(status), status);
+       if (ret < 0)
+               return IRQ_NONE;
+
+       for (i = 0; i < ARRAY_SIZE(status); i++) {
+               unsigned int stat = status[i];
+               if (!stat)
+                       continue;
+
+               while (stat) {
+                       int bit = __ffs(stat);
+                       int line = i * 8 + bit;
+
+                       handle_nested_irq(tc35892_gpio->irq_base + line);
+                       stat &= ~(1 << bit);
+               }
+
+               tc35892_reg_write(tc35892, TC35892_GPIOIC0 + i, status[i]);
+       }
+
+       return IRQ_HANDLED;
+}
+
+static int tc35892_gpio_irq_init(struct tc35892_gpio *tc35892_gpio)
+{
+       int base = tc35892_gpio->irq_base;
+       int irq;
+
+       for (irq = base; irq < base + tc35892_gpio->chip.ngpio; irq++) {
+               set_irq_chip_data(irq, tc35892_gpio);
+               set_irq_chip_and_handler(irq, &tc35892_gpio_irq_chip,
+                                        handle_simple_irq);
+               set_irq_nested_thread(irq, 1);
+#ifdef CONFIG_ARM
+               set_irq_flags(irq, IRQF_VALID);
+#else
+               set_irq_noprobe(irq);
+#endif
+       }
+
+       return 0;
+}
+
+static void tc35892_gpio_irq_remove(struct tc35892_gpio *tc35892_gpio)
+{
+       int base = tc35892_gpio->irq_base;
+       int irq;
+
+       for (irq = base; irq < base + tc35892_gpio->chip.ngpio; irq++) {
+#ifdef CONFIG_ARM
+               set_irq_flags(irq, 0);
+#endif
+               set_irq_chip_and_handler(irq, NULL, NULL);
+               set_irq_chip_data(irq, NULL);
+       }
+}
+
+static int __devinit tc35892_gpio_probe(struct platform_device *pdev)
+{
+       struct tc35892 *tc35892 = dev_get_drvdata(pdev->dev.parent);
+       struct tc35892_gpio_platform_data *pdata;
+       struct tc35892_gpio *tc35892_gpio;
+       int ret;
+       int irq;
+
+       pdata = tc35892->pdata->gpio;
+       if (!pdata)
+               return -ENODEV;
+
+       irq = platform_get_irq(pdev, 0);
+       if (irq < 0)
+               return irq;
+
+       tc35892_gpio = kzalloc(sizeof(struct tc35892_gpio), GFP_KERNEL);
+       if (!tc35892_gpio)
+               return -ENOMEM;
+
+       mutex_init(&tc35892_gpio->irq_lock);
+
+       tc35892_gpio->dev = &pdev->dev;
+       tc35892_gpio->tc35892 = tc35892;
+
+       tc35892_gpio->chip = template_chip;
+       tc35892_gpio->chip.ngpio = tc35892->num_gpio;
+       tc35892_gpio->chip.dev = &pdev->dev;
+       tc35892_gpio->chip.base = pdata->gpio_base;
+
+       tc35892_gpio->irq_base = tc35892->irq_base + TC35892_INT_GPIO(0);
+
+       /* Bring the GPIO module out of reset */
+       ret = tc35892_set_bits(tc35892, TC35892_RSTCTRL,
+                              TC35892_RSTCTRL_GPIRST, 0);
+       if (ret < 0)
+               goto out_free;
+
+       ret = tc35892_gpio_irq_init(tc35892_gpio);
+       if (ret)
+               goto out_free;
+
+       ret = request_threaded_irq(irq, NULL, tc35892_gpio_irq, IRQF_ONESHOT,
+                                  "tc35892-gpio", tc35892_gpio);
+       if (ret) {
+               dev_err(&pdev->dev, "unable to get irq: %d\n", ret);
+               goto out_removeirq;
+       }
+
+       ret = gpiochip_add(&tc35892_gpio->chip);
+       if (ret) {
+               dev_err(&pdev->dev, "unable to add gpiochip: %d\n", ret);
+               goto out_freeirq;
+       }
+
+       platform_set_drvdata(pdev, tc35892_gpio);
+
+       return 0;
+
+out_freeirq:
+       free_irq(irq, tc35892_gpio);
+out_removeirq:
+       tc35892_gpio_irq_remove(tc35892_gpio);
+out_free:
+       kfree(tc35892_gpio);
+       return ret;
+}
+
+static int __devexit tc35892_gpio_remove(struct platform_device *pdev)
+{
+       struct tc35892_gpio *tc35892_gpio = platform_get_drvdata(pdev);
+       int irq = platform_get_irq(pdev, 0);
+       int ret;
+
+       ret = gpiochip_remove(&tc35892_gpio->chip);
+       if (ret < 0) {
+               dev_err(tc35892_gpio->dev,
+                       "unable to remove gpiochip: %d\n", ret);
+               return ret;
+       }
+
+       free_irq(irq, tc35892_gpio);
+       tc35892_gpio_irq_remove(tc35892_gpio);
+
+       platform_set_drvdata(pdev, NULL);
+       kfree(tc35892_gpio);
+
+       return 0;
+}
+
+static struct platform_driver tc35892_gpio_driver = {
+       .driver.name    = "tc35892-gpio",
+       .driver.owner   = THIS_MODULE,
+       .probe          = tc35892_gpio_probe,
+       .remove         = __devexit_p(tc35892_gpio_remove),
+};
+
+static int __init tc35892_gpio_init(void)
+{
+       return platform_driver_register(&tc35892_gpio_driver);
+}
+subsys_initcall(tc35892_gpio_init);
+
+static void __exit tc35892_gpio_exit(void)
+{
+       platform_driver_unregister(&tc35892_gpio_driver);
+}
+module_exit(tc35892_gpio_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("TC35892 GPIO driver");
+MODULE_AUTHOR("Hanumath Prasad, Rabin Vincent");
index cc5316d..b3ba44c 100644 (file)
@@ -900,9 +900,10 @@ static void radeon_cp_dispatch_clear(struct drm_device * dev,
                        flags |= RADEON_FRONT;
        }
        if (flags & (RADEON_DEPTH|RADEON_STENCIL)) {
-               if (!dev_priv->have_z_offset)
+               if (!dev_priv->have_z_offset) {
                        printk_once(KERN_ERR "radeon: illegal depth clear request. Buggy mesa detected - please update.\n");
-               flags &= ~(RADEON_DEPTH | RADEON_STENCIL);
+                       flags &= ~(RADEON_DEPTH | RADEON_STENCIL);
+               }
        }
 
        if (flags & (RADEON_FRONT | RADEON_BACK)) {
index 6a9ac75..e19cf8e 100644 (file)
@@ -447,13 +447,14 @@ config SENSORS_IT87
          will be called it87.
 
 config SENSORS_LM63
-       tristate "National Semiconductor LM63"
+       tristate "National Semiconductor LM63 and LM64"
        depends on I2C
        help
-         If you say yes here you get support for the National Semiconductor
-         LM63 remote diode digital temperature sensor with integrated fan
-         control.  Such chips are found on the Tyan S4882 (Thunder K8QS Pro)
-         motherboard, among others.
+         If you say yes here you get support for the National
+         Semiconductor LM63 and LM64 remote diode digital temperature
+         sensors with integrated fan control.  Such chips are found
+         on the Tyan S4882 (Thunder K8QS Pro) motherboard, among
+         others.
 
          This driver can also be built as a module.  If so, the module
          will be called lm63.
@@ -492,7 +493,8 @@ config SENSORS_LM75
                - NXP's LM75A
                - ST Microelectronics STDS75
                - TelCom (now Microchip) TCN75
-               - Texas Instruments TMP100, TMP101, TMP75, TMP175, TMP275
+               - Texas Instruments TMP100, TMP101, TMP105, TMP75, TMP175,
+                 TMP275
 
          This driver supports driver model based binding through board
          specific I2C device tables.
@@ -749,6 +751,16 @@ config SENSORS_DME1737
          This driver can also be built as a module.  If so, the module
          will be called dme1737.
 
+config SENSORS_EMC1403
+       tristate "SMSC EMC1403 thermal sensor"
+       depends on I2C
+       help
+         If you say yes here you get support for the SMSC EMC1403
+         temperature monitoring chip.
+
+         Threshold values can be configured using sysfs.
+         Data from the different diodes are accessible via sysfs.
+
 config SENSORS_SMSC47M1
        tristate "SMSC LPC47M10x and compatibles"
        help
@@ -831,6 +843,16 @@ config SENSORS_THMC50
          This driver can also be built as a module.  If so, the module
          will be called thmc50.
 
+config SENSORS_TMP102
+       tristate "Texas Instruments TMP102"
+       depends on I2C && EXPERIMENTAL
+       help
+         If you say yes here you get support for Texas Instruments TMP102
+         sensor chips.
+
+         This driver can also be built as a module.  If so, the module
+         will be called tmp102.
+
 config SENSORS_TMP401
        tristate "Texas Instruments TMP401 and compatibles"
        depends on I2C && EXPERIMENTAL
index 86920fb..2138ceb 100644 (file)
@@ -41,6 +41,7 @@ obj-$(CONFIG_SENSORS_ATXP1)   += atxp1.o
 obj-$(CONFIG_SENSORS_CORETEMP) += coretemp.o
 obj-$(CONFIG_SENSORS_DME1737)  += dme1737.o
 obj-$(CONFIG_SENSORS_DS1621)   += ds1621.o
+obj-$(CONFIG_SENSORS_EMC1403)  += emc1403.o
 obj-$(CONFIG_SENSORS_F71805F)  += f71805f.o
 obj-$(CONFIG_SENSORS_F71882FG) += f71882fg.o
 obj-$(CONFIG_SENSORS_F75375S)  += f75375s.o
@@ -90,6 +91,7 @@ obj-$(CONFIG_SENSORS_SMSC47M1)        += smsc47m1.o
 obj-$(CONFIG_SENSORS_SMSC47M192)+= smsc47m192.o
 obj-$(CONFIG_SENSORS_AMC6821)  += amc6821.o
 obj-$(CONFIG_SENSORS_THMC50)   += thmc50.o
+obj-$(CONFIG_SENSORS_TMP102)   += tmp102.o
 obj-$(CONFIG_SENSORS_TMP401)   += tmp401.o
 obj-$(CONFIG_SENSORS_TMP421)   += tmp421.o
 obj-$(CONFIG_SENSORS_VIA_CPUTEMP)+= via-cputemp.o
index 1644b92..15c1a96 100644 (file)
@@ -36,6 +36,7 @@
 #define ADM1031_REG_FAN_DIV(nr)                (0x20 + (nr))
 #define ADM1031_REG_PWM                        (0x22)
 #define ADM1031_REG_FAN_MIN(nr)                (0x10 + (nr))
+#define ADM1031_REG_FAN_FILTER         (0x23)
 
 #define ADM1031_REG_TEMP_OFFSET(nr)    (0x0d + (nr))
 #define ADM1031_REG_TEMP_MAX(nr)       (0x14 + 4 * (nr))
@@ -61,6 +62,9 @@
 #define ADM1031_CONF2_TACH2_ENABLE     0x08
 #define ADM1031_CONF2_TEMP_ENABLE(chan)        (0x10 << (chan))
 
+#define ADM1031_UPDATE_RATE_MASK       0x1c
+#define ADM1031_UPDATE_RATE_SHIFT      2
+
 /* Addresses to scan */
 static const unsigned short normal_i2c[] = { 0x2c, 0x2d, 0x2e, I2C_CLIENT_END };
 
@@ -75,6 +79,7 @@ struct adm1031_data {
        int chip_type;
        char valid;             /* !=0 if following fields are valid */
        unsigned long last_updated;     /* In jiffies */
+       unsigned int update_rate;       /* In milliseconds */
        /* The chan_select_table contains the possible configurations for
         * auto fan control.
         */
@@ -738,6 +743,57 @@ static SENSOR_DEVICE_ATTR(temp3_crit_alarm, S_IRUGO, show_alarm, NULL, 12);
 static SENSOR_DEVICE_ATTR(temp3_fault, S_IRUGO, show_alarm, NULL, 13);
 static SENSOR_DEVICE_ATTR(temp1_crit_alarm, S_IRUGO, show_alarm, NULL, 14);
 
+/* Update Rate */
+static const unsigned int update_rates[] = {
+       16000, 8000, 4000, 2000, 1000, 500, 250, 125,
+};
+
+static ssize_t show_update_rate(struct device *dev,
+                               struct device_attribute *attr, char *buf)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+       struct adm1031_data *data = i2c_get_clientdata(client);
+
+       return sprintf(buf, "%u\n", data->update_rate);
+}
+
+static ssize_t set_update_rate(struct device *dev,
+                              struct device_attribute *attr,
+                              const char *buf, size_t count)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+       struct adm1031_data *data = i2c_get_clientdata(client);
+       unsigned long val;
+       int i, err;
+       u8 reg;
+
+       err = strict_strtoul(buf, 10, &val);
+       if (err)
+               return err;
+
+       /* find the nearest update rate from the table */
+       for (i = 0; i < ARRAY_SIZE(update_rates) - 1; i++) {
+               if (val >= update_rates[i])
+                       break;
+       }
+       /* if not found, we point to the last entry (lowest update rate) */
+
+       /* set the new update rate while preserving other settings */
+       reg = adm1031_read_value(client, ADM1031_REG_FAN_FILTER);
+       reg &= ~ADM1031_UPDATE_RATE_MASK;
+       reg |= i << ADM1031_UPDATE_RATE_SHIFT;
+       adm1031_write_value(client, ADM1031_REG_FAN_FILTER, reg);
+
+       mutex_lock(&data->update_lock);
+       data->update_rate = update_rates[i];
+       mutex_unlock(&data->update_lock);
+
+       return count;
+}
+
+static DEVICE_ATTR(update_rate, S_IRUGO | S_IWUSR, show_update_rate,
+                  set_update_rate);
+
 static struct attribute *adm1031_attributes[] = {
        &sensor_dev_attr_fan1_input.dev_attr.attr,
        &sensor_dev_attr_fan1_div.dev_attr.attr,
@@ -774,6 +830,7 @@ static struct attribute *adm1031_attributes[] = {
 
        &sensor_dev_attr_auto_fan1_min_pwm.dev_attr.attr,
 
+       &dev_attr_update_rate.attr,
        &dev_attr_alarms.attr,
 
        NULL
@@ -900,6 +957,7 @@ static void adm1031_init_client(struct i2c_client *client)
 {
        unsigned int read_val;
        unsigned int mask;
+       int i;
        struct adm1031_data *data = i2c_get_clientdata(client);
 
        mask = (ADM1031_CONF2_PWM1_ENABLE | ADM1031_CONF2_TACH1_ENABLE);
@@ -919,18 +977,24 @@ static void adm1031_init_client(struct i2c_client *client)
                                ADM1031_CONF1_MONITOR_ENABLE);
        }
 
+       /* Read the chip's update rate */
+       mask = ADM1031_UPDATE_RATE_MASK;
+       read_val = adm1031_read_value(client, ADM1031_REG_FAN_FILTER);
+       i = (read_val & mask) >> ADM1031_UPDATE_RATE_SHIFT;
+       data->update_rate = update_rates[i];
 }
 
 static struct adm1031_data *adm1031_update_device(struct device *dev)
 {
        struct i2c_client *client = to_i2c_client(dev);
        struct adm1031_data *data = i2c_get_clientdata(client);
+       unsigned long next_update;
        int chan;
 
        mutex_lock(&data->update_lock);
 
-       if (time_after(jiffies, data->last_updated + HZ + HZ / 2)
-           || !data->valid) {
+       next_update = data->last_updated + msecs_to_jiffies(data->update_rate);
+       if (time_after(jiffies, next_update) || !data->valid) {
 
                dev_dbg(&client->dev, "Starting adm1031 update\n");
                for (chan = 0;
index f085c18..b6598aa 100644 (file)
@@ -148,6 +148,20 @@ static const char *temperature_sensors_sets[][41] = {
 /* Set 18: MacBook Pro 2,2 */
        { "TB0T", "TC0D", "TC0P", "TG0H", "TG0P", "TG0T", "TM0P", "TTF0",
          "Th0H", "Th1H", "Tm0P", "Ts0P", NULL },
+/* Set 19: Macbook Pro 5,3 */
+       { "TB0T", "TB1T", "TB2T", "TB3T", "TC0D", "TC0F", "TC0P", "TG0D",
+         "TG0F", "TG0H", "TG0P", "TG0T", "TN0D", "TN0P", "TTF0", "Th2H",
+         "Tm0P", "Ts0P", "Ts0S", NULL },
+/* Set 20: MacBook Pro 5,4 */
+       { "TB0T", "TB1T", "TB2T", "TB3T", "TC0D", "TC0F", "TC0P", "TN0D",
+         "TN0P", "TTF0", "Th2H", "Ts0P", "Ts0S", NULL },
+/* Set 21: MacBook Pro 6,2 */
+       { "TB0T", "TB1T", "TB2T", "TC0C", "TC0D", "TC0P", "TC1C", "TG0D",
+         "TG0P", "TG0T", "TMCD", "TP0P", "TPCD", "Th1H", "Th2H", "Tm0P",
+         "Ts0P", "Ts0S", NULL },
+/* Set 22: MacBook Pro 7,1 */
+       { "TB0T", "TB1T", "TB2T", "TC0D", "TC0P", "TN0D", "TN0P", "TN0S",
+         "TN1D", "TN1F", "TN1G", "TN1S", "Th1H", "Ts0P", "Ts0S", NULL },
 };
 
 /* List of keys used to read/write fan speeds */
@@ -646,6 +660,17 @@ out:
                return snprintf(sysfsbuf, PAGE_SIZE, "(%d,%d)\n", left, right);
 }
 
+/* Displays sensor key as label */
+static ssize_t applesmc_show_sensor_label(struct device *dev,
+                       struct device_attribute *devattr, char *sysfsbuf)
+{
+       struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+       const char *key =
+               temperature_sensors_sets[applesmc_temperature_set][attr->index];
+
+       return snprintf(sysfsbuf, PAGE_SIZE, "%s\n", key);
+}
+
 /* Displays degree Celsius * 1000 */
 static ssize_t applesmc_show_temperature(struct device *dev,
                        struct device_attribute *devattr, char *sysfsbuf)
@@ -1113,6 +1138,86 @@ static const struct attribute_group fan_attribute_groups[] = {
 /*
  * Temperature sensors sysfs entries.
  */
+static SENSOR_DEVICE_ATTR(temp1_label, S_IRUGO,
+                                       applesmc_show_sensor_label, NULL, 0);
+static SENSOR_DEVICE_ATTR(temp2_label, S_IRUGO,
+                                       applesmc_show_sensor_label, NULL, 1);
+static SENSOR_DEVICE_ATTR(temp3_label, S_IRUGO,
+                                       applesmc_show_sensor_label, NULL, 2);
+static SENSOR_DEVICE_ATTR(temp4_label, S_IRUGO,
+                                       applesmc_show_sensor_label, NULL, 3);
+static SENSOR_DEVICE_ATTR(temp5_label, S_IRUGO,
+                                       applesmc_show_sensor_label, NULL, 4);
+static SENSOR_DEVICE_ATTR(temp6_label, S_IRUGO,
+                                       applesmc_show_sensor_label, NULL, 5);
+static SENSOR_DEVICE_ATTR(temp7_label, S_IRUGO,
+                                       applesmc_show_sensor_label, NULL, 6);
+static SENSOR_DEVICE_ATTR(temp8_label, S_IRUGO,
+                                       applesmc_show_sensor_label, NULL, 7);
+static SENSOR_DEVICE_ATTR(temp9_label, S_IRUGO,
+                                       applesmc_show_sensor_label, NULL, 8);
+static SENSOR_DEVICE_ATTR(temp10_label, S_IRUGO,
+                                       applesmc_show_sensor_label, NULL, 9);
+static SENSOR_DEVICE_ATTR(temp11_label, S_IRUGO,
+                                       applesmc_show_sensor_label, NULL, 10);
+static SENSOR_DEVICE_ATTR(temp12_label, S_IRUGO,
+                                       applesmc_show_sensor_label, NULL, 11);
+static SENSOR_DEVICE_ATTR(temp13_label, S_IRUGO,
+                                       applesmc_show_sensor_label, NULL, 12);
+static SENSOR_DEVICE_ATTR(temp14_label, S_IRUGO,
+                                       applesmc_show_sensor_label, NULL, 13);
+static SENSOR_DEVICE_ATTR(temp15_label, S_IRUGO,
+                                       applesmc_show_sensor_label, NULL, 14);
+static SENSOR_DEVICE_ATTR(temp16_label, S_IRUGO,
+                                       applesmc_show_sensor_label, NULL, 15);
+static SENSOR_DEVICE_ATTR(temp17_label, S_IRUGO,
+                                       applesmc_show_sensor_label, NULL, 16);
+static SENSOR_DEVICE_ATTR(temp18_label, S_IRUGO,
+                                       applesmc_show_sensor_label, NULL, 17);
+static SENSOR_DEVICE_ATTR(temp19_label, S_IRUGO,
+                                       applesmc_show_sensor_label, NULL, 18);
+static SENSOR_DEVICE_ATTR(temp20_label, S_IRUGO,
+                                       applesmc_show_sensor_label, NULL, 19);
+static SENSOR_DEVICE_ATTR(temp21_label, S_IRUGO,
+                                       applesmc_show_sensor_label, NULL, 20);
+static SENSOR_DEVICE_ATTR(temp22_label, S_IRUGO,
+                                       applesmc_show_sensor_label, NULL, 21);
+static SENSOR_DEVICE_ATTR(temp23_label, S_IRUGO,
+                                       applesmc_show_sensor_label, NULL, 22);
+static SENSOR_DEVICE_ATTR(temp24_label, S_IRUGO,
+                                       applesmc_show_sensor_label, NULL, 23);
+static SENSOR_DEVICE_ATTR(temp25_label, S_IRUGO,
+                                       applesmc_show_sensor_label, NULL, 24);
+static SENSOR_DEVICE_ATTR(temp26_label, S_IRUGO,
+                                       applesmc_show_sensor_label, NULL, 25);
+static SENSOR_DEVICE_ATTR(temp27_label, S_IRUGO,
+                                       applesmc_show_sensor_label, NULL, 26);
+static SENSOR_DEVICE_ATTR(temp28_label, S_IRUGO,
+                                       applesmc_show_sensor_label, NULL, 27);
+static SENSOR_DEVICE_ATTR(temp29_label, S_IRUGO,
+                                       applesmc_show_sensor_label, NULL, 28);
+static SENSOR_DEVICE_ATTR(temp30_label, S_IRUGO,
+                                       applesmc_show_sensor_label, NULL, 29);
+static SENSOR_DEVICE_ATTR(temp31_label, S_IRUGO,
+                                       applesmc_show_sensor_label, NULL, 30);
+static SENSOR_DEVICE_ATTR(temp32_label, S_IRUGO,
+                                       applesmc_show_sensor_label, NULL, 31);
+static SENSOR_DEVICE_ATTR(temp33_label, S_IRUGO,
+                                       applesmc_show_sensor_label, NULL, 32);
+static SENSOR_DEVICE_ATTR(temp34_label, S_IRUGO,
+                                       applesmc_show_sensor_label, NULL, 33);
+static SENSOR_DEVICE_ATTR(temp35_label, S_IRUGO,
+                                       applesmc_show_sensor_label, NULL, 34);
+static SENSOR_DEVICE_ATTR(temp36_label, S_IRUGO,
+                                       applesmc_show_sensor_label, NULL, 35);
+static SENSOR_DEVICE_ATTR(temp37_label, S_IRUGO,
+                                       applesmc_show_sensor_label, NULL, 36);
+static SENSOR_DEVICE_ATTR(temp38_label, S_IRUGO,
+                                       applesmc_show_sensor_label, NULL, 37);
+static SENSOR_DEVICE_ATTR(temp39_label, S_IRUGO,
+                                       applesmc_show_sensor_label, NULL, 38);
+static SENSOR_DEVICE_ATTR(temp40_label, S_IRUGO,
+                                       applesmc_show_sensor_label, NULL, 39);
 static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO,
                                        applesmc_show_temperature, NULL, 0);
 static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO,
@@ -1194,6 +1299,50 @@ static SENSOR_DEVICE_ATTR(temp39_input, S_IRUGO,
 static SENSOR_DEVICE_ATTR(temp40_input, S_IRUGO,
                                        applesmc_show_temperature, NULL, 39);
 
+static struct attribute *label_attributes[] = {
+       &sensor_dev_attr_temp1_label.dev_attr.attr,
+       &sensor_dev_attr_temp2_label.dev_attr.attr,
+       &sensor_dev_attr_temp3_label.dev_attr.attr,
+       &sensor_dev_attr_temp4_label.dev_attr.attr,
+       &sensor_dev_attr_temp5_label.dev_attr.attr,
+       &sensor_dev_attr_temp6_label.dev_attr.attr,
+       &sensor_dev_attr_temp7_label.dev_attr.attr,
+       &sensor_dev_attr_temp8_label.dev_attr.attr,
+       &sensor_dev_attr_temp9_label.dev_attr.attr,
+       &sensor_dev_attr_temp10_label.dev_attr.attr,
+       &sensor_dev_attr_temp11_label.dev_attr.attr,
+       &sensor_dev_attr_temp12_label.dev_attr.attr,
+       &sensor_dev_attr_temp13_label.dev_attr.attr,
+       &sensor_dev_attr_temp14_label.dev_attr.attr,
+       &sensor_dev_attr_temp15_label.dev_attr.attr,
+       &sensor_dev_attr_temp16_label.dev_attr.attr,
+       &sensor_dev_attr_temp17_label.dev_attr.attr,
+       &sensor_dev_attr_temp18_label.dev_attr.attr,
+       &sensor_dev_attr_temp19_label.dev_attr.attr,
+       &sensor_dev_attr_temp20_label.dev_attr.attr,
+       &sensor_dev_attr_temp21_label.dev_attr.attr,
+       &sensor_dev_attr_temp22_label.dev_attr.attr,
+       &sensor_dev_attr_temp23_label.dev_attr.attr,
+       &sensor_dev_attr_temp24_label.dev_attr.attr,
+       &sensor_dev_attr_temp25_label.dev_attr.attr,
+       &sensor_dev_attr_temp26_label.dev_attr.attr,
+       &sensor_dev_attr_temp27_label.dev_attr.attr,
+       &sensor_dev_attr_temp28_label.dev_attr.attr,
+       &sensor_dev_attr_temp29_label.dev_attr.attr,
+       &sensor_dev_attr_temp30_label.dev_attr.attr,
+       &sensor_dev_attr_temp31_label.dev_attr.attr,
+       &sensor_dev_attr_temp32_label.dev_attr.attr,
+       &sensor_dev_attr_temp33_label.dev_attr.attr,
+       &sensor_dev_attr_temp34_label.dev_attr.attr,
+       &sensor_dev_attr_temp35_label.dev_attr.attr,
+       &sensor_dev_attr_temp36_label.dev_attr.attr,
+       &sensor_dev_attr_temp37_label.dev_attr.attr,
+       &sensor_dev_attr_temp38_label.dev_attr.attr,
+       &sensor_dev_attr_temp39_label.dev_attr.attr,
+       &sensor_dev_attr_temp40_label.dev_attr.attr,
+       NULL
+};
+
 static struct attribute *temperature_attributes[] = {
        &sensor_dev_attr_temp1_input.dev_attr.attr,
        &sensor_dev_attr_temp2_input.dev_attr.attr,
@@ -1241,6 +1390,10 @@ static struct attribute *temperature_attributes[] = {
 static const struct attribute_group temperature_attributes_group =
        { .attrs = temperature_attributes };
 
+static const struct attribute_group label_attributes_group = {
+       .attrs = label_attributes
+};
+
 /* Module stuff */
 
 /*
@@ -1363,6 +1516,14 @@ static __initdata struct dmi_match_data applesmc_dmi_data[] = {
        { .accelerometer = 0, .light = 0, .temperature_set = 17 },
 /* MacBook Pro 2,2: accelerometer, backlight and temperature set 18 */
        { .accelerometer = 1, .light = 1, .temperature_set = 18 },
+/* MacBook Pro 5,3: accelerometer, backlight and temperature set 19 */
+       { .accelerometer = 1, .light = 1, .temperature_set = 19 },
+/* MacBook Pro 5,4: accelerometer, backlight and temperature set 20 */
+       { .accelerometer = 1, .light = 1, .temperature_set = 20 },
+/* MacBook Pro 6,2: accelerometer, backlight and temperature set 21 */
+       { .accelerometer = 1, .light = 1, .temperature_set = 21 },
+/* MacBook Pro 7,1: accelerometer, backlight and temperature set 22 */
+       { .accelerometer = 1, .light = 1, .temperature_set = 22 },
 };
 
 /* Note that DMI_MATCH(...,"MacBook") will match "MacBookPro1,1".
@@ -1376,6 +1537,22 @@ static __initdata struct dmi_system_id applesmc_whitelist[] = {
          DMI_MATCH(DMI_BOARD_VENDOR, "Apple"),
          DMI_MATCH(DMI_PRODUCT_NAME, "MacBookAir") },
                &applesmc_dmi_data[7]},
+       { applesmc_dmi_match, "Apple MacBook Pro 7", {
+         DMI_MATCH(DMI_BOARD_VENDOR, "Apple"),
+         DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro7") },
+               &applesmc_dmi_data[22]},
+       { applesmc_dmi_match, "Apple MacBook Pro 5,4", {
+         DMI_MATCH(DMI_BOARD_VENDOR, "Apple"),
+         DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro5,4") },
+               &applesmc_dmi_data[20]},
+       { applesmc_dmi_match, "Apple MacBook Pro 5,3", {
+         DMI_MATCH(DMI_BOARD_VENDOR, "Apple"),
+         DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro5,3") },
+               &applesmc_dmi_data[19]},
+       { applesmc_dmi_match, "Apple MacBook Pro 6", {
+         DMI_MATCH(DMI_BOARD_VENDOR, "Apple"),
+         DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro6") },
+               &applesmc_dmi_data[21]},
        { applesmc_dmi_match, "Apple MacBook Pro 5", {
          DMI_MATCH(DMI_BOARD_VENDOR, "Apple"),
          DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro5") },
@@ -1518,7 +1695,8 @@ static int __init applesmc_init(void)
        for (i = 0;
             temperature_sensors_sets[applesmc_temperature_set][i] != NULL;
             i++) {
-               if (temperature_attributes[i] == NULL) {
+               if (temperature_attributes[i] == NULL ||
+                   label_attributes[i] == NULL) {
                        printk(KERN_ERR "applesmc: More temperature sensors "
                                "in temperature_sensors_sets (at least %i)"
                                "than available sysfs files in "
@@ -1530,6 +1708,10 @@ static int __init applesmc_init(void)
                                                temperature_attributes[i]);
                if (ret)
                        goto out_temperature;
+               ret = sysfs_create_file(&pdev->dev.kobj,
+                                               label_attributes[i]);
+               if (ret)
+                       goto out_temperature;
        }
 
        if (applesmc_accelerometer) {
@@ -1580,6 +1762,7 @@ out_accelerometer:
        if (applesmc_accelerometer)
                applesmc_release_accelerometer();
 out_temperature:
+       sysfs_remove_group(&pdev->dev.kobj, &label_attributes_group);
        sysfs_remove_group(&pdev->dev.kobj, &temperature_attributes_group);
 out_fans:
        while (fans_handled)
@@ -1609,6 +1792,7 @@ static void __exit applesmc_exit(void)
        }
        if (applesmc_accelerometer)
                applesmc_release_accelerometer();
+       sysfs_remove_group(&pdev->dev.kobj, &label_attributes_group);
        sysfs_remove_group(&pdev->dev.kobj, &temperature_attributes_group);
        while (fans_handled)
                sysfs_remove_group(&pdev->dev.kobj,
index 16c4202..653db1b 100644 (file)
@@ -1411,6 +1411,13 @@ static int __init atk0110_init(void)
 {
        int ret;
 
+       /* Make sure it's safe to access the device through ACPI */
+       if (!acpi_resources_are_enforced()) {
+               pr_err("atk: Resources not safely usable due to "
+                      "acpi_enforce_resources kernel parameter\n");
+               return -EBUSY;
+       }
+
        ret = acpi_bus_register_driver(&atk_driver);
        if (ret)
                pr_info("atk: acpi_bus_register_driver failed: %d\n", ret);
index 823dd28..980c17d 100644 (file)
@@ -1,12 +1,14 @@
 /*
- * dme1737.c - Driver for the SMSC DME1737, Asus A8000, SMSC SCH311x and
- *             SCH5027 Super-I/O chips integrated hardware monitoring features.
- * Copyright (c) 2007, 2008 Juerg Haefliger <juergh@gmail.com>
+ * dme1737.c - Driver for the SMSC DME1737, Asus A8000, SMSC SCH311x, SCH5027,
+ *             and SCH5127 Super-I/O chips integrated hardware monitoring
+ *             features.
+ * Copyright (c) 2007, 2008, 2009, 2010 Juerg Haefliger <juergh@gmail.com>
  *
  * This driver is an I2C/ISA hybrid, meaning that it uses the I2C bus to access
  * the chip registers if a DME1737, A8000, or SCH5027 is found and the ISA bus
- * if a SCH311x chip is found. Both types of chips have very similar hardware
- * monitoring capabilities but differ in the way they can be accessed.
+ * if a SCH311x or SCH5127 chip is found. Both types of chips have very
+ * similar hardware monitoring capabilities but differ in the way they can be
+ * accessed.
  *
  * 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
@@ -57,7 +59,7 @@ MODULE_PARM_DESC(probe_all_addr, "Include probing of non-standard LPC "
 /* Addresses to scan */
 static const unsigned short normal_i2c[] = {0x2c, 0x2d, 0x2e, I2C_CLIENT_END};
 
-enum chips { dme1737, sch5027, sch311x };
+enum chips { dme1737, sch5027, sch311x, sch5127 };
 
 /* ---------------------------------------------------------------------
  * Registers
@@ -164,10 +166,29 @@ static const u8 DME1737_BIT_ALARM_FAN[] = {10, 11, 12, 13, 22, 23};
 #define DME1737_VERSTEP_MASK   0xf8
 #define SCH311X_DEVICE         0x8c
 #define SCH5027_VERSTEP                0x69
+#define SCH5127_DEVICE         0x8e
+
+/* Device ID values (global configuration register index 0x20) */
+#define DME1737_ID_1   0x77
+#define DME1737_ID_2   0x78
+#define SCH3112_ID     0x7c
+#define SCH3114_ID     0x7d
+#define SCH3116_ID     0x7f
+#define SCH5027_ID     0x89
+#define SCH5127_ID     0x86
 
 /* Length of ISA address segment */
 #define DME1737_EXTENT 2
 
+/* chip-dependent features */
+#define HAS_TEMP_OFFSET                (1 << 0)                /* bit 0 */
+#define HAS_VID                        (1 << 1)                /* bit 1 */
+#define HAS_ZONE3              (1 << 2)                /* bit 2 */
+#define HAS_ZONE_HYST          (1 << 3)                /* bit 3 */
+#define HAS_PWM_MIN            (1 << 4)                /* bit 4 */
+#define HAS_FAN(ix)            (1 << ((ix) + 5))       /* bits 5-10 */
+#define HAS_PWM(ix)            (1 << ((ix) + 11))      /* bits 11-16 */
+
 /* ---------------------------------------------------------------------
  * Data structures and manipulation thereof
  * --------------------------------------------------------------------- */
@@ -187,8 +208,7 @@ struct dme1737_data {
 
        u8 vid;
        u8 pwm_rr_en;
-       u8 has_pwm;
-       u8 has_fan;
+       u32 has_features;
 
        /* Register values */
        u16 in[7];
@@ -224,8 +244,11 @@ static const int IN_NOMINAL_SCH311x[] = {2500, 1500, 3300, 5000, 12000, 3300,
                                         3300};
 static const int IN_NOMINAL_SCH5027[] = {5000, 2250, 3300, 1125, 1125, 3300,
                                         3300};
+static const int IN_NOMINAL_SCH5127[] = {2500, 2250, 3300, 1125, 1125, 3300,
+                                        3300};
 #define IN_NOMINAL(type)       ((type) == sch311x ? IN_NOMINAL_SCH311x : \
                                 (type) == sch5027 ? IN_NOMINAL_SCH5027 : \
+                                (type) == sch5127 ? IN_NOMINAL_SCH5127 : \
                                 IN_NOMINAL_DME1737)
 
 /* Voltage input
@@ -568,7 +591,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 == dme1737) {
+               if (data->has_features & HAS_VID) {
                        data->vid = dme1737_read(data, DME1737_REG_VID) &
                                0x3f;
                }
@@ -599,7 +622,7 @@ static struct dme1737_data *dme1737_update_device(struct device *dev)
                                        DME1737_REG_TEMP_MIN(ix));
                        data->temp_max[ix] = dme1737_read(data,
                                        DME1737_REG_TEMP_MAX(ix));
-                       if (data->type != sch5027) {
+                       if (data->has_features & HAS_TEMP_OFFSET) {
                                data->temp_offset[ix] = dme1737_read(data,
                                                DME1737_REG_TEMP_OFFSET(ix));
                        }
@@ -626,7 +649,7 @@ static struct dme1737_data *dme1737_update_device(struct device *dev)
                for (ix = 0; ix < ARRAY_SIZE(data->fan); ix++) {
                        /* Skip reading registers if optional fans are not
                         * present */
-                       if (!(data->has_fan & (1 << ix))) {
+                       if (!(data->has_features & HAS_FAN(ix))) {
                                continue;
                        }
                        data->fan[ix] = dme1737_read(data,
@@ -650,7 +673,7 @@ static struct dme1737_data *dme1737_update_device(struct device *dev)
                for (ix = 0; ix < ARRAY_SIZE(data->pwm); ix++) {
                        /* Skip reading registers if optional PWMs are not
                         * present */
-                       if (!(data->has_pwm & (1 << ix))) {
+                       if (!(data->has_features & HAS_PWM(ix))) {
                                continue;
                        }
                        data->pwm[ix] = dme1737_read(data,
@@ -672,12 +695,24 @@ static struct dme1737_data *dme1737_update_device(struct device *dev)
 
                /* Thermal zone registers */
                for (ix = 0; ix < ARRAY_SIZE(data->zone_low); ix++) {
-                       data->zone_low[ix] = dme1737_read(data,
-                                       DME1737_REG_ZONE_LOW(ix));
-                       data->zone_abs[ix] = dme1737_read(data,
-                                       DME1737_REG_ZONE_ABS(ix));
+                       /* Skip reading registers if zone3 is not present */
+                       if ((ix == 2) && !(data->has_features & HAS_ZONE3)) {
+                               continue;
+                       }
+                       /* sch5127 zone2 registers are special */
+                       if ((ix == 1) && (data->type == sch5127)) {
+                               data->zone_low[1] = dme1737_read(data,
+                                               DME1737_REG_ZONE_LOW(2));
+                               data->zone_abs[1] = dme1737_read(data,
+                                               DME1737_REG_ZONE_ABS(2));
+                       } else {
+                               data->zone_low[ix] = dme1737_read(data,
+                                               DME1737_REG_ZONE_LOW(ix));
+                               data->zone_abs[ix] = dme1737_read(data,
+                                               DME1737_REG_ZONE_ABS(ix));
+                       }
                }
-               if (data->type != sch5027) {
+               if (data->has_features & HAS_ZONE_HYST) {
                        for (ix = 0; ix < ARRAY_SIZE(data->zone_hyst); ix++) {
                                data->zone_hyst[ix] = dme1737_read(data,
                                                DME1737_REG_ZONE_HYST(ix));
@@ -1594,10 +1629,6 @@ static struct attribute *dme1737_attr[] ={
        &sensor_dev_attr_zone2_auto_point2_temp.dev_attr.attr,
        &sensor_dev_attr_zone2_auto_point3_temp.dev_attr.attr,
        &sensor_dev_attr_zone2_auto_channels_temp.dev_attr.attr,
-       &sensor_dev_attr_zone3_auto_point1_temp.dev_attr.attr,
-       &sensor_dev_attr_zone3_auto_point2_temp.dev_attr.attr,
-       &sensor_dev_attr_zone3_auto_point3_temp.dev_attr.attr,
-       &sensor_dev_attr_zone3_auto_channels_temp.dev_attr.attr,
        NULL
 };
 
@@ -1605,27 +1636,23 @@ static const struct attribute_group dme1737_group = {
        .attrs = dme1737_attr,
 };
 
-/* The following struct holds misc attributes, which are not available in all
- * chips. Their creation depends on the chip type which is determined during
- * module load. */
-static struct attribute *dme1737_misc_attr[] = {
-       /* Temperatures */
+/* The following struct holds temp offset attributes, which are not available
+ * in all chips. The following chips support them:
+ * DME1737, SCH311x */
+static struct attribute *dme1737_temp_offset_attr[] = {
        &sensor_dev_attr_temp1_offset.dev_attr.attr,
        &sensor_dev_attr_temp2_offset.dev_attr.attr,
        &sensor_dev_attr_temp3_offset.dev_attr.attr,
-       /* Zones */
-       &sensor_dev_attr_zone1_auto_point1_temp_hyst.dev_attr.attr,
-       &sensor_dev_attr_zone2_auto_point1_temp_hyst.dev_attr.attr,
-       &sensor_dev_attr_zone3_auto_point1_temp_hyst.dev_attr.attr,
        NULL
 };
 
-static const struct attribute_group dme1737_misc_group = {
-       .attrs = dme1737_misc_attr,
+static const struct attribute_group dme1737_temp_offset_group = {
+       .attrs = dme1737_temp_offset_attr,
 };
 
-/* The following struct holds VID-related attributes. Their creation
-   depends on the chip type which is determined during module load. */
+/* The following struct holds VID related attributes, which are not available
+ * in all chips. The following chips support them:
+ * DME1737 */
 static struct attribute *dme1737_vid_attr[] = {
        &dev_attr_vrm.attr,
        &dev_attr_cpu0_vid.attr,
@@ -1636,6 +1663,36 @@ static const struct attribute_group dme1737_vid_group = {
        .attrs = dme1737_vid_attr,
 };
 
+/* The following struct holds temp zone 3 related attributes, which are not
+ * available in all chips. The following chips support them:
+ * DME1737, SCH311x, SCH5027 */
+static struct attribute *dme1737_zone3_attr[] = {
+       &sensor_dev_attr_zone3_auto_point1_temp.dev_attr.attr,
+       &sensor_dev_attr_zone3_auto_point2_temp.dev_attr.attr,
+       &sensor_dev_attr_zone3_auto_point3_temp.dev_attr.attr,
+       &sensor_dev_attr_zone3_auto_channels_temp.dev_attr.attr,
+       NULL
+};
+
+static const struct attribute_group dme1737_zone3_group = {
+       .attrs = dme1737_zone3_attr,
+};
+
+
+/* The following struct holds temp zone hysteresis  related attributes, which
+ * are not available in all chips. The following chips support them:
+ * DME1737, SCH311x */
+static struct attribute *dme1737_zone_hyst_attr[] = {
+       &sensor_dev_attr_zone1_auto_point1_temp_hyst.dev_attr.attr,
+       &sensor_dev_attr_zone2_auto_point1_temp_hyst.dev_attr.attr,
+       &sensor_dev_attr_zone3_auto_point1_temp_hyst.dev_attr.attr,
+       NULL
+};
+
+static const struct attribute_group dme1737_zone_hyst_group = {
+       .attrs = dme1737_zone_hyst_attr,
+};
+
 /* The following structs hold the PWM attributes, some of which are optional.
  * Their creation depends on the chip configuration which is determined during
  * module load. */
@@ -1691,10 +1748,10 @@ static const struct attribute_group dme1737_pwm_group[] = {
        { .attrs = dme1737_pwm6_attr },
 };
 
-/* The following struct holds misc PWM attributes, which are not available in
- * all chips. Their creation depends on the chip type which is determined
+/* The following struct holds auto PWM min attributes, which are not available
+ * in all chips. Their creation depends on the chip type which is determined
  * during module load. */
-static struct attribute *dme1737_pwm_misc_attr[] = {
+static struct attribute *dme1737_auto_pwm_min_attr[] = {
        &sensor_dev_attr_pwm1_auto_pwm_min.dev_attr.attr,
        &sensor_dev_attr_pwm2_auto_pwm_min.dev_attr.attr,
        &sensor_dev_attr_pwm3_auto_pwm_min.dev_attr.attr,
@@ -1764,14 +1821,25 @@ static struct attribute *dme1737_zone_chmod_attr[] = {
        &sensor_dev_attr_zone2_auto_point1_temp.dev_attr.attr,
        &sensor_dev_attr_zone2_auto_point2_temp.dev_attr.attr,
        &sensor_dev_attr_zone2_auto_point3_temp.dev_attr.attr,
+       NULL
+};
+
+static const struct attribute_group dme1737_zone_chmod_group = {
+       .attrs = dme1737_zone_chmod_attr,
+};
+
+
+/* The permissions of the following zone 3 attributes are changed to read-
+ * writeable if the chip is *not* locked. Otherwise they stay read-only. */
+static struct attribute *dme1737_zone3_chmod_attr[] = {
        &sensor_dev_attr_zone3_auto_point1_temp.dev_attr.attr,
        &sensor_dev_attr_zone3_auto_point2_temp.dev_attr.attr,
        &sensor_dev_attr_zone3_auto_point3_temp.dev_attr.attr,
        NULL
 };
 
-static const struct attribute_group dme1737_zone_chmod_group = {
-       .attrs = dme1737_zone_chmod_attr,
+static const struct attribute_group dme1737_zone3_chmod_group = {
+       .attrs = dme1737_zone3_chmod_attr,
 };
 
 /* The permissions of the following PWM attributes are changed to read-
@@ -1887,30 +1955,35 @@ static void dme1737_remove_files(struct device *dev)
        int ix;
 
        for (ix = 0; ix < ARRAY_SIZE(dme1737_fan_group); ix++) {
-               if (data->has_fan & (1 << ix)) {
+               if (data->has_features & HAS_FAN(ix)) {
                        sysfs_remove_group(&dev->kobj,
                                           &dme1737_fan_group[ix]);
                }
        }
 
        for (ix = 0; ix < ARRAY_SIZE(dme1737_pwm_group); ix++) {
-               if (data->has_pwm & (1 << ix)) {
+               if (data->has_features & HAS_PWM(ix)) {
                        sysfs_remove_group(&dev->kobj,
                                           &dme1737_pwm_group[ix]);
-                       if (data->type != sch5027 && ix < 3) {
+                       if ((data->has_features & HAS_PWM_MIN) && ix < 3) {
                                sysfs_remove_file(&dev->kobj,
-                                                 dme1737_pwm_misc_attr[ix]);
+                                               dme1737_auto_pwm_min_attr[ix]);
                        }
                }
        }
 
-       if (data->type != sch5027) {
-               sysfs_remove_group(&dev->kobj, &dme1737_misc_group);
+       if (data->has_features & HAS_TEMP_OFFSET) {
+               sysfs_remove_group(&dev->kobj, &dme1737_temp_offset_group);
        }
-       if (data->type == dme1737) {
+       if (data->has_features & HAS_VID) {
                sysfs_remove_group(&dev->kobj, &dme1737_vid_group);
        }
-
+       if (data->has_features & HAS_ZONE3) {
+               sysfs_remove_group(&dev->kobj, &dme1737_zone3_group);
+       }
+       if (data->has_features & HAS_ZONE_HYST) {
+               sysfs_remove_group(&dev->kobj, &dme1737_zone_hyst_group);
+       }
        sysfs_remove_group(&dev->kobj, &dme1737_group);
 
        if (!data->client) {
@@ -1934,23 +2007,31 @@ static int dme1737_create_files(struct device *dev)
                goto exit_remove;
        }
 
-       /* Create misc sysfs attributes */
-       if ((data->type != sch5027) &&
+       /* Create chip-dependent sysfs attributes */
+       if ((data->has_features & HAS_TEMP_OFFSET) &&
            (err = sysfs_create_group(&dev->kobj,
-                                     &dme1737_misc_group))) {
+                                     &dme1737_temp_offset_group))) {
                goto exit_remove;
        }
-
-       /* Create VID-related sysfs attributes */
-       if ((data->type == dme1737) &&
+       if ((data->has_features & HAS_VID) &&
            (err = sysfs_create_group(&dev->kobj,
                                      &dme1737_vid_group))) {
                goto exit_remove;
        }
+       if ((data->has_features & HAS_ZONE3) &&
+           (err = sysfs_create_group(&dev->kobj,
+                                     &dme1737_zone3_group))) {
+               goto exit_remove;
+       }
+       if ((data->has_features & HAS_ZONE_HYST) &&
+           (err = sysfs_create_group(&dev->kobj,
+                                     &dme1737_zone_hyst_group))) {
+               goto exit_remove;
+       }
 
        /* Create fan sysfs attributes */
        for (ix = 0; ix < ARRAY_SIZE(dme1737_fan_group); ix++) {
-               if (data->has_fan & (1 << ix)) {
+               if (data->has_features & HAS_FAN(ix)) {
                        if ((err = sysfs_create_group(&dev->kobj,
                                                &dme1737_fan_group[ix]))) {
                                goto exit_remove;
@@ -1960,14 +2041,14 @@ static int dme1737_create_files(struct device *dev)
 
        /* Create PWM sysfs attributes */
        for (ix = 0; ix < ARRAY_SIZE(dme1737_pwm_group); ix++) {
-               if (data->has_pwm & (1 << ix)) {
+               if (data->has_features & HAS_PWM(ix)) {
                        if ((err = sysfs_create_group(&dev->kobj,
                                                &dme1737_pwm_group[ix]))) {
                                goto exit_remove;
                        }
-                       if (data->type != sch5027 && ix < 3 &&
+                       if ((data->has_features & HAS_PWM_MIN) && ix < 3 &&
                            (err = sysfs_create_file(&dev->kobj,
-                                               dme1737_pwm_misc_attr[ix]))) {
+                                       dme1737_auto_pwm_min_attr[ix]))) {
                                goto exit_remove;
                        }
                }
@@ -1983,21 +2064,30 @@ static int dme1737_create_files(struct device *dev)
                dme1737_chmod_group(dev, &dme1737_zone_chmod_group,
                                    S_IRUGO | S_IWUSR);
 
-               /* Change permissions of misc sysfs attributes */
-               if (data->type != sch5027) {
-                       dme1737_chmod_group(dev, &dme1737_misc_group,
+               /* Change permissions of chip-dependent sysfs attributes */
+               if (data->has_features & HAS_TEMP_OFFSET) {
+                       dme1737_chmod_group(dev, &dme1737_temp_offset_group,
+                                           S_IRUGO | S_IWUSR);
+               }
+               if (data->has_features & HAS_ZONE3) {
+                       dme1737_chmod_group(dev, &dme1737_zone3_chmod_group,
+                                           S_IRUGO | S_IWUSR);
+               }
+               if (data->has_features & HAS_ZONE_HYST) {
+                       dme1737_chmod_group(dev, &dme1737_zone_hyst_group,
                                            S_IRUGO | S_IWUSR);
                }
 
                /* Change permissions of PWM sysfs attributes */
                for (ix = 0; ix < ARRAY_SIZE(dme1737_pwm_chmod_group); ix++) {
-                       if (data->has_pwm & (1 << ix)) {
+                       if (data->has_features & HAS_PWM(ix)) {
                                dme1737_chmod_group(dev,
                                                &dme1737_pwm_chmod_group[ix],
                                                S_IRUGO | S_IWUSR);
-                               if (data->type != sch5027 && ix < 3) {
+                               if ((data->has_features & HAS_PWM_MIN) &&
+                                   ix < 3) {
                                        dme1737_chmod_file(dev,
-                                               dme1737_pwm_misc_attr[ix],
+                                               dme1737_auto_pwm_min_attr[ix],
                                                S_IRUGO | S_IWUSR);
                                }
                        }
@@ -2005,7 +2095,7 @@ static int dme1737_create_files(struct device *dev)
 
                /* Change permissions of pwm[1-3] if in manual mode */
                for (ix = 0; ix < 3; ix++) {
-                       if ((data->has_pwm & (1 << ix)) &&
+                       if ((data->has_features & HAS_PWM(ix)) &&
                            (PWM_EN_FROM_REG(data->pwm_config[ix]) == 1)) {
                                dme1737_chmod_file(dev,
                                                dme1737_pwm_chmod_attr[ix],
@@ -2052,20 +2142,20 @@ static int dme1737_init_device(struct device *dev)
                return -EFAULT;
        }
 
-       /* Determine which optional fan and pwm features are enabled/present */
+       /* Determine which optional fan and pwm features are enabled (only
+        * valid for I2C devices) */
        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);
+                       data->has_features |= HAS_FAN(2);
                }
 
                /* 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 (client->addr == 0x2e) {
-                       data->has_fan |= (1 << 3);
-                       data->has_pwm |= (1 << 2);
+                       data->has_features |= HAS_FAN(3) | HAS_PWM(2);
                }
 
                /* Determine which of the optional fan[5-6] and pwm[5-6]
@@ -2077,26 +2167,40 @@ static int dme1737_init_device(struct device *dev)
                        dev_warn(dev, "Failed to query Super-IO for optional "
                                 "features.\n");
                }
-       } else {   /* ISA chip */
-               /* Fan3 and pwm3 are always available. Fan[4-5] and pwm[5-6]
-                * don't exist in the ISA chip. */
-               data->has_fan |= (1 << 2);
-               data->has_pwm |= (1 << 2);
        }
 
-       /* Fan1, fan2, pwm1, and pwm2 are always present */
-       data->has_fan |= 0x03;
-       data->has_pwm |= 0x03;
+       /* Fan[1-2] and pwm[1-2] are present in all chips */
+       data->has_features |= HAS_FAN(0) | HAS_FAN(1) | HAS_PWM(0) | HAS_PWM(1);
+
+       /* Chip-dependent features */
+       switch (data->type) {
+       case dme1737:
+               data->has_features |= HAS_TEMP_OFFSET | HAS_VID | HAS_ZONE3 |
+                       HAS_ZONE_HYST | HAS_PWM_MIN;
+               break;
+       case sch311x:
+               data->has_features |= HAS_TEMP_OFFSET | HAS_ZONE3 |
+                       HAS_ZONE_HYST | HAS_PWM_MIN | HAS_FAN(2) | HAS_PWM(2);
+               break;
+       case sch5027:
+               data->has_features |= HAS_ZONE3;
+               break;
+       case sch5127:
+               data->has_features |= HAS_FAN(2) | HAS_PWM(2);
+               break;
+       default:
+               break;
+       }
 
        dev_info(dev, "Optional features: pwm3=%s, pwm5=%s, pwm6=%s, "
                 "fan3=%s, fan4=%s, fan5=%s, fan6=%s.\n",
-                (data->has_pwm & (1 << 2)) ? "yes" : "no",
-                (data->has_pwm & (1 << 4)) ? "yes" : "no",
-                (data->has_pwm & (1 << 5)) ? "yes" : "no",
-                (data->has_fan & (1 << 2)) ? "yes" : "no",
-                (data->has_fan & (1 << 3)) ? "yes" : "no",
-                (data->has_fan & (1 << 4)) ? "yes" : "no",
-                (data->has_fan & (1 << 5)) ? "yes" : "no");
+                (data->has_features & HAS_PWM(2)) ? "yes" : "no",
+                (data->has_features & HAS_PWM(4)) ? "yes" : "no",
+                (data->has_features & HAS_PWM(5)) ? "yes" : "no",
+                (data->has_features & HAS_FAN(2)) ? "yes" : "no",
+                (data->has_features & HAS_FAN(3)) ? "yes" : "no",
+                (data->has_features & HAS_FAN(4)) ? "yes" : "no",
+                (data->has_features & HAS_FAN(5)) ? "yes" : "no");
 
        reg = dme1737_read(data, DME1737_REG_TACH_PWM);
        /* Inform if fan-to-pwm mapping differs from the default */
@@ -2122,7 +2226,7 @@ static int dme1737_init_device(struct device *dev)
                for (ix = 0; ix < 3; ix++) {
                        data->pwm_config[ix] = dme1737_read(data,
                                                DME1737_REG_PWM_CONFIG(ix));
-                       if ((data->has_pwm & (1 << ix)) &&
+                       if ((data->has_features & HAS_PWM(ix)) &&
                            (PWM_EN_FROM_REG(data->pwm_config[ix]) == -1)) {
                                dev_info(dev, "Switching pwm%d to "
                                         "manual mode.\n", ix + 1);
@@ -2142,7 +2246,7 @@ static int dme1737_init_device(struct device *dev)
        data->pwm_acz[2] = 4;   /* pwm3 -> zone3 */
 
        /* Set VRM */
-       if (data->type == dme1737) {
+       if (data->has_features & HAS_VID) {
                data->vrm = vid_which_vrm();
        }
 
@@ -2163,10 +2267,10 @@ static int dme1737_i2c_get_features(int sio_cip, struct dme1737_data *data)
        dme1737_sio_enter(sio_cip);
 
        /* Check device ID
-        * The DME1737 can return either 0x78 or 0x77 as its device ID.
-        * The SCH5027 returns 0x89 as its device ID. */
+        * We currently know about two kinds of DME1737 and SCH5027. */
        reg = force_id ? force_id : dme1737_sio_inb(sio_cip, 0x20);
-       if (!(reg == 0x77 || reg == 0x78 || reg == 0x89)) {
+       if (!(reg == DME1737_ID_1 || reg == DME1737_ID_2 ||
+             reg == SCH5027_ID)) {
                err = -ENODEV;
                goto exit;
        }
@@ -2185,16 +2289,16 @@ static int dme1737_i2c_get_features(int sio_cip, struct dme1737_data *data)
         * are enabled and available. Bits [3:2] of registers 0x43-0x46 are set
         * to '10' if the respective feature is enabled. */
        if ((inb(addr + 0x43) & 0x0c) == 0x08) { /* fan6 */
-               data->has_fan |= (1 << 5);
+               data->has_features |= HAS_FAN(5);
        }
        if ((inb(addr + 0x44) & 0x0c) == 0x08) { /* pwm6 */
-               data->has_pwm |= (1 << 5);
+               data->has_features |= HAS_PWM(5);
        }
        if ((inb(addr + 0x45) & 0x0c) == 0x08) { /* fan5 */
-               data->has_fan |= (1 << 4);
+               data->has_features |= HAS_FAN(4);
        }
        if ((inb(addr + 0x46) & 0x0c) == 0x08) { /* pwm5 */
-               data->has_pwm |= (1 << 4);
+               data->has_features |= HAS_PWM(4);
        }
 
 exit:
@@ -2222,7 +2326,6 @@ static int dme1737_i2c_detect(struct i2c_client *client,
        if (company == DME1737_COMPANY_SMSC &&
            verstep == SCH5027_VERSTEP) {
                name = "sch5027";
-
        } else if (company == DME1737_COMPANY_SMSC &&
                   (verstep & DME1737_VERSTEP_MASK) == DME1737_VERSTEP) {
                name = "dme1737";
@@ -2329,10 +2432,10 @@ static int __init dme1737_isa_detect(int sio_cip, unsigned short *addr)
        dme1737_sio_enter(sio_cip);
 
        /* Check device ID
-        * We currently know about SCH3112 (0x7c), SCH3114 (0x7d), and
-        * SCH3116 (0x7f). */
+        * We currently know about SCH3112, SCH3114, SCH3116, and SCH5127 */
        reg = force_id ? force_id : dme1737_sio_inb(sio_cip, 0x20);
-       if (!(reg == 0x7c || reg == 0x7d || reg == 0x7f)) {
+       if (!(reg == SCH3112_ID || reg == SCH3114_ID || reg == SCH3116_ID ||
+             reg == SCH5127_ID)) {
                err = -ENODEV;
                goto exit;
        }
@@ -2424,23 +2527,42 @@ static int __devinit dme1737_isa_probe(struct platform_device *pdev)
        platform_set_drvdata(pdev, data);
 
        /* Skip chip detection if module is loaded with force_id parameter */
-       if (!force_id) {
+       switch (force_id) {
+       case SCH3112_ID:
+       case SCH3114_ID:
+       case SCH3116_ID:
+               data->type = sch311x;
+               break;
+       case SCH5127_ID:
+               data->type = sch5127;
+               break;
+       default:
                company = dme1737_read(data, DME1737_REG_COMPANY);
                device = dme1737_read(data, DME1737_REG_DEVICE);
 
-               if (!((company == DME1737_COMPANY_SMSC) &&
-                     (device == SCH311X_DEVICE))) {
+               if ((company == DME1737_COMPANY_SMSC) &&
+                   (device == SCH311X_DEVICE)) {
+                       data->type = sch311x;
+               } else if ((company == DME1737_COMPANY_SMSC) &&
+                          (device == SCH5127_DEVICE)) {
+                       data->type = sch5127;
+               } else {
                        err = -ENODEV;
                        goto exit_kfree;
                }
        }
-       data->type = sch311x;
 
-       /* Fill in the remaining client fields and initialize the mutex */
-       data->name = "sch311x";
+       if (data->type == sch5127) {
+               data->name = "sch5127";
+       } else {
+               data->name = "sch311x";
+       }
+
+       /* Initialize the mutex */
        mutex_init(&data->update_lock);
 
-       dev_info(dev, "Found a SCH311x chip at 0x%04x\n", data->addr);
+       dev_info(dev, "Found a %s chip at 0x%04x\n",
+                data->type == sch5127 ? "SCH5127" : "SCH311x", data->addr);
 
        /* Initialize the chip */
        if ((err = dme1737_init_device(dev))) {
diff --git a/drivers/hwmon/emc1403.c b/drivers/hwmon/emc1403.c
new file mode 100644 (file)
index 0000000..0e4b564
--- /dev/null
@@ -0,0 +1,344 @@
+/*
+ * emc1403.c - SMSC Thermal Driver
+ *
+ * Copyright (C) 2008 Intel Corp
+ *
+ *  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * 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; 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; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * TODO
+ *     -       cache alarm and critical limit registers
+ *     -       add emc1404 support
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/err.h>
+#include <linux/sysfs.h>
+#include <linux/mutex.h>
+
+#define THERMAL_PID_REG                0xfd
+#define THERMAL_SMSC_ID_REG    0xfe
+#define THERMAL_REVISION_REG   0xff
+
+struct thermal_data {
+       struct device *hwmon_dev;
+       struct mutex mutex;
+       /* Cache the hyst value so we don't keep re-reading it. In theory
+          we could cache it forever as nobody else should be writing it. */
+       u8 cached_hyst;
+       unsigned long hyst_valid;
+};
+
+static ssize_t show_temp(struct device *dev,
+                       struct device_attribute *attr, char *buf)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+       struct sensor_device_attribute *sda = to_sensor_dev_attr(attr);
+       int retval = i2c_smbus_read_byte_data(client, sda->index);
+
+       if (retval < 0)
+               return retval;
+       return sprintf(buf, "%d000\n", retval);
+}
+
+static ssize_t show_bit(struct device *dev,
+                       struct device_attribute *attr, char *buf)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+       struct sensor_device_attribute_2 *sda = to_sensor_dev_attr_2(attr);
+       int retval = i2c_smbus_read_byte_data(client, sda->nr);
+
+       if (retval < 0)
+               return retval;
+       retval &= sda->index;
+       return sprintf(buf, "%d\n", retval ? 1 : 0);
+}
+
+static ssize_t store_temp(struct device *dev,
+               struct device_attribute *attr, const char *buf, size_t count)
+{
+       struct sensor_device_attribute *sda = to_sensor_dev_attr(attr);
+       struct i2c_client *client = to_i2c_client(dev);
+       unsigned long val;
+       int retval;
+
+       if (strict_strtoul(buf, 10, &val))
+               return -EINVAL;
+       retval = i2c_smbus_write_byte_data(client, sda->index,
+                                       DIV_ROUND_CLOSEST(val, 1000));
+       if (retval < 0)
+               return retval;
+       return count;
+}
+
+static ssize_t show_hyst(struct device *dev,
+                       struct device_attribute *attr, char *buf)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+       struct thermal_data *data = i2c_get_clientdata(client);
+       struct sensor_device_attribute *sda = to_sensor_dev_attr(attr);
+       int retval;
+       int hyst;
+
+       retval = i2c_smbus_read_byte_data(client, sda->index);
+       if (retval < 0)
+               return retval;
+
+       if (time_after(jiffies, data->hyst_valid)) {
+               hyst = i2c_smbus_read_byte_data(client, 0x21);
+               if (hyst < 0)
+                       return retval;
+               data->cached_hyst = hyst;
+               data->hyst_valid = jiffies + HZ;
+       }
+       return sprintf(buf, "%d000\n", retval - data->cached_hyst);
+}
+
+static ssize_t store_hyst(struct device *dev,
+               struct device_attribute *attr, const char *buf, size_t count)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+       struct thermal_data *data = i2c_get_clientdata(client);
+       struct sensor_device_attribute *sda = to_sensor_dev_attr(attr);
+       int retval;
+       int hyst;
+       unsigned long val;
+
+       if (strict_strtoul(buf, 10, &val))
+               return -EINVAL;
+
+       mutex_lock(&data->mutex);
+       retval = i2c_smbus_read_byte_data(client, sda->index);
+       if (retval < 0)
+               goto fail;
+
+       hyst = val - retval * 1000;
+       hyst = DIV_ROUND_CLOSEST(hyst, 1000);
+       if (hyst < 0 || hyst > 255) {
+               retval = -ERANGE;
+               goto fail;
+       }
+
+       retval = i2c_smbus_write_byte_data(client, 0x21, hyst);
+       if (retval == 0) {
+               retval = count;
+               data->cached_hyst = hyst;
+               data->hyst_valid = jiffies + HZ;
+       }
+fail:
+       mutex_unlock(&data->mutex);
+       return retval;
+}
+
+/*
+ *     Sensors. We pass the actual i2c register to the methods.
+ */
+
+static SENSOR_DEVICE_ATTR(temp1_min, S_IRUGO | S_IWUSR,
+       show_temp, store_temp, 0x06);
+static SENSOR_DEVICE_ATTR(temp1_max, S_IRUGO | S_IWUSR,
+       show_temp, store_temp, 0x05);
+static SENSOR_DEVICE_ATTR(temp1_crit, S_IRUGO | S_IWUSR,
+       show_temp, store_temp, 0x20);
+static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL, 0x00);
+static SENSOR_DEVICE_ATTR_2(temp1_min_alarm, S_IRUGO,
+       show_bit, NULL, 0x36, 0x01);
+static SENSOR_DEVICE_ATTR_2(temp1_max_alarm, S_IRUGO,
+       show_bit, NULL, 0x35, 0x01);
+static SENSOR_DEVICE_ATTR_2(temp1_crit_alarm, S_IRUGO,
+       show_bit, NULL, 0x37, 0x01);
+static SENSOR_DEVICE_ATTR(temp1_crit_hyst, S_IRUGO | S_IWUSR,
+       show_hyst, store_hyst, 0x20);
+
+static SENSOR_DEVICE_ATTR(temp2_min, S_IRUGO | S_IWUSR,
+       show_temp, store_temp, 0x08);
+static SENSOR_DEVICE_ATTR(temp2_max, S_IRUGO | S_IWUSR,
+       show_temp, store_temp, 0x07);
+static SENSOR_DEVICE_ATTR(temp2_crit, S_IRUGO | S_IWUSR,
+       show_temp, store_temp, 0x19);
+static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, show_temp, NULL, 0x01);
+static SENSOR_DEVICE_ATTR_2(temp2_min_alarm, S_IRUGO,
+       show_bit, NULL, 0x36, 0x02);
+static SENSOR_DEVICE_ATTR_2(temp2_max_alarm, S_IRUGO,
+       show_bit, NULL, 0x35, 0x02);
+static SENSOR_DEVICE_ATTR_2(temp2_crit_alarm, S_IRUGO,
+       show_bit, NULL, 0x37, 0x02);
+static SENSOR_DEVICE_ATTR(temp2_crit_hyst, S_IRUGO | S_IWUSR,
+       show_hyst, store_hyst, 0x19);
+
+static SENSOR_DEVICE_ATTR(temp3_min, S_IRUGO | S_IWUSR,
+       show_temp, store_temp, 0x16);
+static SENSOR_DEVICE_ATTR(temp3_max, S_IRUGO | S_IWUSR,
+       show_temp, store_temp, 0x15);
+static SENSOR_DEVICE_ATTR(temp3_crit, S_IRUGO | S_IWUSR,
+       show_temp, store_temp, 0x1A);
+static SENSOR_DEVICE_ATTR(temp3_input, S_IRUGO, show_temp, NULL, 0x23);
+static SENSOR_DEVICE_ATTR_2(temp3_min_alarm, S_IRUGO,
+       show_bit, NULL, 0x36, 0x04);
+static SENSOR_DEVICE_ATTR_2(temp3_max_alarm, S_IRUGO,
+       show_bit, NULL, 0x35, 0x04);
+static SENSOR_DEVICE_ATTR_2(temp3_crit_alarm, S_IRUGO,
+       show_bit, NULL, 0x37, 0x04);
+static SENSOR_DEVICE_ATTR(temp3_crit_hyst, S_IRUGO | S_IWUSR,
+       show_hyst, store_hyst, 0x1A);
+
+static struct attribute *mid_att_thermal[] = {
+       &sensor_dev_attr_temp1_min.dev_attr.attr,
+       &sensor_dev_attr_temp1_max.dev_attr.attr,
+       &sensor_dev_attr_temp1_crit.dev_attr.attr,
+       &sensor_dev_attr_temp1_input.dev_attr.attr,
+       &sensor_dev_attr_temp1_min_alarm.dev_attr.attr,
+       &sensor_dev_attr_temp1_max_alarm.dev_attr.attr,
+       &sensor_dev_attr_temp1_crit_alarm.dev_attr.attr,
+       &sensor_dev_attr_temp1_crit_hyst.dev_attr.attr,
+       &sensor_dev_attr_temp2_min.dev_attr.attr,
+       &sensor_dev_attr_temp2_max.dev_attr.attr,
+       &sensor_dev_attr_temp2_crit.dev_attr.attr,
+       &sensor_dev_attr_temp2_input.dev_attr.attr,
+       &sensor_dev_attr_temp2_min_alarm.dev_attr.attr,
+       &sensor_dev_attr_temp2_max_alarm.dev_attr.attr,
+       &sensor_dev_attr_temp2_crit_alarm.dev_attr.attr,
+       &sensor_dev_attr_temp2_crit_hyst.dev_attr.attr,
+       &sensor_dev_attr_temp3_min.dev_attr.attr,
+       &sensor_dev_attr_temp3_max.dev_attr.attr,
+       &sensor_dev_attr_temp3_crit.dev_attr.attr,
+       &sensor_dev_attr_temp3_input.dev_attr.attr,
+       &sensor_dev_attr_temp3_min_alarm.dev_attr.attr,
+       &sensor_dev_attr_temp3_max_alarm.dev_attr.attr,
+       &sensor_dev_attr_temp3_crit_alarm.dev_attr.attr,
+       &sensor_dev_attr_temp3_crit_hyst.dev_attr.attr,
+       NULL
+};
+
+static const struct attribute_group m_thermal_gr = {
+       .attrs = mid_att_thermal
+};
+
+static int emc1403_detect(struct i2c_client *client,
+                       struct i2c_board_info *info)
+{
+       int id;
+       /* Check if thermal chip is SMSC and EMC1403 */
+
+       id = i2c_smbus_read_byte_data(client, THERMAL_SMSC_ID_REG);
+       if (id != 0x5d)
+               return -ENODEV;
+
+       /* Note: 0x25 is the 1404 which is very similar and this
+          driver could be extended */
+       id = i2c_smbus_read_byte_data(client, THERMAL_PID_REG);
+       if (id != 0x21)
+               return -ENODEV;
+
+       id = i2c_smbus_read_byte_data(client, THERMAL_REVISION_REG);
+       if (id != 0x01)
+               return -ENODEV;
+
+       strlcpy(info->type, "emc1403", I2C_NAME_SIZE);
+       return 0;
+}
+
+static int emc1403_probe(struct i2c_client *client,
+                       const struct i2c_device_id *id)
+{
+       int res;
+       struct thermal_data *data;
+
+       data = kzalloc(sizeof(struct thermal_data), GFP_KERNEL);
+       if (data == NULL) {
+               dev_warn(&client->dev, "out of memory");
+               return -ENOMEM;
+       }
+
+       i2c_set_clientdata(client, data);
+       mutex_init(&data->mutex);
+       data->hyst_valid = jiffies - 1;         /* Expired */
+
+       res = sysfs_create_group(&client->dev.kobj, &m_thermal_gr);
+       if (res) {
+               dev_warn(&client->dev, "create group failed\n");
+               hwmon_device_unregister(data->hwmon_dev);
+               goto thermal_error1;
+       }
+       data->hwmon_dev = hwmon_device_register(&client->dev);
+       if (IS_ERR(data->hwmon_dev)) {
+               res = PTR_ERR(data->hwmon_dev);
+               dev_warn(&client->dev, "register hwmon dev failed\n");
+               goto thermal_error2;
+       }
+       dev_info(&client->dev, "EMC1403 Thermal chip found\n");
+       return res;
+
+thermal_error2:
+       sysfs_remove_group(&client->dev.kobj, &m_thermal_gr);
+thermal_error1:
+       kfree(data);
+       return res;
+}
+
+static int emc1403_remove(struct i2c_client *client)
+{
+       struct thermal_data *data = i2c_get_clientdata(client);
+
+       hwmon_device_unregister(data->hwmon_dev);
+       sysfs_remove_group(&client->dev.kobj, &m_thermal_gr);
+       kfree(data);
+       return 0;
+}
+
+static const unsigned short emc1403_address_list[] = {
+       0x18, 0x2a, 0x4c, 0x4d, I2C_CLIENT_END
+};
+
+static const struct i2c_device_id emc1403_idtable[] = {
+       { "emc1403", 0 },
+       { }
+};
+MODULE_DEVICE_TABLE(i2c, emc1403_idtable);
+
+static struct i2c_driver sensor_emc1403 = {
+       .class = I2C_CLASS_HWMON,
+       .driver = {
+               .name = "emc1403",
+       },
+       .detect = emc1403_detect,
+       .probe = emc1403_probe,
+       .remove = emc1403_remove,
+       .id_table = emc1403_idtable,
+       .address_list = emc1403_address_list,
+};
+
+static int __init sensor_emc1403_init(void)
+{
+       return i2c_add_driver(&sensor_emc1403);
+}
+
+static void  __exit sensor_emc1403_exit(void)
+{
+       i2c_del_driver(&sensor_emc1403);
+}
+
+module_init(sensor_emc1403_init);
+module_exit(sensor_emc1403_exit);
+
+MODULE_AUTHOR("Kalhan Trisal <kalhan.trisal@intel.com");
+MODULE_DESCRIPTION("emc1403 Thermal Driver");
+MODULE_LICENSE("GPL v2");
index a95fa42..537841e 100644 (file)
@@ -856,21 +856,19 @@ static inline int superio_inb(int base, int reg)
 static int superio_inw(int base, int reg)
 {
        int val;
-       outb(reg++, base);
-       val = inb(base + 1) << 8;
-       outb(reg, base);
-       val |= inb(base + 1);
+       val  = superio_inb(base, reg) << 8;
+       val |= superio_inb(base, reg + 1);
        return val;
 }
 
 static inline void superio_enter(int base)
 {
        /* according to the datasheet the key must be send twice! */
-       outb( SIO_UNLOCK_KEY, base);
-       outb( SIO_UNLOCK_KEY, base);
+       outb(SIO_UNLOCK_KEY, base);
+       outb(SIO_UNLOCK_KEY, base);
 }
 
-static inline void superio_select( int base, int ld)
+static inline void superio_select(int base, int ld)
 {
        outb(SIO_REG_LDSEL, base);
        outb(ld, base + 1);
@@ -905,10 +903,8 @@ static u16 f71882fg_read16(struct f71882fg_data *data, u8 reg)
 {
        u16 val;
 
-       outb(reg++, data->addr + ADDR_REG_OFFSET);
-       val = inb(data->addr + DATA_REG_OFFSET) << 8;
-       outb(reg, data->addr + ADDR_REG_OFFSET);
-       val |= inb(data->addr + DATA_REG_OFFSET);
+       val  = f71882fg_read8(data, reg) << 8;
+       val |= f71882fg_read8(data, reg + 1);
 
        return val;
 }
@@ -921,10 +917,8 @@ static void f71882fg_write8(struct f71882fg_data *data, u8 reg, u8 val)
 
 static void f71882fg_write16(struct f71882fg_data *data, u8 reg, u16 val)
 {
-       outb(reg++, data->addr + ADDR_REG_OFFSET);
-       outb(val >> 8, data->addr + DATA_REG_OFFSET);
-       outb(reg, data->addr + ADDR_REG_OFFSET);
-       outb(val & 255, data->addr + DATA_REG_OFFSET);
+       f71882fg_write8(data, reg,     val >> 8);
+       f71882fg_write8(data, reg + 1, val & 0xff);
 }
 
 static u16 f71882fg_read_temp(struct f71882fg_data *data, int nr)
@@ -945,7 +939,7 @@ static struct f71882fg_data *f71882fg_update_device(struct device *dev)
        mutex_lock(&data->update_lock);
 
        /* Update once every 60 seconds */
-       if ( time_after(jiffies, data->last_limits + 60 * HZ ) ||
+       if (time_after(jiffies, data->last_limits + 60 * HZ) ||
                        !data->valid) {
                if (data->type == f71882fg || data->type == f71889fg) {
                        data->in1_max =
@@ -1127,8 +1121,12 @@ static ssize_t store_fan_full_speed(struct device *dev,
                                    const char *buf, size_t count)
 {
        struct f71882fg_data *data = dev_get_drvdata(dev);
-       int nr = to_sensor_dev_attr_2(devattr)->index;
-       long val = simple_strtol(buf, NULL, 10);
+       int err, nr = to_sensor_dev_attr_2(devattr)->index;
+       long val;
+
+       err = strict_strtol(buf, 10, &val);
+       if (err)
+               return err;
 
        val = SENSORS_LIMIT(val, 23, 1500000);
        val = fan_to_reg(val);
@@ -1157,8 +1155,12 @@ static ssize_t store_fan_beep(struct device *dev, struct device_attribute
        *devattr, const char *buf, size_t count)
 {
        struct f71882fg_data *data = dev_get_drvdata(dev);
-       int nr = to_sensor_dev_attr_2(devattr)->index;
-       unsigned long val = simple_strtoul(buf, NULL, 10);
+       int err, nr = to_sensor_dev_attr_2(devattr)->index;
+       unsigned long val;
+
+       err = strict_strtoul(buf, 10, &val);
+       if (err)
+               return err;
 
        mutex_lock(&data->update_lock);
        data->fan_beep = f71882fg_read8(data, F71882FG_REG_FAN_BEEP);
@@ -1206,7 +1208,14 @@ static ssize_t store_in_max(struct device *dev, struct device_attribute
        *devattr, const char *buf, size_t count)
 {
        struct f71882fg_data *data = dev_get_drvdata(dev);
-       long val = simple_strtol(buf, NULL, 10) / 8;
+       int err;
+       long val;
+
+       err = strict_strtol(buf, 10, &val);
+       if (err)
+               return err;
+
+       val /= 8;
        val = SENSORS_LIMIT(val, 0, 255);
 
        mutex_lock(&data->update_lock);
@@ -1233,8 +1242,12 @@ static ssize_t store_in_beep(struct device *dev, struct device_attribute
        *devattr, const char *buf, size_t count)
 {
        struct f71882fg_data *data = dev_get_drvdata(dev);
-       int nr = to_sensor_dev_attr_2(devattr)->index;
-       unsigned long val = simple_strtoul(buf, NULL, 10);
+       int err, nr = to_sensor_dev_attr_2(devattr)->index;
+       unsigned long val;
+
+       err = strict_strtoul(buf, 10, &val);
+       if (err)
+               return err;
 
        mutex_lock(&data->update_lock);
        data->in_beep = f71882fg_read8(data, F71882FG_REG_IN_BEEP);
@@ -1299,8 +1312,14 @@ static ssize_t store_temp_max(struct device *dev, struct device_attribute
        *devattr, const char *buf, size_t count)
 {
        struct f71882fg_data *data = dev_get_drvdata(dev);
-       int nr = to_sensor_dev_attr_2(devattr)->index;
-       long val = simple_strtol(buf, NULL, 10) / 1000;
+       int err, nr = to_sensor_dev_attr_2(devattr)->index;
+       long val;
+
+       err = strict_strtol(buf, 10, &val);
+       if (err)
+               return err;
+
+       val /= 1000;
        val = SENSORS_LIMIT(val, 0, 255);
 
        mutex_lock(&data->update_lock);
@@ -1333,10 +1352,16 @@ static ssize_t store_temp_max_hyst(struct device *dev, struct device_attribute
        *devattr, const char *buf, size_t count)
 {
        struct f71882fg_data *data = dev_get_drvdata(dev);
-       int nr = to_sensor_dev_attr_2(devattr)->index;
-       long val = simple_strtol(buf, NULL, 10) / 1000;
+       int err, nr = to_sensor_dev_attr_2(devattr)->index;
        ssize_t ret = count;
        u8 reg;
+       long val;
+
+       err = strict_strtol(buf, 10, &val);
+       if (err)
+               return err;
+
+       val /= 1000;
 
        mutex_lock(&data->update_lock);
 
@@ -1372,8 +1397,14 @@ static ssize_t store_temp_crit(struct device *dev, struct device_attribute
        *devattr, const char *buf, size_t count)
 {
        struct f71882fg_data *data = dev_get_drvdata(dev);
-       int nr = to_sensor_dev_attr_2(devattr)->index;
-       long val = simple_strtol(buf, NULL, 10) / 1000;
+       int err, nr = to_sensor_dev_attr_2(devattr)->index;
+       long val;
+
+       err = strict_strtol(buf, 10, &val);
+       if (err)
+               return err;
+
+       val /= 1000;
        val = SENSORS_LIMIT(val, 0, 255);
 
        mutex_lock(&data->update_lock);
@@ -1427,8 +1458,12 @@ static ssize_t store_temp_beep(struct device *dev, struct device_attribute
        *devattr, const char *buf, size_t count)
 {
        struct f71882fg_data *data = dev_get_drvdata(dev);
-       int nr = to_sensor_dev_attr_2(devattr)->index;
-       unsigned long val = simple_strtoul(buf, NULL, 10);
+       int err, nr = to_sensor_dev_attr_2(devattr)->index;
+       unsigned long val;
+
+       err = strict_strtoul(buf, 10, &val);
+       if (err)
+               return err;
 
        mutex_lock(&data->update_lock);
        data->temp_beep = f71882fg_read8(data, F71882FG_REG_TEMP_BEEP);
@@ -1490,8 +1525,13 @@ static ssize_t store_pwm(struct device *dev,
                         size_t count)
 {
        struct f71882fg_data *data = dev_get_drvdata(dev);
-       int nr = to_sensor_dev_attr_2(devattr)->index;
-       long val = simple_strtol(buf, NULL, 10);
+       int err, nr = to_sensor_dev_attr_2(devattr)->index;
+       long val;
+
+       err = strict_strtol(buf, 10, &val);
+       if (err)
+               return err;
+
        val = SENSORS_LIMIT(val, 0, 255);
 
        mutex_lock(&data->update_lock);
@@ -1551,8 +1591,12 @@ static ssize_t store_pwm_enable(struct device *dev, struct device_attribute
                                *devattr, const char *buf, size_t count)
 {
        struct f71882fg_data *data = dev_get_drvdata(dev);
-       int nr = to_sensor_dev_attr_2(devattr)->index;
-       long val = simple_strtol(buf, NULL, 10);
+       int err, nr = to_sensor_dev_attr_2(devattr)->index;
+       long val;
+
+       err = strict_strtol(buf, 10, &val);
+       if (err)
+               return err;
 
        /* Special case for F8000 pwm channel 3 which only does auto mode */
        if (data->type == f8000 && nr == 2 && val != 2)
@@ -1626,9 +1670,14 @@ static ssize_t store_pwm_auto_point_pwm(struct device *dev,
                                        const char *buf, size_t count)
 {
        struct f71882fg_data *data = dev_get_drvdata(dev);
-       int pwm = to_sensor_dev_attr_2(devattr)->index;
+       int err, pwm = to_sensor_dev_attr_2(devattr)->index;
        int point = to_sensor_dev_attr_2(devattr)->nr;
-       long val = simple_strtol(buf, NULL, 10);
+       long val;
+
+       err = strict_strtol(buf, 10, &val);
+       if (err)
+               return err;
+
        val = SENSORS_LIMIT(val, 0, 255);
 
        mutex_lock(&data->update_lock);
@@ -1674,10 +1723,16 @@ static ssize_t store_pwm_auto_point_temp_hyst(struct device *dev,
                                              const char *buf, size_t count)
 {
        struct f71882fg_data *data = dev_get_drvdata(dev);
-       int nr = to_sensor_dev_attr_2(devattr)->index;
+       int err, nr = to_sensor_dev_attr_2(devattr)->index;
        int point = to_sensor_dev_attr_2(devattr)->nr;
-       long val = simple_strtol(buf, NULL, 10) / 1000;
        u8 reg;
+       long val;
+
+       err = strict_strtol(buf, 10, &val);
+       if (err)
+               return err;
+
+       val /= 1000;
 
        mutex_lock(&data->update_lock);
        data->pwm_auto_point_temp[nr][point] =
@@ -1716,8 +1771,12 @@ static ssize_t store_pwm_interpolate(struct device *dev,
                                     const char *buf, size_t count)
 {
        struct f71882fg_data *data = dev_get_drvdata(dev);
-       int nr = to_sensor_dev_attr_2(devattr)->index;
-       unsigned long val = simple_strtoul(buf, NULL, 10);
+       int err, nr = to_sensor_dev_attr_2(devattr)->index;
+       unsigned long val;
+
+       err = strict_strtoul(buf, 10, &val);
+       if (err)
+               return err;
 
        mutex_lock(&data->update_lock);
        data->pwm_auto_point_mapping[nr] =
@@ -1752,8 +1811,12 @@ static ssize_t store_pwm_auto_point_channel(struct device *dev,
                                            const char *buf, size_t count)
 {
        struct f71882fg_data *data = dev_get_drvdata(dev);
-       int nr = to_sensor_dev_attr_2(devattr)->index;
-       long val = simple_strtol(buf, NULL, 10);
+       int err, nr = to_sensor_dev_attr_2(devattr)->index;
+       long val;
+
+       err = strict_strtol(buf, 10, &val);
+       if (err)
+               return err;
 
        switch (val) {
        case 1:
@@ -1798,9 +1861,15 @@ static ssize_t store_pwm_auto_point_temp(struct device *dev,
                                         const char *buf, size_t count)
 {
        struct f71882fg_data *data = dev_get_drvdata(dev);
-       int pwm = to_sensor_dev_attr_2(devattr)->index;
+       int err, pwm = to_sensor_dev_attr_2(devattr)->index;
        int point = to_sensor_dev_attr_2(devattr)->nr;
-       long val = simple_strtol(buf, NULL, 10) / 1000;
+       long val;
+
+       err = strict_strtol(buf, 10, &val);
+       if (err)
+               return err;
+
+       val /= 1000;
 
        if (data->type == f71889fg)
                val = SENSORS_LIMIT(val, -128, 127);
@@ -2109,6 +2178,13 @@ static int __init f71882fg_find(int sioaddr, unsigned short *address,
        int err = -ENODEV;
        u16 devid;
 
+       /* Don't step on other drivers' I/O space by accident */
+       if (!request_region(sioaddr, 2, DRVNAME)) {
+               printk(KERN_ERR DRVNAME ": I/O address 0x%04x already in use\n",
+                               (int)sioaddr);
+               return -EBUSY;
+       }
+
        superio_enter(sioaddr);
 
        devid = superio_inw(sioaddr, SIO_REG_MANID);
@@ -2151,8 +2227,7 @@ static int __init f71882fg_find(int sioaddr, unsigned short *address,
        }
 
        *address = superio_inw(sioaddr, SIO_REG_ADDR);
-       if (*address == 0)
-       {
+       if (*address == 0) {
                printk(KERN_WARNING DRVNAME ": Base address not set\n");
                goto exit;
        }
@@ -2164,6 +2239,7 @@ static int __init f71882fg_find(int sioaddr, unsigned short *address,
                (int)superio_inb(sioaddr, SIO_REG_DEVREV));
 exit:
        superio_exit(sioaddr);
+       release_region(sioaddr, 2);
        return err;
 }
 
index bf81aff..776aeb3 100644 (file)
@@ -53,7 +53,7 @@
  * Address is fully defined internally and cannot be changed.
  */
 
-static const unsigned short normal_i2c[] = { 0x4c, I2C_CLIENT_END };
+static const unsigned short normal_i2c[] = { 0x18, 0x4c, 0x4e, I2C_CLIENT_END };
 
 /*
  * The LM63 registers
@@ -131,12 +131,15 @@ static struct lm63_data *lm63_update_device(struct device *dev);
 static int lm63_detect(struct i2c_client *client, struct i2c_board_info *info);
 static void lm63_init_client(struct i2c_client *client);
 
+enum chips { lm63, lm64 };
+
 /*
  * Driver data (common to all clients)
  */
 
 static const struct i2c_device_id lm63_id[] = {
-       { "lm63", 0 },
+       { "lm63", lm63 },
+       { "lm64", lm64 },
        { }
 };
 MODULE_DEVICE_TABLE(i2c, lm63_id);
@@ -422,6 +425,7 @@ static int lm63_detect(struct i2c_client *new_client,
        struct i2c_adapter *adapter = new_client->adapter;
        u8 man_id, chip_id, reg_config1, reg_config2;
        u8 reg_alert_status, reg_alert_mask;
+       int address = new_client->addr;
 
        if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
                return -ENODEV;
@@ -439,7 +443,6 @@ static int lm63_detect(struct i2c_client *new_client,
                         LM63_REG_ALERT_MASK);
 
        if (man_id != 0x01 /* National Semiconductor */
-        || chip_id != 0x41 /* LM63 */
         || (reg_config1 & 0x18) != 0x00
         || (reg_config2 & 0xF8) != 0x00
         || (reg_alert_status & 0x20) != 0x00
@@ -450,7 +453,12 @@ static int lm63_detect(struct i2c_client *new_client,
                return -ENODEV;
        }
 
-       strlcpy(info->type, "lm63", I2C_NAME_SIZE);
+       if (chip_id == 0x41 && address == 0x4c)
+               strlcpy(info->type, "lm63", I2C_NAME_SIZE);
+       else if (chip_id == 0x51 && (address == 0x18 || address == 0x4e))
+               strlcpy(info->type, "lm64", I2C_NAME_SIZE);
+       else
+               return -ENODEV;
 
        return 0;
 }
index 8ae2cfe..5646342 100644 (file)
@@ -46,6 +46,7 @@ enum lm75_type {              /* keep sorted in alphabetical order */
        tcn75,
        tmp100,
        tmp101,
+       tmp105,
        tmp175,
        tmp275,
        tmp75,
@@ -220,6 +221,7 @@ static const struct i2c_device_id lm75_ids[] = {
        { "tcn75", tcn75, },
        { "tmp100", tmp100, },
        { "tmp101", tmp101, },
+       { "tmp105", tmp105, },
        { "tmp175", tmp175, },
        { "tmp275", tmp275, },
        { "tmp75", tmp75, },
index 7cc2708..760ef72 100644 (file)
@@ -982,7 +982,8 @@ static struct lm90_data *lm90_update_device(struct device *dev)
 
        mutex_lock(&data->update_lock);
 
-       if (time_after(jiffies, data->last_updated + HZ * 2) || !data->valid) {
+       if (time_after(jiffies, data->last_updated + HZ / 2 + HZ / 10)
+        || !data->valid) {
                u8 h, l;
 
                dev_dbg(&client->dev, "Updating lm90 data.\n");
index 65c232a..21d201b 100644 (file)
@@ -45,9 +45,7 @@ enum ltc4245_cmd {
        LTC4245_VEEIN                   = 0x19,
        LTC4245_VEESENSE                = 0x1a,
        LTC4245_VEEOUT                  = 0x1b,
-       LTC4245_GPIOADC1                = 0x1c,
-       LTC4245_GPIOADC2                = 0x1d,
-       LTC4245_GPIOADC3                = 0x1e,
+       LTC4245_GPIOADC                 = 0x1c,
 };
 
 struct ltc4245_data {
@@ -61,7 +59,7 @@ struct ltc4245_data {
        u8 cregs[0x08];
 
        /* Voltage registers */
-       u8 vregs[0x0f];
+       u8 vregs[0x0d];
 };
 
 static struct ltc4245_data *ltc4245_update_device(struct device *dev)
@@ -86,7 +84,7 @@ static struct ltc4245_data *ltc4245_update_device(struct device *dev)
                                data->cregs[i] = val;
                }
 
-               /* Read voltage registers -- 0x10 to 0x1f */
+               /* Read voltage registers -- 0x10 to 0x1c */
                for (i = 0; i < ARRAY_SIZE(data->vregs); i++) {
                        val = i2c_smbus_read_byte_data(client, i+0x10);
                        if (unlikely(val < 0))
@@ -128,9 +126,7 @@ static int ltc4245_get_voltage(struct device *dev, u8 reg)
        case LTC4245_VEEOUT:
                voltage = regval * -55;
                break;
-       case LTC4245_GPIOADC1:
-       case LTC4245_GPIOADC2:
-       case LTC4245_GPIOADC3:
+       case LTC4245_GPIOADC:
                voltage = regval * 10;
                break;
        default:
@@ -297,9 +293,7 @@ LTC4245_ALARM(in7_min_alarm,        (1 << 2),       LTC4245_FAULT2);
 LTC4245_ALARM(in8_min_alarm,   (1 << 3),       LTC4245_FAULT2);
 
 /* GPIO voltages */
-LTC4245_VOLTAGE(in9_input,                     LTC4245_GPIOADC1);
-LTC4245_VOLTAGE(in10_input,                    LTC4245_GPIOADC2);
-LTC4245_VOLTAGE(in11_input,                    LTC4245_GPIOADC3);
+LTC4245_VOLTAGE(in9_input,                     LTC4245_GPIOADC);
 
 /* Power Consumption (virtual) */
 LTC4245_POWER(power1_input,                    LTC4245_12VSENSE);
@@ -342,8 +336,6 @@ static struct attribute *ltc4245_attributes[] = {
        &sensor_dev_attr_in8_min_alarm.dev_attr.attr,
 
        &sensor_dev_attr_in9_input.dev_attr.attr,
-       &sensor_dev_attr_in10_input.dev_attr.attr,
-       &sensor_dev_attr_in11_input.dev_attr.attr,
 
        &sensor_dev_attr_power1_input.dev_attr.attr,
        &sensor_dev_attr_power2_input.dev_attr.attr,
diff --git a/drivers/hwmon/tmp102.c b/drivers/hwmon/tmp102.c
new file mode 100644 (file)
index 0000000..8013895
--- /dev/null
@@ -0,0 +1,321 @@
+/* Texas Instruments TMP102 SMBus temperature sensor driver
+ *
+ * Copyright (C) 2010 Steven King <sfking@fdwdc.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.
+ *
+ * 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., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/err.h>
+#include <linux/mutex.h>
+#include <linux/device.h>
+
+#define        DRIVER_NAME "tmp102"
+
+#define        TMP102_TEMP_REG                 0x00
+#define        TMP102_CONF_REG                 0x01
+/* note: these bit definitions are byte swapped */
+#define                TMP102_CONF_SD          0x0100
+#define                TMP102_CONF_TM          0x0200
+#define                TMP102_CONF_POL         0x0400
+#define                TMP102_CONF_F0          0x0800
+#define                TMP102_CONF_F1          0x1000
+#define                TMP102_CONF_R0          0x2000
+#define                TMP102_CONF_R1          0x4000
+#define                TMP102_CONF_OS          0x8000
+#define                TMP102_CONF_EM          0x0010
+#define                TMP102_CONF_AL          0x0020
+#define                TMP102_CONF_CR0         0x0040
+#define                TMP102_CONF_CR1         0x0080
+#define        TMP102_TLOW_REG                 0x02
+#define        TMP102_THIGH_REG                0x03
+
+struct tmp102 {
+       struct device *hwmon_dev;
+       struct mutex lock;
+       u16 config_orig;
+       unsigned long last_update;
+       int temp[3];
+};
+
+/* SMBus specifies low byte first, but the TMP102 returns high byte first,
+ * so we have to swab16 the values */
+static inline int tmp102_read_reg(struct i2c_client *client, u8 reg)
+{
+       int result = i2c_smbus_read_word_data(client, reg);
+       return result < 0 ? result : swab16(result);
+}
+
+static inline int tmp102_write_reg(struct i2c_client *client, u8 reg, u16 val)
+{
+       return i2c_smbus_write_word_data(client, reg, swab16(val));
+}
+
+/* convert left adjusted 13-bit TMP102 register value to milliCelsius */
+static inline int tmp102_reg_to_mC(s16 val)
+{
+       return ((val & ~0x01) * 1000) / 128;
+}
+
+/* convert milliCelsius to left adjusted 13-bit TMP102 register value */
+static inline u16 tmp102_mC_to_reg(int val)
+{
+       return (val * 128) / 1000;
+}
+
+static const u8 tmp102_reg[] = {
+       TMP102_TEMP_REG,
+       TMP102_TLOW_REG,
+       TMP102_THIGH_REG,
+};
+
+static struct tmp102 *tmp102_update_device(struct i2c_client *client)
+{
+       struct tmp102 *tmp102 = i2c_get_clientdata(client);
+
+       mutex_lock(&tmp102->lock);
+       if (time_after(jiffies, tmp102->last_update + HZ / 3)) {
+               int i;
+               for (i = 0; i < ARRAY_SIZE(tmp102->temp); ++i) {
+                       int status = tmp102_read_reg(client, tmp102_reg[i]);
+                       if (status > -1)
+                               tmp102->temp[i] = tmp102_reg_to_mC(status);
+               }
+               tmp102->last_update = jiffies;
+       }
+       mutex_unlock(&tmp102->lock);
+       return tmp102;
+}
+
+static ssize_t tmp102_show_temp(struct device *dev,
+                               struct device_attribute *attr,
+                               char *buf)
+{
+       struct sensor_device_attribute *sda = to_sensor_dev_attr(attr);
+       struct tmp102 *tmp102 = tmp102_update_device(to_i2c_client(dev));
+
+       return sprintf(buf, "%d\n", tmp102->temp[sda->index]);
+}
+
+static ssize_t tmp102_set_temp(struct device *dev,
+                              struct device_attribute *attr,
+                              const char *buf, size_t count)
+{
+       struct sensor_device_attribute *sda = to_sensor_dev_attr(attr);
+       struct i2c_client *client = to_i2c_client(dev);
+       struct tmp102 *tmp102 = i2c_get_clientdata(client);
+       long val;
+       int status;
+
+       if (strict_strtol(buf, 10, &val) < 0)
+               return -EINVAL;
+       val = SENSORS_LIMIT(val, -256000, 255000);
+
+       mutex_lock(&tmp102->lock);
+       tmp102->temp[sda->index] = val;
+       status = tmp102_write_reg(client, tmp102_reg[sda->index],
+                                 tmp102_mC_to_reg(val));
+       mutex_unlock(&tmp102->lock);
+       return status ? : count;
+}
+
+static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, tmp102_show_temp, NULL , 0);
+
+static SENSOR_DEVICE_ATTR(temp1_max_hyst, S_IWUSR | S_IRUGO, tmp102_show_temp,
+                         tmp102_set_temp, 1);
+
+static SENSOR_DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO, tmp102_show_temp,
+                         tmp102_set_temp, 2);
+
+static struct attribute *tmp102_attributes[] = {
+       &sensor_dev_attr_temp1_input.dev_attr.attr,
+       &sensor_dev_attr_temp1_max_hyst.dev_attr.attr,
+       &sensor_dev_attr_temp1_max.dev_attr.attr,
+       NULL
+};
+
+static const struct attribute_group tmp102_attr_group = {
+       .attrs = tmp102_attributes,
+};
+
+#define TMP102_CONFIG  (TMP102_CONF_TM | TMP102_CONF_EM | TMP102_CONF_CR1)
+#define TMP102_CONFIG_RD_ONLY (TMP102_CONF_R0 | TMP102_CONF_R1 | TMP102_CONF_AL)
+
+static int __devinit tmp102_probe(struct i2c_client *client,
+                                 const struct i2c_device_id *id)
+{
+       struct tmp102 *tmp102;
+       int status;
+
+       if (!i2c_check_functionality(client->adapter,
+                                    I2C_FUNC_SMBUS_WORD_DATA)) {
+               dev_err(&client->dev, "adapter doesnt support SMBus word "
+                       "transactions\n");
+               return -ENODEV;
+       }
+
+       tmp102 = kzalloc(sizeof(*tmp102), GFP_KERNEL);
+       if (!tmp102) {
+               dev_dbg(&client->dev, "kzalloc failed\n");
+               return -ENOMEM;
+       }
+       i2c_set_clientdata(client, tmp102);
+
+       status = tmp102_read_reg(client, TMP102_CONF_REG);
+       if (status < 0) {
+               dev_err(&client->dev, "error reading config register\n");
+               goto fail_free;
+       }
+       tmp102->config_orig = status;
+       status = tmp102_write_reg(client, TMP102_CONF_REG, TMP102_CONFIG);
+       if (status < 0) {
+               dev_err(&client->dev, "error writing config register\n");
+               goto fail_restore_config;
+       }
+       status = tmp102_read_reg(client, TMP102_CONF_REG);
+       if (status < 0) {
+               dev_err(&client->dev, "error reading config register\n");
+               goto fail_restore_config;
+       }
+       status &= ~TMP102_CONFIG_RD_ONLY;
+       if (status != TMP102_CONFIG) {
+               dev_err(&client->dev, "config settings did not stick\n");
+               status = -ENODEV;
+               goto fail_restore_config;
+       }
+       tmp102->last_update = jiffies - HZ;
+       mutex_init(&tmp102->lock);
+
+       status = sysfs_create_group(&client->dev.kobj, &tmp102_attr_group);
+       if (status) {
+               dev_dbg(&client->dev, "could not create sysfs files\n");
+               goto fail_restore_config;
+       }
+       tmp102->hwmon_dev = hwmon_device_register(&client->dev);
+       if (IS_ERR(tmp102->hwmon_dev)) {
+               dev_dbg(&client->dev, "unable to register hwmon device\n");
+               status = PTR_ERR(tmp102->hwmon_dev);
+               goto fail_remove_sysfs;
+       }
+
+       dev_info(&client->dev, "initialized\n");
+
+       return 0;
+
+fail_remove_sysfs:
+       sysfs_remove_group(&client->dev.kobj, &tmp102_attr_group);
+fail_restore_config:
+       tmp102_write_reg(client, TMP102_CONF_REG, tmp102->config_orig);
+fail_free:
+       i2c_set_clientdata(client, NULL);
+       kfree(tmp102);
+
+       return status;
+}
+
+static int __devexit tmp102_remove(struct i2c_client *client)
+{
+       struct tmp102 *tmp102 = i2c_get_clientdata(client);
+
+       hwmon_device_unregister(tmp102->hwmon_dev);
+       sysfs_remove_group(&client->dev.kobj, &tmp102_attr_group);
+
+       /* Stop monitoring if device was stopped originally */
+       if (tmp102->config_orig & TMP102_CONF_SD) {
+               int config;
+
+               config = tmp102_read_reg(client, TMP102_CONF_REG);
+               if (config >= 0)
+                       tmp102_write_reg(client, TMP102_CONF_REG,
+                                        config | TMP102_CONF_SD);
+       }
+
+       i2c_set_clientdata(client, NULL);
+       kfree(tmp102);
+
+       return 0;
+}
+
+#ifdef CONFIG_PM
+static int tmp102_suspend(struct device *dev)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+       int config;
+
+       config = tmp102_read_reg(client, TMP102_CONF_REG);
+       if (config < 0)
+               return config;
+
+       config |= TMP102_CONF_SD;
+       return tmp102_write_reg(client, TMP102_CONF_REG, config);
+}
+
+static int tmp102_resume(struct device *dev)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+       int config;
+
+       config = tmp102_read_reg(client, TMP102_CONF_REG);
+       if (config < 0)
+               return config;
+
+       config &= ~TMP102_CONF_SD;
+       return tmp102_write_reg(client, TMP102_CONF_REG, config);
+}
+
+static const struct dev_pm_ops tmp102_dev_pm_ops = {
+       .suspend        = tmp102_suspend,
+       .resume         = tmp102_resume,
+};
+
+#define TMP102_DEV_PM_OPS (&tmp102_dev_pm_ops)
+#else
+#define        TMP102_DEV_PM_OPS NULL
+#endif /* CONFIG_PM */
+
+static const struct i2c_device_id tmp102_id[] = {
+       { "tmp102", 0 },
+       { }
+};
+MODULE_DEVICE_TABLE(i2c, tmp102_id);
+
+static struct i2c_driver tmp102_driver = {
+       .driver.name    = DRIVER_NAME,
+       .driver.pm      = TMP102_DEV_PM_OPS,
+       .probe          = tmp102_probe,
+       .remove         = __devexit_p(tmp102_remove),
+       .id_table       = tmp102_id,
+};
+
+static int __init tmp102_init(void)
+{
+       return i2c_add_driver(&tmp102_driver);
+}
+module_init(tmp102_init);
+
+static void __exit tmp102_exit(void)
+{
+       i2c_del_driver(&tmp102_driver);
+}
+module_exit(tmp102_exit);
+
+MODULE_AUTHOR("Steven King <sfking@fdwdc.com>");
+MODULE_DESCRIPTION("Texas Instruments TMP102 temperature sensor driver");
+MODULE_LICENSE("GPL");
index d14a1af..ad8d535 100644 (file)
@@ -92,17 +92,6 @@ static const u8 TMP411_TEMP_HIGHEST_LSB[2]           = { 0x33, 0x37 };
 #define TMP411_DEVICE_ID                       0x12
 
 /*
- * Functions declarations
- */
-
-static int tmp401_probe(struct i2c_client *client,
-                       const struct i2c_device_id *id);
-static int tmp401_detect(struct i2c_client *client,
-                        struct i2c_board_info *info);
-static int tmp401_remove(struct i2c_client *client);
-static struct tmp401_data *tmp401_update_device(struct device *dev);
-
-/*
  * Driver data (common to all clients)
  */
 
@@ -113,18 +102,6 @@ static const struct i2c_device_id tmp401_id[] = {
 };
 MODULE_DEVICE_TABLE(i2c, tmp401_id);
 
-static struct i2c_driver tmp401_driver = {
-       .class          = I2C_CLASS_HWMON,
-       .driver = {
-               .name   = "tmp401",
-       },
-       .probe          = tmp401_probe,
-       .remove         = tmp401_remove,
-       .id_table       = tmp401_id,
-       .detect         = tmp401_detect,
-       .address_list   = normal_i2c,
-};
-
 /*
  * Client data (each client gets its own)
  */
@@ -194,6 +171,71 @@ static u8 tmp401_crit_temp_to_register(long temp, u8 config)
        return (temp + 500) / 1000;
 }
 
+static struct tmp401_data *tmp401_update_device_reg16(
+       struct i2c_client *client, struct tmp401_data *data)
+{
+       int i;
+
+       for (i = 0; i < 2; i++) {
+               /*
+                * High byte must be read first immediately followed
+                * by the low byte
+                */
+               data->temp[i] = i2c_smbus_read_byte_data(client,
+                       TMP401_TEMP_MSB[i]) << 8;
+               data->temp[i] |= i2c_smbus_read_byte_data(client,
+                       TMP401_TEMP_LSB[i]);
+               data->temp_low[i] = i2c_smbus_read_byte_data(client,
+                       TMP401_TEMP_LOW_LIMIT_MSB_READ[i]) << 8;
+               data->temp_low[i] |= i2c_smbus_read_byte_data(client,
+                       TMP401_TEMP_LOW_LIMIT_LSB[i]);
+               data->temp_high[i] = i2c_smbus_read_byte_data(client,
+                       TMP401_TEMP_HIGH_LIMIT_MSB_READ[i]) << 8;
+               data->temp_high[i] |= i2c_smbus_read_byte_data(client,
+                       TMP401_TEMP_HIGH_LIMIT_LSB[i]);
+               data->temp_crit[i] = i2c_smbus_read_byte_data(client,
+                       TMP401_TEMP_CRIT_LIMIT[i]);
+
+               if (data->kind == tmp411) {
+                       data->temp_lowest[i] = i2c_smbus_read_byte_data(client,
+                               TMP411_TEMP_LOWEST_MSB[i]) << 8;
+                       data->temp_lowest[i] |= i2c_smbus_read_byte_data(
+                               client, TMP411_TEMP_LOWEST_LSB[i]);
+
+                       data->temp_highest[i] = i2c_smbus_read_byte_data(
+                               client, TMP411_TEMP_HIGHEST_MSB[i]) << 8;
+                       data->temp_highest[i] |= i2c_smbus_read_byte_data(
+                               client, TMP411_TEMP_HIGHEST_LSB[i]);
+               }
+       }
+       return data;
+}
+
+static struct tmp401_data *tmp401_update_device(struct device *dev)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+       struct tmp401_data *data = i2c_get_clientdata(client);
+
+       mutex_lock(&data->update_lock);
+
+       if (time_after(jiffies, data->last_updated + HZ) || !data->valid) {
+               data->status = i2c_smbus_read_byte_data(client, TMP401_STATUS);
+               data->config = i2c_smbus_read_byte_data(client,
+                                               TMP401_CONFIG_READ);
+               tmp401_update_device_reg16(client, data);
+
+               data->temp_crit_hyst = i2c_smbus_read_byte_data(client,
+                                               TMP401_TEMP_CRIT_HYST);
+
+               data->last_updated = jiffies;
+               data->valid = 1;
+       }
+
+       mutex_unlock(&data->update_lock);
+
+       return data;
+}
+
 static ssize_t show_temp_value(struct device *dev,
        struct device_attribute *devattr, char *buf)
 {
@@ -420,30 +462,36 @@ static ssize_t reset_temp_history(struct device *dev,
 }
 
 static struct sensor_device_attribute tmp401_attr[] = {
-       SENSOR_ATTR(temp1_input, 0444, show_temp_value, NULL, 0),
-       SENSOR_ATTR(temp1_min, 0644, show_temp_min, store_temp_min, 0),
-       SENSOR_ATTR(temp1_max, 0644, show_temp_max, store_temp_max, 0),
-       SENSOR_ATTR(temp1_crit, 0644, show_temp_crit, store_temp_crit, 0),
-       SENSOR_ATTR(temp1_crit_hyst, 0644, show_temp_crit_hyst,
+       SENSOR_ATTR(temp1_input, S_IRUGO, show_temp_value, NULL, 0),
+       SENSOR_ATTR(temp1_min, S_IWUSR | S_IRUGO, show_temp_min,
+                   store_temp_min, 0),
+       SENSOR_ATTR(temp1_max, S_IWUSR | S_IRUGO, show_temp_max,
+                   store_temp_max, 0),
+       SENSOR_ATTR(temp1_crit, S_IWUSR | S_IRUGO, show_temp_crit,
+                   store_temp_crit, 0),
+       SENSOR_ATTR(temp1_crit_hyst, S_IWUSR | S_IRUGO, show_temp_crit_hyst,
                    store_temp_crit_hyst, 0),
-       SENSOR_ATTR(temp1_min_alarm, 0444, show_status, NULL,
+       SENSOR_ATTR(temp1_min_alarm, S_IRUGO, show_status, NULL,
                    TMP401_STATUS_LOCAL_LOW),
-       SENSOR_ATTR(temp1_max_alarm, 0444, show_status, NULL,
+       SENSOR_ATTR(temp1_max_alarm, S_IRUGO, show_status, NULL,
                    TMP401_STATUS_LOCAL_HIGH),
-       SENSOR_ATTR(temp1_crit_alarm, 0444, show_status, NULL,
+       SENSOR_ATTR(temp1_crit_alarm, S_IRUGO, show_status, NULL,
                    TMP401_STATUS_LOCAL_CRIT),
-       SENSOR_ATTR(temp2_input, 0444, show_temp_value, NULL, 1),
-       SENSOR_ATTR(temp2_min, 0644, show_temp_min, store_temp_min, 1),
-       SENSOR_ATTR(temp2_max, 0644, show_temp_max, store_temp_max, 1),
-       SENSOR_ATTR(temp2_crit, 0644, show_temp_crit, store_temp_crit, 1),
-       SENSOR_ATTR(temp2_crit_hyst, 0444, show_temp_crit_hyst, NULL, 1),
-       SENSOR_ATTR(temp2_fault, 0444, show_status, NULL,
+       SENSOR_ATTR(temp2_input, S_IRUGO, show_temp_value, NULL, 1),
+       SENSOR_ATTR(temp2_min, S_IWUSR | S_IRUGO, show_temp_min,
+                   store_temp_min, 1),
+       SENSOR_ATTR(temp2_max, S_IWUSR | S_IRUGO, show_temp_max,
+                   store_temp_max, 1),
+       SENSOR_ATTR(temp2_crit, S_IWUSR | S_IRUGO, show_temp_crit,
+                   store_temp_crit, 1),
+       SENSOR_ATTR(temp2_crit_hyst, S_IRUGO, show_temp_crit_hyst, NULL, 1),
+       SENSOR_ATTR(temp2_fault, S_IRUGO, show_status, NULL,
                    TMP401_STATUS_REMOTE_OPEN),
-       SENSOR_ATTR(temp2_min_alarm, 0444, show_status, NULL,
+       SENSOR_ATTR(temp2_min_alarm, S_IRUGO, show_status, NULL,
                    TMP401_STATUS_REMOTE_LOW),
-       SENSOR_ATTR(temp2_max_alarm, 0444, show_status, NULL,
+       SENSOR_ATTR(temp2_max_alarm, S_IRUGO, show_status, NULL,
                    TMP401_STATUS_REMOTE_HIGH),
-       SENSOR_ATTR(temp2_crit_alarm, 0444, show_status, NULL,
+       SENSOR_ATTR(temp2_crit_alarm, S_IRUGO, show_status, NULL,
                    TMP401_STATUS_REMOTE_CRIT),
 };
 
@@ -455,11 +503,11 @@ static struct sensor_device_attribute tmp401_attr[] = {
  * and remote channels.
  */
 static struct sensor_device_attribute tmp411_attr[] = {
-       SENSOR_ATTR(temp1_highest, 0444, show_temp_highest, NULL, 0),
-       SENSOR_ATTR(temp1_lowest, 0444, show_temp_lowest, NULL, 0),
-       SENSOR_ATTR(temp2_highest, 0444, show_temp_highest, NULL, 1),
-       SENSOR_ATTR(temp2_lowest, 0444, show_temp_lowest, NULL, 1),
-       SENSOR_ATTR(temp_reset_history, 0200, NULL, reset_temp_history, 0),
+       SENSOR_ATTR(temp1_highest, S_IRUGO, show_temp_highest, NULL, 0),
+       SENSOR_ATTR(temp1_lowest, S_IRUGO, show_temp_lowest, NULL, 0),
+       SENSOR_ATTR(temp2_highest, S_IRUGO, show_temp_highest, NULL, 1),
+       SENSOR_ATTR(temp2_lowest, S_IRUGO, show_temp_lowest, NULL, 1),
+       SENSOR_ATTR(temp_reset_history, S_IWUSR, NULL, reset_temp_history, 0),
 };
 
 /*
@@ -529,6 +577,27 @@ static int tmp401_detect(struct i2c_client *client,
        return 0;
 }
 
+static int tmp401_remove(struct i2c_client *client)
+{
+       struct tmp401_data *data = i2c_get_clientdata(client);
+       int i;
+
+       if (data->hwmon_dev)
+               hwmon_device_unregister(data->hwmon_dev);
+
+       for (i = 0; i < ARRAY_SIZE(tmp401_attr); i++)
+               device_remove_file(&client->dev, &tmp401_attr[i].dev_attr);
+
+       if (data->kind == tmp411) {
+               for (i = 0; i < ARRAY_SIZE(tmp411_attr); i++)
+                       device_remove_file(&client->dev,
+                                          &tmp411_attr[i].dev_attr);
+       }
+
+       kfree(data);
+       return 0;
+}
+
 static int tmp401_probe(struct i2c_client *client,
                        const struct i2c_device_id *id)
 {
@@ -581,91 +650,17 @@ exit_remove:
        return err;
 }
 
-static int tmp401_remove(struct i2c_client *client)
-{
-       struct tmp401_data *data = i2c_get_clientdata(client);
-       int i;
-
-       if (data->hwmon_dev)
-               hwmon_device_unregister(data->hwmon_dev);
-
-       for (i = 0; i < ARRAY_SIZE(tmp401_attr); i++)
-               device_remove_file(&client->dev, &tmp401_attr[i].dev_attr);
-
-       if (data->kind == tmp411) {
-               for (i = 0; i < ARRAY_SIZE(tmp411_attr); i++)
-                       device_remove_file(&client->dev,
-                                          &tmp411_attr[i].dev_attr);
-       }
-
-       kfree(data);
-       return 0;
-}
-
-static struct tmp401_data *tmp401_update_device_reg16(
-       struct i2c_client *client, struct tmp401_data *data)
-{
-       int i;
-
-       for (i = 0; i < 2; i++) {
-               /*
-                * High byte must be read first immediately followed
-                * by the low byte
-                */
-               data->temp[i] = i2c_smbus_read_byte_data(client,
-                       TMP401_TEMP_MSB[i]) << 8;
-               data->temp[i] |= i2c_smbus_read_byte_data(client,
-                       TMP401_TEMP_LSB[i]);
-               data->temp_low[i] = i2c_smbus_read_byte_data(client,
-                       TMP401_TEMP_LOW_LIMIT_MSB_READ[i]) << 8;
-               data->temp_low[i] |= i2c_smbus_read_byte_data(client,
-                       TMP401_TEMP_LOW_LIMIT_LSB[i]);
-               data->temp_high[i] = i2c_smbus_read_byte_data(client,
-                       TMP401_TEMP_HIGH_LIMIT_MSB_READ[i]) << 8;
-               data->temp_high[i] |= i2c_smbus_read_byte_data(client,
-                       TMP401_TEMP_HIGH_LIMIT_LSB[i]);
-               data->temp_crit[i] = i2c_smbus_read_byte_data(client,
-                       TMP401_TEMP_CRIT_LIMIT[i]);
-
-               if (data->kind == tmp411) {
-                       data->temp_lowest[i] = i2c_smbus_read_byte_data(client,
-                               TMP411_TEMP_LOWEST_MSB[i]) << 8;
-                       data->temp_lowest[i] |= i2c_smbus_read_byte_data(
-                               client, TMP411_TEMP_LOWEST_LSB[i]);
-
-                       data->temp_highest[i] = i2c_smbus_read_byte_data(
-                               client, TMP411_TEMP_HIGHEST_MSB[i]) << 8;
-                       data->temp_highest[i] |= i2c_smbus_read_byte_data(
-                               client, TMP411_TEMP_HIGHEST_LSB[i]);
-               }
-       }
-       return data;
-}
-
-static struct tmp401_data *tmp401_update_device(struct device *dev)
-{
-       struct i2c_client *client = to_i2c_client(dev);
-       struct tmp401_data *data = i2c_get_clientdata(client);
-
-       mutex_lock(&data->update_lock);
-
-       if (time_after(jiffies, data->last_updated + HZ) || !data->valid) {
-               data->status = i2c_smbus_read_byte_data(client, TMP401_STATUS);
-               data->config = i2c_smbus_read_byte_data(client,
-                                               TMP401_CONFIG_READ);
-               tmp401_update_device_reg16(client, data);
-
-               data->temp_crit_hyst = i2c_smbus_read_byte_data(client,
-                                               TMP401_TEMP_CRIT_HYST);
-
-               data->last_updated = jiffies;
-               data->valid = 1;
-       }
-
-       mutex_unlock(&data->update_lock);
-
-       return data;
-}
+static struct i2c_driver tmp401_driver = {
+       .class          = I2C_CLASS_HWMON,
+       .driver = {
+               .name   = "tmp401",
+       },
+       .probe          = tmp401_probe,
+       .remove         = tmp401_remove,
+       .id_table       = tmp401_id,
+       .detect         = tmp401_detect,
+       .address_list   = normal_i2c,
+};
 
 static int __init tmp401_init(void)
 {
index f15e90a..fb5c518 100644 (file)
@@ -1,3 +1,14 @@
+config INTEL_IDLE
+       tristate "Cpuidle Driver for Intel Processors"
+       depends on CPU_IDLE
+       depends on X86
+       depends on CPU_SUP_INTEL
+       depends on EXPERIMENTAL
+       help
+         Enable intel_idle, a cpuidle driver that includes knowledge of
+         native Intel hardware idle features.  The acpi_idle driver
+         can be configured at the same time, in order to handle
+         processors intel_idle does not support.
 
 menu "Memory power savings"
 depends on X86_64
index 5f68fc3..23d295c 100644 (file)
@@ -1,2 +1,3 @@
 obj-$(CONFIG_I7300_IDLE)                       += i7300_idle.o
+obj-$(CONFIG_INTEL_IDLE)                       += intel_idle.o
 
diff --git a/drivers/idle/intel_idle.c b/drivers/idle/intel_idle.c
new file mode 100755 (executable)
index 0000000..54f0fb4
--- /dev/null
@@ -0,0 +1,461 @@
+/*
+ * intel_idle.c - native hardware idle loop for modern Intel processors
+ *
+ * Copyright (c) 2010, Intel Corporation.
+ * Len Brown <len.brown@intel.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+/*
+ * intel_idle is a cpuidle driver that loads on specific Intel processors
+ * in lieu of the legacy ACPI processor_idle driver.  The intent is to
+ * make Linux more efficient on these processors, as intel_idle knows
+ * more than ACPI, as well as make Linux more immune to ACPI BIOS bugs.
+ */
+
+/*
+ * Design Assumptions
+ *
+ * All CPUs have same idle states as boot CPU
+ *
+ * Chipset BM_STS (bus master status) bit is a NOP
+ *     for preventing entry into deep C-stats
+ */
+
+/*
+ * Known limitations
+ *
+ * The driver currently initializes for_each_online_cpu() upon modprobe.
+ * It it unaware of subsequent processors hot-added to the system.
+ * This means that if you boot with maxcpus=n and later online
+ * processors above n, those processors will use C1 only.
+ *
+ * ACPI has a .suspend hack to turn off deep c-statees during suspend
+ * to avoid complications with the lapic timer workaround.
+ * Have not seen issues with suspend, but may need same workaround here.
+ *
+ * There is currently no kernel-based automatic probing/loading mechanism
+ * if the driver is built as a module.
+ */
+
+/* un-comment DEBUG to enable pr_debug() statements */
+#define DEBUG
+
+#include <linux/kernel.h>
+#include <linux/cpuidle.h>
+#include <linux/clockchips.h>
+#include <linux/hrtimer.h>     /* ktime_get_real() */
+#include <trace/events/power.h>
+#include <linux/sched.h>
+
+#define INTEL_IDLE_VERSION "0.4"
+#define PREFIX "intel_idle: "
+
+#define MWAIT_SUBSTATE_MASK    (0xf)
+#define MWAIT_CSTATE_MASK      (0xf)
+#define MWAIT_SUBSTATE_SIZE    (4)
+#define MWAIT_MAX_NUM_CSTATES  8
+#define CPUID_MWAIT_LEAF (5)
+#define CPUID5_ECX_EXTENSIONS_SUPPORTED (0x1)
+#define CPUID5_ECX_INTERRUPT_BREAK     (0x2)
+
+static struct cpuidle_driver intel_idle_driver = {
+       .name = "intel_idle",
+       .owner = THIS_MODULE,
+};
+/* intel_idle.max_cstate=0 disables driver */
+static int max_cstate = MWAIT_MAX_NUM_CSTATES - 1;
+static int power_policy = 7; /* 0 = max perf; 15 = max powersave */
+
+static unsigned int substates;
+static int (*choose_substate)(int);
+
+/* Reliable LAPIC Timer States, bit 1 for C1 etc.  */
+static unsigned int lapic_timer_reliable_states;
+
+static struct cpuidle_device *intel_idle_cpuidle_devices;
+static int intel_idle(struct cpuidle_device *dev, struct cpuidle_state *state);
+
+static struct cpuidle_state *cpuidle_state_table;
+
+/*
+ * States are indexed by the cstate number,
+ * which is also the index into the MWAIT hint array.
+ * Thus C0 is a dummy.
+ */
+static struct cpuidle_state nehalem_cstates[MWAIT_MAX_NUM_CSTATES] = {
+       { /* MWAIT C0 */ },
+       { /* MWAIT C1 */
+               .name = "NHM-C1",
+               .desc = "MWAIT 0x00",
+               .driver_data = (void *) 0x00,
+               .flags = CPUIDLE_FLAG_TIME_VALID,
+               .exit_latency = 3,
+               .power_usage = 1000,
+               .target_residency = 6,
+               .enter = &intel_idle },
+       { /* MWAIT C2 */
+               .name = "NHM-C3",
+               .desc = "MWAIT 0x10",
+               .driver_data = (void *) 0x10,
+               .flags = CPUIDLE_FLAG_TIME_VALID,
+               .exit_latency = 20,
+               .power_usage = 500,
+               .target_residency = 80,
+               .enter = &intel_idle },
+       { /* MWAIT C3 */
+               .name = "NHM-C6",
+               .desc = "MWAIT 0x20",
+               .driver_data = (void *) 0x20,
+               .flags = CPUIDLE_FLAG_TIME_VALID,
+               .exit_latency = 200,
+               .power_usage = 350,
+               .target_residency = 800,
+               .enter = &intel_idle },
+};
+
+static struct cpuidle_state atom_cstates[MWAIT_MAX_NUM_CSTATES] = {
+       { /* MWAIT C0 */ },
+       { /* MWAIT C1 */
+               .name = "ATM-C1",
+               .desc = "MWAIT 0x00",
+               .driver_data = (void *) 0x00,
+               .flags = CPUIDLE_FLAG_TIME_VALID,
+               .exit_latency = 1,
+               .power_usage = 1000,
+               .target_residency = 4,
+               .enter = &intel_idle },
+       { /* MWAIT C2 */
+               .name = "ATM-C2",
+               .desc = "MWAIT 0x10",
+               .driver_data = (void *) 0x10,
+               .flags = CPUIDLE_FLAG_TIME_VALID,
+               .exit_latency = 20,
+               .power_usage = 500,
+               .target_residency = 80,
+               .enter = &intel_idle },
+       { /* MWAIT C3 */ },
+       { /* MWAIT C4 */
+               .name = "ATM-C4",
+               .desc = "MWAIT 0x30",
+               .driver_data = (void *) 0x30,
+               .flags = CPUIDLE_FLAG_TIME_VALID,
+               .exit_latency = 100,
+               .power_usage = 250,
+               .target_residency = 400,
+               .enter = &intel_idle },
+       { /* MWAIT C5 */ },
+       { /* MWAIT C6 */
+               .name = "ATM-C6",
+               .desc = "MWAIT 0x40",
+               .driver_data = (void *) 0x40,
+               .flags = CPUIDLE_FLAG_TIME_VALID,
+               .exit_latency = 200,
+               .power_usage = 150,
+               .target_residency = 800,
+               .enter = NULL },        /* disabled */
+};
+
+/*
+ * choose_tunable_substate()
+ *
+ * Run-time decision on which C-state substate to invoke
+ * If power_policy = 0, choose shallowest substate (0)
+ * If power_policy = 15, choose deepest substate
+ * If power_policy = middle, choose middle substate etc.
+ */
+static int choose_tunable_substate(int cstate)
+{
+       unsigned int num_substates;
+       unsigned int substate_choice;
+
+       power_policy &= 0xF;    /* valid range: 0-15 */
+       cstate &= 7;    /* valid range: 0-7 */
+
+       num_substates = (substates >> ((cstate) * 4)) & MWAIT_SUBSTATE_MASK;
+
+       if (num_substates <= 1)
+               return 0;
+
+       substate_choice = ((power_policy + (power_policy + 1) *
+                               (num_substates - 1)) / 16);
+
+       return substate_choice;
+}
+
+/*
+ * choose_zero_substate()
+ */
+static int choose_zero_substate(int cstate)
+{
+       return 0;
+}
+
+/**
+ * intel_idle
+ * @dev: cpuidle_device
+ * @state: cpuidle state
+ *
+ */
+static int intel_idle(struct cpuidle_device *dev, struct cpuidle_state *state)
+{
+       unsigned long ecx = 1; /* break on interrupt flag */
+       unsigned long eax = (unsigned long)cpuidle_get_statedata(state);
+       unsigned int cstate;
+       ktime_t kt_before, kt_after;
+       s64 usec_delta;
+       int cpu = smp_processor_id();
+
+       cstate = (((eax) >> MWAIT_SUBSTATE_SIZE) & MWAIT_CSTATE_MASK) + 1;
+
+       eax = eax + (choose_substate)(cstate);
+
+       local_irq_disable();
+
+       if (!(lapic_timer_reliable_states & (1 << (cstate))))
+               clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_ENTER, &cpu);
+
+       kt_before = ktime_get_real();
+
+       stop_critical_timings();
+#ifndef MODULE
+       trace_power_start(POWER_CSTATE, (eax >> 4) + 1);
+#endif
+       if (!need_resched()) {
+
+               __monitor((void *)&current_thread_info()->flags, 0, 0);
+               smp_mb();
+               if (!need_resched())
+                       __mwait(eax, ecx);
+       }
+
+       start_critical_timings();
+
+       kt_after = ktime_get_real();
+       usec_delta = ktime_to_us(ktime_sub(kt_after, kt_before));
+
+       local_irq_enable();
+
+       if (!(lapic_timer_reliable_states & (1 << (cstate))))
+               clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_EXIT, &cpu);
+
+       return usec_delta;
+}
+
+/*
+ * intel_idle_probe()
+ */
+static int intel_idle_probe(void)
+{
+       unsigned int eax, ebx, ecx, edx;
+
+       if (max_cstate == 0) {
+               pr_debug(PREFIX "disabled\n");
+               return -EPERM;
+       }
+
+       if (boot_cpu_data.x86_vendor != X86_VENDOR_INTEL)
+               return -ENODEV;
+
+       if (!boot_cpu_has(X86_FEATURE_MWAIT))
+               return -ENODEV;
+
+       if (boot_cpu_data.cpuid_level < CPUID_MWAIT_LEAF)
+               return -ENODEV;
+
+       cpuid(CPUID_MWAIT_LEAF, &eax, &ebx, &ecx, &edx);
+
+       if (!(ecx & CPUID5_ECX_EXTENSIONS_SUPPORTED) ||
+               !(ecx & CPUID5_ECX_INTERRUPT_BREAK))
+                       return -ENODEV;
+#ifdef DEBUG
+       if (substates == 0)     /* can over-ride via modparam */
+#endif
+               substates = edx;
+
+       pr_debug(PREFIX "MWAIT substates: 0x%x\n", substates);
+
+       if (boot_cpu_has(X86_FEATURE_ARAT))     /* Always Reliable APIC Timer */
+               lapic_timer_reliable_states = 0xFFFFFFFF;
+
+       if (boot_cpu_data.x86 != 6)     /* family 6 */
+               return -ENODEV;
+
+       switch (boot_cpu_data.x86_model) {
+
+       case 0x1A:      /* Core i7, Xeon 5500 series */
+       case 0x1E:      /* Core i7 and i5 Processor - Lynnfield Jasper Forest */
+       case 0x1F:      /* Core i7 and i5 Processor - Nehalem */
+       case 0x2E:      /* Nehalem-EX Xeon */
+               lapic_timer_reliable_states = (1 << 1);  /* C1 */
+
+       case 0x25:      /* Westmere */
+       case 0x2C:      /* Westmere */
+               cpuidle_state_table = nehalem_cstates;
+               choose_substate = choose_tunable_substate;
+               break;
+
+       case 0x1C:      /* 28 - Atom Processor */
+               lapic_timer_reliable_states = (1 << 2) | (1 << 1); /* C2, C1 */
+               cpuidle_state_table = atom_cstates;
+               choose_substate = choose_zero_substate;
+               break;
+#ifdef FUTURE_USE
+       case 0x17:      /* 23 - Core 2 Duo */
+               lapic_timer_reliable_states = (1 << 2) | (1 << 1); /* C2, C1 */
+#endif
+
+       default:
+               pr_debug(PREFIX "does not run on family %d model %d\n",
+                       boot_cpu_data.x86, boot_cpu_data.x86_model);
+               return -ENODEV;
+       }
+
+       pr_debug(PREFIX "v" INTEL_IDLE_VERSION
+               " model 0x%X\n", boot_cpu_data.x86_model);
+
+       pr_debug(PREFIX "lapic_timer_reliable_states 0x%x\n",
+               lapic_timer_reliable_states);
+       return 0;
+}
+
+/*
+ * intel_idle_cpuidle_devices_uninit()
+ * unregister, free cpuidle_devices
+ */
+static void intel_idle_cpuidle_devices_uninit(void)
+{
+       int i;
+       struct cpuidle_device *dev;
+
+       for_each_online_cpu(i) {
+               dev = per_cpu_ptr(intel_idle_cpuidle_devices, i);
+               cpuidle_unregister_device(dev);
+       }
+
+       free_percpu(intel_idle_cpuidle_devices);
+       return;
+}
+/*
+ * intel_idle_cpuidle_devices_init()
+ * allocate, initialize, register cpuidle_devices
+ */
+static int intel_idle_cpuidle_devices_init(void)
+{
+       int i, cstate;
+       struct cpuidle_device *dev;
+
+       intel_idle_cpuidle_devices = alloc_percpu(struct cpuidle_device);
+       if (intel_idle_cpuidle_devices == NULL)
+               return -ENOMEM;
+
+       for_each_online_cpu(i) {
+               dev = per_cpu_ptr(intel_idle_cpuidle_devices, i);
+
+               dev->state_count = 1;
+
+               for (cstate = 1; cstate < MWAIT_MAX_NUM_CSTATES; ++cstate) {
+                       int num_substates;
+
+                       if (cstate > max_cstate) {
+                               printk(PREFIX "max_cstate %d reached\n",
+                                       max_cstate);
+                               break;
+                       }
+
+                       /* does the state exist in CPUID.MWAIT? */
+                       num_substates = (substates >> ((cstate) * 4))
+                                               & MWAIT_SUBSTATE_MASK;
+                       if (num_substates == 0)
+                               continue;
+                       /* is the state not enabled? */
+                       if (cpuidle_state_table[cstate].enter == NULL) {
+                               /* does the driver not know about the state? */
+                               if (*cpuidle_state_table[cstate].name == '\0')
+                                       pr_debug(PREFIX "unaware of model 0x%x"
+                                               " MWAIT %d please"
+                                               " contact lenb@kernel.org",
+                                       boot_cpu_data.x86_model, cstate);
+                               continue;
+                       }
+
+                       if ((cstate > 2) &&
+                               !boot_cpu_has(X86_FEATURE_NONSTOP_TSC))
+                               mark_tsc_unstable("TSC halts in idle"
+                                       " states deeper than C2");
+
+                       dev->states[dev->state_count] = /* structure copy */
+                               cpuidle_state_table[cstate];
+
+                       dev->state_count += 1;
+               }
+
+               dev->cpu = i;
+               if (cpuidle_register_device(dev)) {
+                       pr_debug(PREFIX "cpuidle_register_device %d failed!\n",
+                                i);
+                       intel_idle_cpuidle_devices_uninit();
+                       return -EIO;
+               }
+       }
+
+       return 0;
+}
+
+
+static int __init intel_idle_init(void)
+{
+       int retval;
+
+       retval = intel_idle_probe();
+       if (retval)
+               return retval;
+
+       retval = cpuidle_register_driver(&intel_idle_driver);
+       if (retval) {
+               printk(KERN_DEBUG PREFIX "intel_idle yielding to %s",
+                       cpuidle_get_driver()->name);
+               return retval;
+       }
+
+       retval = intel_idle_cpuidle_devices_init();
+       if (retval) {
+               cpuidle_unregister_driver(&intel_idle_driver);
+               return retval;
+       }
+
+       return 0;
+}
+
+static void __exit intel_idle_exit(void)
+{
+       intel_idle_cpuidle_devices_uninit();
+       cpuidle_unregister_driver(&intel_idle_driver);
+
+       return;
+}
+
+module_init(intel_idle_init);
+module_exit(intel_idle_exit);
+
+module_param(power_policy, int, 0644);
+module_param(max_cstate, int, 0444);
+#ifdef DEBUG
+module_param(substates, int, 0444);
+#endif
+
+MODULE_AUTHOR("Len Brown <len.brown@intel.com>");
+MODULE_DESCRIPTION("Cpuidle driver for Intel Hardware v" INTEL_IDLE_VERSION);
+MODULE_LICENSE("GPL");
index 4647484..08f948d 100644 (file)
@@ -706,14 +706,9 @@ static int ib_ucm_alloc_data(const void **dest, u64 src, u32 len)
        if (!len)
                return 0;
 
-       data = kmalloc(len, GFP_KERNEL);
-       if (!data)
-               return -ENOMEM;
-
-       if (copy_from_user(data, (void __user *)(unsigned long)src, len)) {
-               kfree(data);
-               return -EFAULT;
-       }
+       data = memdup_user((void __user *)(unsigned long)src, len);
+       if (IS_ERR(data))
+               return PTR_ERR(data);
 
        *dest = data;
        return 0;
index 7554704..edef852 100644 (file)
@@ -144,10 +144,11 @@ static ssize_t dev_counters_read(struct file *file, char __user *buf,
                                 size_t count, loff_t *ppos)
 {
        u64 *counters;
+       size_t avail;
        struct qib_devdata *dd = private2dd(file);
 
-       return simple_read_from_buffer(buf, count, ppos, counters,
-               dd->f_read_cntrs(dd, *ppos, NULL, &counters));
+       avail = dd->f_read_cntrs(dd, *ppos, NULL, &counters);
+       return simple_read_from_buffer(buf, count, ppos, counters, avail);
 }
 
 /* read the per-device counters */
@@ -155,10 +156,11 @@ static ssize_t dev_names_read(struct file *file, char __user *buf,
                              size_t count, loff_t *ppos)
 {
        char *names;
+       size_t avail;
        struct qib_devdata *dd = private2dd(file);
 
-       return simple_read_from_buffer(buf, count, ppos, names,
-               dd->f_read_cntrs(dd, *ppos, &names, NULL));
+       avail = dd->f_read_cntrs(dd, *ppos, &names, NULL);
+       return simple_read_from_buffer(buf, count, ppos, names, avail);
 }
 
 static const struct file_operations cntr_ops[] = {
@@ -176,10 +178,11 @@ static ssize_t portnames_read(struct file *file, char __user *buf,
                              size_t count, loff_t *ppos)
 {
        char *names;
+       size_t avail;
        struct qib_devdata *dd = private2dd(file);
 
-       return simple_read_from_buffer(buf, count, ppos, names,
-               dd->f_read_portcntrs(dd, *ppos, 0, &names, NULL));
+       avail = dd->f_read_portcntrs(dd, *ppos, 0, &names, NULL);
+       return simple_read_from_buffer(buf, count, ppos, names, avail);
 }
 
 /* read the per-port counters for port 1 (pidx 0) */
@@ -187,10 +190,11 @@ static ssize_t portcntrs_1_read(struct file *file, char __user *buf,
                                size_t count, loff_t *ppos)
 {
        u64 *counters;
+       size_t avail;
        struct qib_devdata *dd = private2dd(file);
 
-       return simple_read_from_buffer(buf, count, ppos, counters,
-               dd->f_read_portcntrs(dd, *ppos, 0, NULL, &counters));
+       avail = dd->f_read_portcntrs(dd, *ppos, 0, NULL, &counters);
+       return simple_read_from_buffer(buf, count, ppos, counters, avail);
 }
 
 /* read the per-port counters for port 2 (pidx 1) */
@@ -198,10 +202,11 @@ static ssize_t portcntrs_2_read(struct file *file, char __user *buf,
                                size_t count, loff_t *ppos)
 {
        u64 *counters;
+       size_t avail;
        struct qib_devdata *dd = private2dd(file);
 
-       return simple_read_from_buffer(buf, count, ppos, counters,
-               dd->f_read_portcntrs(dd, *ppos, 1, NULL, &counters));
+       avail = dd->f_read_portcntrs(dd, *ppos, 1, NULL, &counters);
+       return simple_read_from_buffer(buf, count, ppos, counters, avail);
 }
 
 static const struct file_operations portcntr_ops[] = {
index 7b6549f..1eadadc 100644 (file)
@@ -3475,14 +3475,6 @@ struct qib_devdata *qib_init_iba6120_funcs(struct pci_dev *pdev,
        struct qib_devdata *dd;
        int ret;
 
-#ifndef CONFIG_PCI_MSI
-       qib_early_err(&pdev->dev, "QLogic PCIE device 0x%x cannot "
-             "work if CONFIG_PCI_MSI is not enabled\n",
-             ent->device);
-       dd = ERR_PTR(-ENODEV);
-       goto bail;
-#endif
-
        dd = qib_alloc_devdata(pdev, sizeof(struct qib_pportdata) +
                               sizeof(struct qib_chip_specific));
        if (IS_ERR(dd))
@@ -3554,10 +3546,6 @@ struct qib_devdata *qib_init_iba6120_funcs(struct pci_dev *pdev,
        if (qib_mini_init)
                goto bail;
 
-#ifndef CONFIG_PCI_MSI
-       qib_dev_err(dd, "PCI_MSI not configured, NO interrupts\n");
-#endif
-
        if (qib_pcie_params(dd, 8, NULL, NULL))
                qib_dev_err(dd, "Failed to setup PCIe or interrupts; "
                            "continuing anyway\n");
index 2c24eab..503992d 100644 (file)
@@ -42,9 +42,6 @@
 #include <linux/jiffies.h>
 #include <rdma/ib_verbs.h>
 #include <rdma/ib_smi.h>
-#if defined(CONFIG_DCA) || defined(CONFIG_DCA_MODULE)
-#include <linux/dca.h>
-#endif
 
 #include "qib.h"
 #include "qib_7322_regs.h"
@@ -114,40 +111,18 @@ static ushort qib_singleport;
 module_param_named(singleport, qib_singleport, ushort, S_IRUGO);
 MODULE_PARM_DESC(singleport, "Use only IB port 1; more per-port buffer space");
 
-
-/*
- * Setup QMH7342 receive and transmit parameters, necessary because
- * each bay, Mez connector, and IB port need different tuning, beyond
- * what the switch and HCA can do automatically.
- * It's expected to be done by cat'ing files to the modules file,
- * rather than setting up as a module parameter.
- * It's a "write-only" file, returns 0 when read back.
- * The unit, port, bay (if given), and values MUST be done as a single write.
- * The unit, port, and bay must precede the values to be effective.
- */
-static int setup_qmh_params(const char *, struct kernel_param *);
-static unsigned dummy_qmh_params;
-module_param_call(qmh_serdes_setup, setup_qmh_params, param_get_uint,
-                 &dummy_qmh_params, S_IWUSR | S_IRUGO);
-
-/* similarly for QME7342, but it's simpler */
-static int setup_qme_params(const char *, struct kernel_param *);
-static unsigned dummy_qme_params;
-module_param_call(qme_serdes_setup, setup_qme_params, param_get_uint,
-                 &dummy_qme_params, S_IWUSR | S_IRUGO);
-
 #define MAX_ATTEN_LEN 64 /* plenty for any real system */
 /* for read back, default index is ~5m copper cable */
-static char cable_atten_list[MAX_ATTEN_LEN] = "10";
-static struct kparam_string kp_cable_atten = {
-       .string = cable_atten_list,
+static char txselect_list[MAX_ATTEN_LEN] = "10";
+static struct kparam_string kp_txselect = {
+       .string = txselect_list,
        .maxlen = MAX_ATTEN_LEN
 };
-static int  setup_cable_atten(const char *, struct kernel_param *);
-module_param_call(cable_atten, setup_cable_atten, param_get_string,
-                 &kp_cable_atten, S_IWUSR | S_IRUGO);
-MODULE_PARM_DESC(cable_atten, \
-                "cable attenuation indices for cables with invalid EEPROM");
+static int  setup_txselect(const char *, struct kernel_param *);
+module_param_call(txselect, setup_txselect, param_get_string,
+                 &kp_txselect, S_IWUSR | S_IRUGO);
+MODULE_PARM_DESC(txselect, \
+                "Tx serdes indices (for no QSFP or invalid QSFP data)");
 
 #define BOARD_QME7342 5
 #define BOARD_QMH7342 6
@@ -540,12 +515,6 @@ struct qib_chip_specific {
        u32 lastbuf_for_pio;
        u32 stay_in_freeze;
        u32 recovery_ports_initted;
-#if defined(CONFIG_DCA) || defined(CONFIG_DCA_MODULE)
-       u32 dca_ctrl;
-       int rhdr_cpu[18];
-       int sdma_cpu[2];
-       u64 dca_rcvhdr_ctrl[5]; /* B, C, D, E, F */
-#endif
        struct msix_entry *msix_entries;
        void  **msix_arg;
        unsigned long *sendchkenable;
@@ -574,11 +543,12 @@ struct vendor_txdds_ent {
 static void write_tx_serdes_param(struct qib_pportdata *, struct txdds_ent *);
 
 #define TXDDS_TABLE_SZ 16 /* number of entries per speed in onchip table */
+#define TXDDS_EXTRA_SZ 11 /* number of extra tx settings entries */
 #define SERDES_CHANS 4 /* yes, it's obvious, but one less magic number */
 
 #define H1_FORCE_VAL 8
-#define H1_FORCE_QME 1 /*  may be overridden via setup_qme_params() */
-#define H1_FORCE_QMH 7 /*  may be overridden via setup_qmh_params() */
+#define H1_FORCE_QME 1 /*  may be overridden via setup_txselect() */
+#define H1_FORCE_QMH 7 /*  may be overridden via setup_txselect() */
 
 /* The static and dynamic registers are paired, and the pairs indexed by spd */
 #define krp_static_adapt_dis(spd) (KREG_IBPORT_IDX(ADAPT_DISABLE_STATIC_SDR) \
@@ -590,15 +560,6 @@ static void write_tx_serdes_param(struct qib_pportdata *, struct txdds_ent *);
 #define QDR_STATIC_ADAPT_INIT 0xffffffffffULL /* up, disable H0,H1-8, LE */
 #define QDR_STATIC_ADAPT_INIT_R1 0xf0ffffffffULL /* r1 up, disable H0,H1-8 */
 
-static const struct txdds_ent qmh_sdr_txdds =  { 11, 0,  5,  6 };
-static const struct txdds_ent qmh_ddr_txdds =  {  7, 0,  2,  8 };
-static const struct txdds_ent qmh_qdr_txdds =  {  0, 1,  3, 10 };
-
-/* this is used for unknown mez cards also */
-static const struct txdds_ent qme_sdr_txdds =  { 11, 0,  4,  4 };
-static const struct txdds_ent qme_ddr_txdds =  {  7, 0,  2,  7 };
-static const struct txdds_ent qme_qdr_txdds =  {  0, 1, 12, 11 };
-
 struct qib_chippport_specific {
        u64 __iomem *kpregbase;
        u64 __iomem *cpregbase;
@@ -637,12 +598,8 @@ struct qib_chippport_specific {
         * Per-bay per-channel rcv QMH H1 values and Tx values for QDR.
         * entry zero is unused, to simplify indexing
         */
-       u16 h1_val;
-       u8 amp[SERDES_CHANS];
-       u8 pre[SERDES_CHANS];
-       u8 mainv[SERDES_CHANS];
-       u8 post[SERDES_CHANS];
-       u8 no_eep;  /* attenuation index to use if no qsfp info */
+       u8 h1_val;
+       u8 no_eep;  /* txselect table index to use if no qsfp info */
        u8 ipg_tries;
        u8 ibmalfusesnap;
        struct qib_qsfp_data qsfp_data;
@@ -676,52 +633,6 @@ static struct {
                SYM_LSB(IntStatus, SDmaCleanupDone_1), 2 },
 };
 
-#if defined(CONFIG_DCA) || defined(CONFIG_DCA_MODULE)
-static const struct dca_reg_map {
-       int     shadow_inx;
-       int     lsb;
-       u64     mask;
-       u16     regno;
-} dca_rcvhdr_reg_map[] = {
-       { 0, SYM_LSB(DCACtrlB, RcvHdrq0DCAOPH),
-          ~SYM_MASK(DCACtrlB, RcvHdrq0DCAOPH) , KREG_IDX(DCACtrlB) },
-       { 0, SYM_LSB(DCACtrlB, RcvHdrq1DCAOPH),
-          ~SYM_MASK(DCACtrlB, RcvHdrq1DCAOPH) , KREG_IDX(DCACtrlB) },
-       { 0, SYM_LSB(DCACtrlB, RcvHdrq2DCAOPH),
-          ~SYM_MASK(DCACtrlB, RcvHdrq2DCAOPH) , KREG_IDX(DCACtrlB) },
-       { 0, SYM_LSB(DCACtrlB, RcvHdrq3DCAOPH),
-          ~SYM_MASK(DCACtrlB, RcvHdrq3DCAOPH) , KREG_IDX(DCACtrlB) },
-       { 1, SYM_LSB(DCACtrlC, RcvHdrq4DCAOPH),
-          ~SYM_MASK(DCACtrlC, RcvHdrq4DCAOPH) , KREG_IDX(DCACtrlC) },
-       { 1, SYM_LSB(DCACtrlC, RcvHdrq5DCAOPH),
-          ~SYM_MASK(DCACtrlC, RcvHdrq5DCAOPH) , KREG_IDX(DCACtrlC) },
-       { 1, SYM_LSB(DCACtrlC, RcvHdrq6DCAOPH),
-          ~SYM_MASK(DCACtrlC, RcvHdrq6DCAOPH) , KREG_IDX(DCACtrlC) },
-       { 1, SYM_LSB(DCACtrlC, RcvHdrq7DCAOPH),
-          ~SYM_MASK(DCACtrlC, RcvHdrq7DCAOPH) , KREG_IDX(DCACtrlC) },
-       { 2, SYM_LSB(DCACtrlD, RcvHdrq8DCAOPH),
-          ~SYM_MASK(DCACtrlD, RcvHdrq8DCAOPH) , KREG_IDX(DCACtrlD) },
-       { 2, SYM_LSB(DCACtrlD, RcvHdrq9DCAOPH),
-          ~SYM_MASK(DCACtrlD, RcvHdrq9DCAOPH) , KREG_IDX(DCACtrlD) },
-       { 2, SYM_LSB(DCACtrlD, RcvHdrq10DCAOPH),
-          ~SYM_MASK(DCACtrlD, RcvHdrq10DCAOPH) , KREG_IDX(DCACtrlD) },
-       { 2, SYM_LSB(DCACtrlD, RcvHdrq11DCAOPH),
-          ~SYM_MASK(DCACtrlD, RcvHdrq11DCAOPH) , KREG_IDX(DCACtrlD) },
-       { 3, SYM_LSB(DCACtrlE, RcvHdrq12DCAOPH),
-          ~SYM_MASK(DCACtrlE, RcvHdrq12DCAOPH) , KREG_IDX(DCACtrlE) },
-       { 3, SYM_LSB(DCACtrlE, RcvHdrq13DCAOPH),
-          ~SYM_MASK(DCACtrlE, RcvHdrq13DCAOPH) , KREG_IDX(DCACtrlE) },
-       { 3, SYM_LSB(DCACtrlE, RcvHdrq14DCAOPH),
-          ~SYM_MASK(DCACtrlE, RcvHdrq14DCAOPH) , KREG_IDX(DCACtrlE) },
-       { 3, SYM_LSB(DCACtrlE, RcvHdrq15DCAOPH),
-          ~SYM_MASK(DCACtrlE, RcvHdrq15DCAOPH) , KREG_IDX(DCACtrlE) },
-       { 4, SYM_LSB(DCACtrlF, RcvHdrq16DCAOPH),
-          ~SYM_MASK(DCACtrlF, RcvHdrq16DCAOPH) , KREG_IDX(DCACtrlF) },
-       { 4, SYM_LSB(DCACtrlF, RcvHdrq17DCAOPH),
-          ~SYM_MASK(DCACtrlF, RcvHdrq17DCAOPH) , KREG_IDX(DCACtrlF) },
-};
-#endif
-
 /* ibcctrl bits */
 #define QLOGIC_IB_IBCC_LINKINITCMD_DISABLE 1
 /* cycle through TS1/TS2 till OK */
@@ -2572,95 +2483,6 @@ static void qib_setup_7322_setextled(struct qib_pportdata *ppd, u32 on)
                qib_write_kreg_port(ppd, krp_rcvpktledcnt, ledblink);
 }
 
-#if defined(CONFIG_DCA) || defined(CONFIG_DCA_MODULE)
-static void qib_update_rhdrq_dca(struct qib_ctxtdata *rcd)
-{
-       struct qib_devdata *dd = rcd->dd;
-       struct qib_chip_specific *cspec = dd->cspec;
-       int cpu = get_cpu();
-
-       if (cspec->rhdr_cpu[rcd->ctxt] != cpu) {
-               const struct dca_reg_map *rmp;
-
-               cspec->rhdr_cpu[rcd->ctxt] = cpu;
-               rmp = &dca_rcvhdr_reg_map[rcd->ctxt];
-               cspec->dca_rcvhdr_ctrl[rmp->shadow_inx] &= rmp->mask;
-               cspec->dca_rcvhdr_ctrl[rmp->shadow_inx] |=
-                       (u64) dca3_get_tag(&dd->pcidev->dev, cpu) << rmp->lsb;
-               qib_write_kreg(dd, rmp->regno,
-                              cspec->dca_rcvhdr_ctrl[rmp->shadow_inx]);
-               cspec->dca_ctrl |= SYM_MASK(DCACtrlA, RcvHdrqDCAEnable);
-               qib_write_kreg(dd, KREG_IDX(DCACtrlA), cspec->dca_ctrl);
-       }
-       put_cpu();
-}
-
-static void qib_update_sdma_dca(struct qib_pportdata *ppd)
-{
-       struct qib_devdata *dd = ppd->dd;
-       struct qib_chip_specific *cspec = dd->cspec;
-       int cpu = get_cpu();
-       unsigned pidx = ppd->port - 1;
-
-       if (cspec->sdma_cpu[pidx] != cpu) {
-               cspec->sdma_cpu[pidx] = cpu;
-               cspec->dca_rcvhdr_ctrl[4] &= ~(ppd->hw_pidx ?
-                       SYM_MASK(DCACtrlF, SendDma1DCAOPH) :
-                       SYM_MASK(DCACtrlF, SendDma0DCAOPH));
-               cspec->dca_rcvhdr_ctrl[4] |=
-                       (u64) dca3_get_tag(&dd->pcidev->dev, cpu) <<
-                               (ppd->hw_pidx ?
-                                       SYM_LSB(DCACtrlF, SendDma1DCAOPH) :
-                                       SYM_LSB(DCACtrlF, SendDma0DCAOPH));
-               qib_write_kreg(dd, KREG_IDX(DCACtrlF),
-                              cspec->dca_rcvhdr_ctrl[4]);
-               cspec->dca_ctrl |= ppd->hw_pidx ?
-                       SYM_MASK(DCACtrlA, SendDMAHead1DCAEnable) :
-                       SYM_MASK(DCACtrlA, SendDMAHead0DCAEnable);
-               qib_write_kreg(dd, KREG_IDX(DCACtrlA), cspec->dca_ctrl);
-       }
-       put_cpu();
-}
-
-static void qib_setup_dca(struct qib_devdata *dd)
-{
-       struct qib_chip_specific *cspec = dd->cspec;
-       int i;
-
-       for (i = 0; i < ARRAY_SIZE(cspec->rhdr_cpu); i++)
-               cspec->rhdr_cpu[i] = -1;
-       for (i = 0; i < ARRAY_SIZE(cspec->sdma_cpu); i++)
-               cspec->sdma_cpu[i] = -1;
-       cspec->dca_rcvhdr_ctrl[0] =
-               (1ULL << SYM_LSB(DCACtrlB, RcvHdrq0DCAXfrCnt)) |
-               (1ULL << SYM_LSB(DCACtrlB, RcvHdrq1DCAXfrCnt)) |
-               (1ULL << SYM_LSB(DCACtrlB, RcvHdrq2DCAXfrCnt)) |
-               (1ULL << SYM_LSB(DCACtrlB, RcvHdrq3DCAXfrCnt));
-       cspec->dca_rcvhdr_ctrl[1] =
-               (1ULL << SYM_LSB(DCACtrlC, RcvHdrq4DCAXfrCnt)) |
-               (1ULL << SYM_LSB(DCACtrlC, RcvHdrq5DCAXfrCnt)) |
-               (1ULL << SYM_LSB(DCACtrlC, RcvHdrq6DCAXfrCnt)) |
-               (1ULL << SYM_LSB(DCACtrlC, RcvHdrq7DCAXfrCnt));
-       cspec->dca_rcvhdr_ctrl[2] =
-               (1ULL << SYM_LSB(DCACtrlD, RcvHdrq8DCAXfrCnt)) |
-               (1ULL << SYM_LSB(DCACtrlD, RcvHdrq9DCAXfrCnt)) |
-               (1ULL << SYM_LSB(DCACtrlD, RcvHdrq10DCAXfrCnt)) |
-               (1ULL << SYM_LSB(DCACtrlD, RcvHdrq11DCAXfrCnt));
-       cspec->dca_rcvhdr_ctrl[3] =
-               (1ULL << SYM_LSB(DCACtrlE, RcvHdrq12DCAXfrCnt)) |
-               (1ULL << SYM_LSB(DCACtrlE, RcvHdrq13DCAXfrCnt)) |
-               (1ULL << SYM_LSB(DCACtrlE, RcvHdrq14DCAXfrCnt)) |
-               (1ULL << SYM_LSB(DCACtrlE, RcvHdrq15DCAXfrCnt));
-       cspec->dca_rcvhdr_ctrl[4] =
-               (1ULL << SYM_LSB(DCACtrlF, RcvHdrq16DCAXfrCnt)) |
-               (1ULL << SYM_LSB(DCACtrlF, RcvHdrq17DCAXfrCnt));
-       for (i = 0; i < ARRAY_SIZE(cspec->sdma_cpu); i++)
-               qib_write_kreg(dd, KREG_IDX(DCACtrlB) + i,
-                              cspec->dca_rcvhdr_ctrl[i]);
-}
-
-#endif
-
 /*
  * Disable MSIx interrupt if enabled, call generic MSIx code
  * to cleanup, and clear pending MSIx interrupts.
@@ -2701,15 +2523,6 @@ static void qib_setup_7322_cleanup(struct qib_devdata *dd)
 {
        int i;
 
-#if defined(CONFIG_DCA) || defined(CONFIG_DCA_MODULE)
-       if (dd->flags & QIB_DCA_ENABLED) {
-               dca_remove_requester(&dd->pcidev->dev);
-               dd->flags &= ~QIB_DCA_ENABLED;
-               dd->cspec->dca_ctrl = 0;
-               qib_write_kreg(dd, KREG_IDX(DCACtrlA), dd->cspec->dca_ctrl);
-       }
-#endif
-
        qib_7322_free_irq(dd);
        kfree(dd->cspec->cntrs);
        kfree(dd->cspec->sendchkenable);
@@ -3017,11 +2830,6 @@ static irqreturn_t qib_7322pintr(int irq, void *data)
        if (dd->int_counter != (u32) -1)
                dd->int_counter++;
 
-#if defined(CONFIG_DCA) || defined(CONFIG_DCA_MODULE)
-       if (dd->flags & QIB_DCA_ENABLED)
-               qib_update_rhdrq_dca(rcd);
-#endif
-
        /* Clear the interrupt bit we expect to be set. */
        qib_write_kreg(dd, kr_intclear, ((1ULL << QIB_I_RCVAVAIL_LSB) |
                       (1ULL << QIB_I_RCVURG_LSB)) << rcd->ctxt);
@@ -3085,11 +2893,6 @@ static irqreturn_t sdma_intr(int irq, void *data)
        if (dd->int_counter != (u32) -1)
                dd->int_counter++;
 
-#if defined(CONFIG_DCA) || defined(CONFIG_DCA_MODULE)
-       if (dd->flags & QIB_DCA_ENABLED)
-               qib_update_sdma_dca(ppd);
-#endif
-
        /* Clear the interrupt bit we expect to be set. */
        qib_write_kreg(dd, kr_intclear, ppd->hw_pidx ?
                       INT_MASK_P(SDma, 1) : INT_MASK_P(SDma, 0));
@@ -3119,11 +2922,6 @@ static irqreturn_t sdma_idle_intr(int irq, void *data)
        if (dd->int_counter != (u32) -1)
                dd->int_counter++;
 
-#if defined(CONFIG_DCA) || defined(CONFIG_DCA_MODULE)
-       if (dd->flags & QIB_DCA_ENABLED)
-               qib_update_sdma_dca(ppd);
-#endif
-
        /* Clear the interrupt bit we expect to be set. */
        qib_write_kreg(dd, kr_intclear, ppd->hw_pidx ?
                       INT_MASK_P(SDmaIdle, 1) : INT_MASK_P(SDmaIdle, 0));
@@ -3153,11 +2951,6 @@ static irqreturn_t sdma_progress_intr(int irq, void *data)
        if (dd->int_counter != (u32) -1)
                dd->int_counter++;
 
-#if defined(CONFIG_DCA) || defined(CONFIG_DCA_MODULE)
-       if (dd->flags & QIB_DCA_ENABLED)
-               qib_update_sdma_dca(ppd);
-#endif
-
        /* Clear the interrupt bit we expect to be set. */
        qib_write_kreg(dd, kr_intclear, ppd->hw_pidx ?
                       INT_MASK_P(SDmaProgress, 1) :
@@ -3188,11 +2981,6 @@ static irqreturn_t sdma_cleanup_intr(int irq, void *data)
        if (dd->int_counter != (u32) -1)
                dd->int_counter++;
 
-#if defined(CONFIG_DCA) || defined(CONFIG_DCA_MODULE)
-       if (dd->flags & QIB_DCA_ENABLED)
-               qib_update_sdma_dca(ppd);
-#endif
-
        /* Clear the interrupt bit we expect to be set. */
        qib_write_kreg(dd, kr_intclear, ppd->hw_pidx ?
                       INT_MASK_PM(SDmaCleanupDone, 1) :
@@ -4299,10 +4087,6 @@ static void rcvctrl_7322_mod(struct qib_pportdata *ppd, unsigned int op,
                qib_write_kreg_ctxt(dd, krc_rcvhdraddr, ctxt,
                                    rcd->rcvhdrq_phys);
                rcd->seq_cnt = 1;
-#if defined(CONFIG_DCA) || defined(CONFIG_DCA_MODULE)
-               if (dd->flags & QIB_DCA_ENABLED)
-                       qib_update_rhdrq_dca(rcd);
-#endif
        }
        if (op & QIB_RCVCTRL_CTXT_DIS)
                ppd->p_rcvctrl &=
@@ -5360,7 +5144,13 @@ static int qib_7322_ib_updown(struct qib_pportdata *ppd, int ibup, u64 ibcs)
                                     QIBL_IB_AUTONEG_INPROG)))
                        set_7322_ibspeed_fast(ppd, ppd->link_speed_enabled);
                if (!(ppd->lflags & QIBL_IB_AUTONEG_INPROG)) {
+                       /* unlock the Tx settings, speed may change */
+                       qib_write_kreg_port(ppd, krp_tx_deemph_override,
+                               SYM_MASK(IBSD_TX_DEEMPHASIS_OVERRIDE_0,
+                               reset_tx_deemphasis_override));
                        qib_cancel_sends(ppd);
+                       /* on link down, ensure sane pcs state */
+                       qib_7322_mini_pcs_reset(ppd);
                        spin_lock_irqsave(&ppd->sdma_lock, flags);
                        if (__qib_sdma_running(ppd))
                                __qib_sdma_process_event(ppd,
@@ -5766,26 +5556,28 @@ static void qib_init_7322_qsfp(struct qib_pportdata *ppd)
 }
 
 /*
- * called at device initialization time, and also if the cable_atten
+ * called at device initialization time, and also if the txselect
  * module parameter is changed.  This is used for cables that don't
  * have valid QSFP EEPROMs (not present, or attenuation is zero).
  * We initialize to the default, then if there is a specific
- * unit,port match, we use that.
+ * unit,port match, we use that (and set it immediately, for the
+ * current speed, if the link is at INIT or better).
  * String format is "default# unit#,port#=# ... u,p=#", separators must
- * be a SPACE character.  A newline terminates.
+ * be a SPACE character.  A newline terminates.  The u,p=# tuples may
+ * optionally have "u,p=#,#", where the final # is the H1 value
  * The last specific match is used (actually, all are used, but last
  * one is the one that winds up set); if none at all, fall back on default.
  */
 static void set_no_qsfp_atten(struct qib_devdata *dd, int change)
 {
        char *nxt, *str;
-       int pidx, unit, port, deflt;
+       u32 pidx, unit, port, deflt, h1;
        unsigned long val;
-       int any = 0;
+       int any = 0, seth1;
 
-       str = cable_atten_list;
+       str = txselect_list;
 
-       /* default number is validated in setup_cable_atten() */
+       /* default number is validated in setup_txselect() */
        deflt = simple_strtoul(str, &nxt, 0);
        for (pidx = 0; pidx < dd->num_pports; ++pidx)
                dd->pport[pidx].cpspec->no_eep = deflt;
@@ -5812,16 +5604,28 @@ static void set_no_qsfp_atten(struct qib_devdata *dd, int change)
                                ;
                        continue;
                }
-               if (val >= TXDDS_TABLE_SZ)
+               if (val >= TXDDS_TABLE_SZ + TXDDS_EXTRA_SZ)
                        continue;
+               seth1 = 0;
+               h1 = 0; /* gcc thinks it might be used uninitted */
+               if (*nxt == ',' && nxt[1]) {
+                       str = ++nxt;
+                       h1 = (u32)simple_strtoul(str, &nxt, 0);
+                       if (nxt == str)
+                               while (*nxt && *nxt++ != ' ') /* skip */
+                                       ;
+                       else
+                               seth1 = 1;
+               }
                for (pidx = 0; dd->unit == unit && pidx < dd->num_pports;
                     ++pidx) {
-                       if (dd->pport[pidx].port != port ||
-                               !dd->pport[pidx].link_speed_supported)
+                       struct qib_pportdata *ppd = &dd->pport[pidx];
+
+                       if (ppd->port != port || !ppd->link_speed_supported)
                                continue;
-                       dd->pport[pidx].cpspec->no_eep = val;
+                       ppd->cpspec->no_eep = val;
                        /* now change the IBC and serdes, overriding generic */
-                       init_txdds_table(&dd->pport[pidx], 1);
+                       init_txdds_table(ppd, 1);
                        any++;
                }
                if (*nxt == '\n')
@@ -5832,35 +5636,35 @@ static void set_no_qsfp_atten(struct qib_devdata *dd, int change)
                 * Change the IBC and serdes, but since it's
                 * general, don't override specific settings.
                 */
-               for (pidx = 0; pidx < dd->num_pports; ++pidx) {
-                       if (!dd->pport[pidx].link_speed_supported)
-                               continue;
-                       init_txdds_table(&dd->pport[pidx], 0);
-               }
+               for (pidx = 0; pidx < dd->num_pports; ++pidx)
+                       if (dd->pport[pidx].link_speed_supported)
+                               init_txdds_table(&dd->pport[pidx], 0);
        }
 }
 
-/* handle the cable_atten parameter changing */
-static int setup_cable_atten(const char *str, struct kernel_param *kp)
+/* handle the txselect parameter changing */
+static int setup_txselect(const char *str, struct kernel_param *kp)
 {
        struct qib_devdata *dd;
        unsigned long val;
        char *n;
        if (strlen(str) >= MAX_ATTEN_LEN) {
-               printk(KERN_INFO QIB_DRV_NAME " cable_atten_values string "
+               printk(KERN_INFO QIB_DRV_NAME " txselect_values string "
                       "too long\n");
                return -ENOSPC;
        }
        val = simple_strtoul(str, &n, 0);
-       if (n == str || val >= TXDDS_TABLE_SZ) {
+       if (n == str || val >= (TXDDS_TABLE_SZ + TXDDS_EXTRA_SZ)) {
                printk(KERN_INFO QIB_DRV_NAME
-                      "cable_atten_values must start with a number\n");
+                      "txselect_values must start with a number < %d\n",
+                       TXDDS_TABLE_SZ + TXDDS_EXTRA_SZ);
                return -EINVAL;
        }
-       strcpy(cable_atten_list, str);
+       strcpy(txselect_list, str);
 
        list_for_each_entry(dd, &qib_dev_list, list)
-               set_no_qsfp_atten(dd, 1);
+               if (dd->deviceid == PCI_DEVICE_ID_QLOGIC_IB_7322)
+                       set_no_qsfp_atten(dd, 1);
        return 0;
 }
 
@@ -6261,28 +6065,17 @@ static int qib_init_7322_variables(struct qib_devdata *dd)
                 * in adapter-specific routines.
                 */
                if (!(ppd->dd->flags & QIB_HAS_QSFP)) {
-                       int i;
-                       const struct txdds_ent *txdds;
-
                        if (!IS_QMH(ppd->dd) && !IS_QME(ppd->dd))
                                qib_devinfo(ppd->dd->pcidev, "IB%u:%u: "
                                            "Unknown mezzanine card type\n",
-                                           ppd->dd->unit, ppd->port);
-                       txdds = IS_QMH(ppd->dd) ? &qmh_qdr_txdds :
-                               &qme_qdr_txdds;
-
+                                           dd->unit, ppd->port);
+                       cp->h1_val = IS_QMH(dd) ? H1_FORCE_QMH : H1_FORCE_QME;
                        /*
-                        * set values in case link comes up
-                        * before table is written to driver.
+                        * Choose center value as default tx serdes setting
+                        * until changed through module parameter.
                         */
-                       cp->h1_val = IS_QMH(ppd->dd) ? H1_FORCE_QMH :
-                               H1_FORCE_QME;
-                       for (i = 0; i < SERDES_CHANS; i++) {
-                               cp->amp[i] = txdds->amp;
-                               cp->pre[i] = txdds->pre;
-                               cp->mainv[i] = txdds->main;
-                               cp->post[i] = txdds->post;
-                       }
+                       ppd->cpspec->no_eep = IS_QMH(dd) ?
+                               TXDDS_TABLE_SZ + 2 : TXDDS_TABLE_SZ + 4;
                } else
                        cp->h1_val = H1_FORCE_VAL;
 
@@ -6299,8 +6092,7 @@ static int qib_init_7322_variables(struct qib_devdata *dd)
 
        dd->rcvhdrentsize = QIB_RCVHDR_ENTSIZE;
        dd->rcvhdrsize = QIB_DFLT_RCVHDRSIZE;
-       dd->rhf_offset =
-               dd->rcvhdrentsize - sizeof(u64) / sizeof(u32);
+       dd->rhf_offset = dd->rcvhdrentsize - sizeof(u64) / sizeof(u32);
 
        /* we always allocate at least 2048 bytes for eager buffers */
        dd->rcvegrbufsize = max(mtu, 2048);
@@ -6919,13 +6711,6 @@ struct qib_devdata *qib_init_iba7322_funcs(struct pci_dev *pdev,
        /* clear diagctrl register, in case diags were running and crashed */
        qib_write_kreg(dd, kr_hwdiagctrl, 0);
 
-#if defined(CONFIG_DCA) || defined(CONFIG_DCA_MODULE)
-       ret = dca_add_requester(&pdev->dev);
-       if (!ret) {
-               dd->flags |= QIB_DCA_ENABLED;
-               qib_setup_dca(dd);
-       }
-#endif
        goto bail;
 
 bail_cleanup:
@@ -7111,8 +6896,8 @@ static const struct txdds_ent txdds_ddr[TXDDS_TABLE_SZ] = {
 static const struct txdds_ent txdds_qdr[TXDDS_TABLE_SZ] = {
        /* amp, pre, main, post */
        {  2, 2, 15,  6 },      /* Loopback */
-       {  0, 1,  0,  7 },      /*  2 dB */
-       {  0, 1,  0,  9 },      /*  3 dB */
+       {  0, 1,  0,  7 },      /*  2 dB (also QMH7342) */
+       {  0, 1,  0,  9 },      /*  3 dB (also QMH7342) */
        {  0, 1,  0, 11 },      /*  4 dB */
        {  0, 1,  0, 13 },      /*  5 dB */
        {  0, 1,  0, 15 },      /*  6 dB */
@@ -7128,6 +6913,57 @@ static const struct txdds_ent txdds_qdr[TXDDS_TABLE_SZ] = {
        {  0, 2,  9, 15 },      /* 16 dB */
 };
 
+/*
+ * extra entries for use with txselect, for indices >= TXDDS_TABLE_SZ.
+ * These are mostly used for mez cards going through connectors
+ * and backplane traces, but can be used to add other "unusual"
+ * table values as well.
+ */
+static const struct txdds_ent txdds_extra_sdr[TXDDS_EXTRA_SZ] = {
+       /* amp, pre, main, post */
+       {  0, 0, 0,  1 },       /* QMH7342 backplane settings */
+       {  0, 0, 0,  1 },       /* QMH7342 backplane settings */
+       {  0, 0, 0,  2 },       /* QMH7342 backplane settings */
+       {  0, 0, 0,  2 },       /* QMH7342 backplane settings */
+       {  0, 0, 0, 11 },       /* QME7342 backplane settings */
+       {  0, 0, 0, 11 },       /* QME7342 backplane settings */
+       {  0, 0, 0, 11 },       /* QME7342 backplane settings */
+       {  0, 0, 0, 11 },       /* QME7342 backplane settings */
+       {  0, 0, 0, 11 },       /* QME7342 backplane settings */
+       {  0, 0, 0, 11 },       /* QME7342 backplane settings */
+       {  0, 0, 0, 11 },       /* QME7342 backplane settings */
+};
+
+static const struct txdds_ent txdds_extra_ddr[TXDDS_EXTRA_SZ] = {
+       /* amp, pre, main, post */
+       {  0, 0, 0,  7 },       /* QMH7342 backplane settings */
+       {  0, 0, 0,  7 },       /* QMH7342 backplane settings */
+       {  0, 0, 0,  8 },       /* QMH7342 backplane settings */
+       {  0, 0, 0,  8 },       /* QMH7342 backplane settings */
+       {  0, 0, 0, 13 },       /* QME7342 backplane settings */
+       {  0, 0, 0, 13 },       /* QME7342 backplane settings */
+       {  0, 0, 0, 13 },       /* QME7342 backplane settings */
+       {  0, 0, 0, 13 },       /* QME7342 backplane settings */
+       {  0, 0, 0, 13 },       /* QME7342 backplane settings */
+       {  0, 0, 0, 13 },       /* QME7342 backplane settings */
+       {  0, 0, 0, 13 },       /* QME7342 backplane settings */
+};
+
+static const struct txdds_ent txdds_extra_qdr[TXDDS_EXTRA_SZ] = {
+       /* amp, pre, main, post */
+       {  0, 1,  0,  4 },      /* QMH7342 backplane settings */
+       {  0, 1,  0,  5 },      /* QMH7342 backplane settings */
+       {  0, 1,  0,  6 },      /* QMH7342 backplane settings */
+       {  0, 1,  0,  8 },      /* QMH7342 backplane settings */
+       {  0, 1, 12, 10 },      /* QME7342 backplane setting */
+       {  0, 1, 12, 11 },      /* QME7342 backplane setting */
+       {  0, 1, 12, 12 },      /* QME7342 backplane setting */
+       {  0, 1, 12, 14 },      /* QME7342 backplane setting */
+       {  0, 1, 12,  6 },      /* QME7342 backplane setting */
+       {  0, 1, 12,  7 },      /* QME7342 backplane setting */
+       {  0, 1, 12,  8 },      /* QME7342 backplane setting */
+};
+
 static const struct txdds_ent *get_atten_table(const struct txdds_ent *txdds,
                                               unsigned atten)
 {
@@ -7145,7 +6981,7 @@ static const struct txdds_ent *get_atten_table(const struct txdds_ent *txdds,
 }
 
 /*
- * if override is set, the module parameter cable_atten has a value
+ * if override is set, the module parameter txselect has a value
  * for this specific port, so use it, rather than our normal mechanism.
  */
 static void find_best_ent(struct qib_pportdata *ppd,
@@ -7184,15 +7020,28 @@ static void find_best_ent(struct qib_pportdata *ppd,
                *ddr_dds = get_atten_table(txdds_ddr, qd->atten[0]);
                *qdr_dds = get_atten_table(txdds_qdr, qd->atten[1]);
                return;
-       } else {
+       } else if (ppd->cpspec->no_eep < TXDDS_TABLE_SZ) {
                /*
                 * If we have no (or incomplete) data from the cable
-                * EEPROM, or no QSFP, use the module parameter value
-                * to index into the attentuation table.
+                * EEPROM, or no QSFP, or override is set, use the
+                * module parameter value to index into the attentuation
+                * table.
                 */
-               *sdr_dds = &txdds_sdr[ppd->cpspec->no_eep];
-               *ddr_dds = &txdds_ddr[ppd->cpspec->no_eep];
-               *qdr_dds = &txdds_qdr[ppd->cpspec->no_eep];
+               idx = ppd->cpspec->no_eep;
+               *sdr_dds = &txdds_sdr[idx];
+               *ddr_dds = &txdds_ddr[idx];
+               *qdr_dds = &txdds_qdr[idx];
+       } else if (ppd->cpspec->no_eep < (TXDDS_TABLE_SZ + TXDDS_EXTRA_SZ)) {
+               /* similar to above, but index into the "extra" table. */
+               idx = ppd->cpspec->no_eep - TXDDS_TABLE_SZ;
+               *sdr_dds = &txdds_extra_sdr[idx];
+               *ddr_dds = &txdds_extra_ddr[idx];
+               *qdr_dds = &txdds_extra_qdr[idx];
+       } else {
+               /* this shouldn't happen, it's range checked */
+               *sdr_dds = txdds_sdr + qib_long_atten;
+               *ddr_dds = txdds_ddr + qib_long_atten;
+               *qdr_dds = txdds_qdr + qib_long_atten;
        }
 }
 
@@ -7203,33 +7052,24 @@ static void init_txdds_table(struct qib_pportdata *ppd, int override)
        int idx;
        int single_ent = 0;
 
-       if (IS_QMH(ppd->dd)) {
-               /* normally will be overridden, via setup_qmh() */
-               sdr_dds = &qmh_sdr_txdds;
-               ddr_dds = &qmh_ddr_txdds;
-               qdr_dds = &qmh_qdr_txdds;
-               single_ent = 1;
-       } else if (IS_QME(ppd->dd)) {
-               sdr_dds = &qme_sdr_txdds;
-               ddr_dds = &qme_ddr_txdds;
-               qdr_dds = &qme_qdr_txdds;
+       find_best_ent(ppd, &sdr_dds, &ddr_dds, &qdr_dds, override);
+
+       /* for mez cards or override, use the selected value for all entries */
+       if (!(ppd->dd->flags & QIB_HAS_QSFP) || override)
                single_ent = 1;
-       } else
-               find_best_ent(ppd, &sdr_dds, &ddr_dds, &qdr_dds, override);
 
        /* Fill in the first entry with the best entry found. */
        set_txdds(ppd, 0, sdr_dds);
        set_txdds(ppd, TXDDS_TABLE_SZ, ddr_dds);
        set_txdds(ppd, 2 * TXDDS_TABLE_SZ, qdr_dds);
-
-       /*
-        * for our current speed, also write that value into the
-        * tx serdes registers.
-        */
-       dds = (struct txdds_ent *)(ppd->link_speed_active == QIB_IB_QDR ?
-                                  qdr_dds : (ppd->link_speed_active ==
-                                             QIB_IB_DDR ? ddr_dds : sdr_dds));
-       write_tx_serdes_param(ppd, dds);
+       if (ppd->lflags & (QIBL_LINKINIT | QIBL_LINKARMED |
+               QIBL_LINKACTIVE)) {
+               dds = (struct txdds_ent *)(ppd->link_speed_active ==
+                                          QIB_IB_QDR ?  qdr_dds :
+                                          (ppd->link_speed_active ==
+                                           QIB_IB_DDR ? ddr_dds : sdr_dds));
+               write_tx_serdes_param(ppd, dds);
+       }
 
        /* Fill in the remaining entries with the default table values. */
        for (idx = 1; idx < ARRAY_SIZE(txdds_sdr); ++idx) {
@@ -7352,6 +7192,11 @@ static int serdes_7322_init(struct qib_pportdata *ppd)
         */
        init_txdds_table(ppd, 0);
 
+       /* ensure no tx overrides from earlier driver loads */
+       qib_write_kreg_port(ppd, krp_tx_deemph_override,
+               SYM_MASK(IBSD_TX_DEEMPHASIS_OVERRIDE_0,
+               reset_tx_deemphasis_override));
+
        /* Patch some SerDes defaults to "Better for IB" */
        /* Timing Loop Bandwidth: cdr_timing[11:9] = 0 */
        ibsd_wr_allchans(ppd, 2, 0, BMASK(11, 9));
@@ -7421,7 +7266,7 @@ static int serdes_7322_init(struct qib_pportdata *ppd)
                            QDR_STATIC_ADAPT_DOWN_R1 : QDR_STATIC_ADAPT_DOWN);
        ppd->cpspec->qdr_dfe_on = 1;
 
-       /* (FLoop LOS gate: PPM filter  enabled */
+       /* FLoop LOS gate: PPM filter  enabled */
        ibsd_wr_allchans(ppd, 38, 0 << 10, 1 << 10);
 
        /* rx offset center enabled */
@@ -7486,68 +7331,39 @@ static void write_tx_serdes_param(struct qib_pportdata *ppd,
                    SYM_MASK(IBSD_TX_DEEMPHASIS_OVERRIDE_0, txc0_ena) |
                    SYM_MASK(IBSD_TX_DEEMPHASIS_OVERRIDE_0, txcp1_ena) |
                    SYM_MASK(IBSD_TX_DEEMPHASIS_OVERRIDE_0, txcn1_ena));
-       deemph |= 1ULL << SYM_LSB(IBSD_TX_DEEMPHASIS_OVERRIDE_0,
-                                 tx_override_deemphasis_select);
-       deemph |= txdds->amp << SYM_LSB(IBSD_TX_DEEMPHASIS_OVERRIDE_0,
-                                   txampcntl_d2a);
-       deemph |= txdds->main << SYM_LSB(IBSD_TX_DEEMPHASIS_OVERRIDE_0,
-                                     txc0_ena);
-       deemph |= txdds->post << SYM_LSB(IBSD_TX_DEEMPHASIS_OVERRIDE_0,
-                                    txcp1_ena);
-       deemph |= txdds->pre << SYM_LSB(IBSD_TX_DEEMPHASIS_OVERRIDE_0,
+
+       deemph |= SYM_MASK(IBSD_TX_DEEMPHASIS_OVERRIDE_0,
+                          tx_override_deemphasis_select);
+       deemph |= (txdds->amp & SYM_RMASK(IBSD_TX_DEEMPHASIS_OVERRIDE_0,
+                   txampcntl_d2a)) << SYM_LSB(IBSD_TX_DEEMPHASIS_OVERRIDE_0,
+                                      txampcntl_d2a);
+       deemph |= (txdds->main & SYM_RMASK(IBSD_TX_DEEMPHASIS_OVERRIDE_0,
+                    txc0_ena)) << SYM_LSB(IBSD_TX_DEEMPHASIS_OVERRIDE_0,
+                                  txc0_ena);
+       deemph |= (txdds->post & SYM_RMASK(IBSD_TX_DEEMPHASIS_OVERRIDE_0,
+                    txcp1_ena)) << SYM_LSB(IBSD_TX_DEEMPHASIS_OVERRIDE_0,
+                                   txcp1_ena);
+       deemph |= (txdds->pre & SYM_RMASK(IBSD_TX_DEEMPHASIS_OVERRIDE_0,
+                    txcn1_ena)) << SYM_LSB(IBSD_TX_DEEMPHASIS_OVERRIDE_0,
                                    txcn1_ena);
        qib_write_kreg_port(ppd, krp_tx_deemph_override, deemph);
 }
 
 /*
- * set per-bay, per channel parameters.  For now, we ignore
- * do_tx, and always set tx parameters, and set them with the same value
- * for all channels, using the channel 0 value.   We may switch to
- * per-channel settings in the future, and that method only needs
- * to be done once.
- * Because this also writes the IBC txdds table with a single set
- * of values, it should be called only for cases where we want to completely
- * force a specific setting, typically only for mez cards.
+ * Set the parameters for mez cards on link bounce, so they are
+ * always exactly what was requested.  Similar logic to init_txdds
+ * but does just the serdes.
  */
 static void adj_tx_serdes(struct qib_pportdata *ppd)
 {
-       struct txdds_ent txdds;
-       int i;
-       u8 *amp, *pre, *mainv, *post;
-
-       /*
-        * Because we use TX_DEEMPHASIS_OVERRIDE, we need to
-        * always do tx side, just like H1, since it is cleared
-        * by link down
-        */
-       amp = ppd->cpspec->amp;
-       pre = ppd->cpspec->pre;
-       mainv = ppd->cpspec->mainv;
-       post = ppd->cpspec->post;
-
-       amp[0] &= SYM_RMASK(IBSD_TX_DEEMPHASIS_OVERRIDE_0,
-                           txampcntl_d2a);
-       mainv[0] &= SYM_RMASK(IBSD_TX_DEEMPHASIS_OVERRIDE_0,
-                             txc0_ena);
-       post[0] &= SYM_RMASK(IBSD_TX_DEEMPHASIS_OVERRIDE_0,
-                            txcp1_ena);
-       pre[0] &= SYM_RMASK(IBSD_TX_DEEMPHASIS_OVERRIDE_0,
-                           txcn1_ena);
-
-       /*
-        * Use the channel zero values, only, for now, for
-        * all channels
-       */
-       txdds.amp = amp[0];
-       txdds.pre = pre[0];
-       txdds.main = mainv[0];
-       txdds.post = post[0];
-
-       /* write the QDR table for IBC use, as backup for link down */
-       for (i = 0; i < ARRAY_SIZE(txdds_qdr); ++i)
-               set_txdds(ppd, i + 32, &txdds);
+       const struct txdds_ent *sdr_dds, *ddr_dds, *qdr_dds;
+       struct txdds_ent *dds;
 
-       write_tx_serdes_param(ppd, &txdds);
+       find_best_ent(ppd, &sdr_dds, &ddr_dds, &qdr_dds, 1);
+       dds = (struct txdds_ent *)(ppd->link_speed_active == QIB_IB_QDR ?
+               qdr_dds : (ppd->link_speed_active == QIB_IB_DDR ?
+                               ddr_dds : sdr_dds));
+       write_tx_serdes_param(ppd, dds);
 }
 
 /* set QDR forced value for H1, if needed */
@@ -7567,235 +7383,6 @@ static void force_h1(struct qib_pportdata *ppd)
        }
 }
 
-/*
- * Parse the parameters for the QMH7342, to get rx and tx serdes
- * settings for that Bay, for both possible mez connectors (PCIe bus)
- * and IB link (one link on mez1, two possible on mez2).
- *
- * Data is comma or white space separated.
- *
- * A set of data has 7 groups, rx and tx groups have SERDES_CHANS values,
- * one per IB lane (serdes channel).
- * The groups are Bay, bus# H1 rcv, and amp, pre, post, main Tx values (QDR).
- * The Bay # is used only for debugging currently.
- * H1 values are set whenever the link goes down, or is at cfg_test or
- * cfg_wait_enh.  Tx values are programmed once, when this routine is called
- * (and with default values at chip initialization).  Values are any base, in
- * strtoul style, and values are seperated by comma, or any white space
- * (space, tab, newline).
- *
- * An example set might look like this (white space vs
- * comma used for human ease of reading)
- * The ordering is a set of Bay# Bus# H1, amp, pre, post, and main for mez1 IB1,
- * repeat for mez2 IB1, then mez2 IB2.
- *
- * B B H1:0       amp:0       pre:0        post: 0        main:0
- * a u H1:  1     amp:  1     pre:  1      post:   1      main:  1
- * y s H1:    2   amp:    2   pre:    2    post:      2   main:    2
- *     H1:      4 amp:      3 pre:      3  post:        3 main:      3
- * 1 3    8,6,5,6     0,0,0,0     1,1,1,1       10,10,10,10    3,3,3,3
- * 1 6    7,6,6,7     0,0,0,0     1,1,1,1       10,10,10,10    3,3,3,3
- * 1 6    9,7,7,8     0,0,0,0     1,1,1,1       10,10,10,10    3,3,3,3
- */
-#define N_QMH_FIELDS 22
-static int setup_qmh_params(const char *str, struct kernel_param *kp)
-{
-       char *abuf, *v, *nv, *nvp;
-       struct qib_devdata *dd;
-       struct qib_pportdata *ppd;
-       u32 mez, vlen, nf, port, bay;
-       int ret = 0, found = 0;
-
-       vlen = strlen(str) + 1;
-       abuf = kmalloc(vlen, GFP_KERNEL);
-       if (!abuf) {
-               printk(KERN_INFO QIB_DRV_NAME
-                      " Unable to allocate QMH param buffer; ignoring\n");
-               return 0;
-       }
-       memcpy(abuf, str, vlen);
-       v = abuf;
-
-       /* these 3 are because gcc can't know they are set before used */
-       port = 1;
-       mez = 1; /* used only for debugging */
-       bay = 0; /* used only for debugging */
-       ppd = NULL;
-       for (nf = 0; (nv = strsep(&v, ", \t\n\r")) &&
-            nf < (N_QMH_FIELDS * 3);) {
-               u32 val;
-
-               if (!*nv)
-                       /* allow for multiple separators */
-                       continue;
-
-               val = simple_strtoul(nv, &nvp, 0);
-               if (nv == nvp) {
-                       printk(KERN_INFO QIB_DRV_NAME
-                              " Bay%u, mez%u IB%u non-numeric value (%s) "
-                              "field #%u, ignoring rest\n", bay, mez,
-                              port, nv, nf % (N_QMH_FIELDS * 3));
-                       ret = -EINVAL;
-                       goto bail;
-               }
-               if (!(nf % N_QMH_FIELDS)) {
-                       ppd = NULL;
-                       bay = val;
-                       if (!bay || bay > 16) {
-                               printk(KERN_INFO QIB_DRV_NAME
-                                      " Invalid bay # %u, field %u, "
-                                      "ignoring rest\n", bay, nf);
-                               ret = -EINVAL;
-                               goto bail;
-                       }
-               } else if ((nf % N_QMH_FIELDS) == 1) {
-                       u32 bus = val;
-                       if (nf == 1) {
-                               mez = 1;
-                               port = 1;
-                       } else if (nf == (N_QMH_FIELDS + 1)) {
-                               mez = 2;
-                               port = 1;
-                       } else {
-                               mez = 2;
-                               port = 2;
-                       }
-                       list_for_each_entry(dd, &qib_dev_list, list) {
-                               if (dd->deviceid != PCI_DEVICE_ID_QLOGIC_IB_7322
-                                   || !IS_QMH(dd))
-                                       continue; /* only for QMH cards */
-                               if (dd->pcidev->bus->number == bus) {
-                                       found++;
-                                       ppd = &dd->pport[port - 1];
-                               }
-                       }
-               } else if (ppd) {
-                       u32 parm = (nf % N_QMH_FIELDS) - 2;
-                       if (parm < SERDES_CHANS && !(parm % SERDES_CHANS))
-                               ppd->cpspec->h1_val = val;
-                       else if (parm < (2 * SERDES_CHANS))
-                               ppd->cpspec->amp[parm % SERDES_CHANS] = val;
-                       else if (parm < (3 * SERDES_CHANS))
-                               ppd->cpspec->pre[parm % SERDES_CHANS] = val;
-                       else if (parm < (4 * SERDES_CHANS))
-                               ppd->cpspec->post[parm % SERDES_CHANS] = val;
-                       else {
-                               ppd->cpspec->mainv[parm % SERDES_CHANS] = val;
-                               /* At the end of a port, set params */
-                               if (parm == ((5 * SERDES_CHANS) - 1))
-                                       adj_tx_serdes(ppd);
-                       }
-               }
-               nf++;
-       }
-       if (!found) {
-               printk(KERN_ERR QIB_DRV_NAME
-                      ": No match found for qmh_serdes_setup parameter\n");
-               ret = -EINVAL;
-       }
-bail:
-       kfree(abuf);
-       return ret;
-}
-
-/*
- * Similarly for QME7342, but the format is simpler, values are the
- * same for all mez card positions in a blade (2 or 4 per blade), but
- * are different for some blades vs others, and we don't need to
- * specify different parameters for different serdes channels or different
- * IB ports.
- * Format is: h1 amp,pre,post,main
- * Alternate format (so ports can be different): Pport# h1 amp,pre,post,main
- */
-#define N_QME_FIELDS 5
-static int setup_qme_params(const char *str, struct kernel_param *kp)
-{
-       char *abuf, *v, *nv, *nvp;
-       struct qib_devdata *dd;
-       u32 vlen, nf, port = 0;
-       u8 h1, tx[4]; /* amp, pre, post, main */
-       int ret =  -EINVAL;
-       char *seplist;
-
-       vlen = strlen(str) + 1;
-       abuf = kmalloc(vlen, GFP_KERNEL);
-       if (!abuf) {
-               printk(KERN_INFO QIB_DRV_NAME
-                      " Unable to allocate QME param buffer; ignoring\n");
-               return 0;
-       }
-       strncpy(abuf, str, vlen);
-
-       v = abuf;
-       seplist = " \t";
-       h1 = H1_FORCE_QME; /* gcc can't figure out always set before used */
-
-       for (nf = 0; (nv = strsep(&v, seplist)); ) {
-               u32 val;
-
-               if (!*nv)
-                       /* allow for multiple separators */
-                       continue;
-
-               if (!nf && *nv == 'P') {
-                       /* alternate format with port */
-                       val = simple_strtoul(++nv, &nvp, 0);
-                       if (nv == nvp || port >= NUM_IB_PORTS) {
-                               printk(KERN_INFO QIB_DRV_NAME
-                                      " %s: non-numeric port value (%s) "
-                                      "ignoring rest\n", __func__, nv);
-                               goto done;
-                       }
-                       port = val;
-                       continue; /* without incrementing nf */
-               }
-               val = simple_strtoul(nv, &nvp, 0);
-               if (nv == nvp) {
-                       printk(KERN_INFO QIB_DRV_NAME
-                              " %s: non-numeric value (%s) "
-                              "field #%u, ignoring rest\n", __func__,
-                              nv, nf);
-                       goto done;
-               }
-               if (!nf) {
-                       h1 = val;
-                       seplist = ",";
-               } else
-                       tx[nf - 1] = val;
-               if (++nf == N_QME_FIELDS) {
-                       list_for_each_entry(dd, &qib_dev_list, list) {
-                               int pidx, i;
-                               if (dd->deviceid != PCI_DEVICE_ID_QLOGIC_IB_7322
-                                   || !IS_QME(dd))
-                                       continue; /* only for QME cards */
-                               for (pidx = 0; pidx < dd->num_pports; ++pidx) {
-                                       struct qib_pportdata *ppd;
-                                       ppd = &dd->pport[pidx];
-                                       if ((port && ppd->port != port) ||
-                                               !ppd->link_speed_supported)
-                                               continue;
-                                       ppd->cpspec->h1_val = h1;
-                                       for (i = 0; i < SERDES_CHANS; i++) {
-                                               ppd->cpspec->amp[i] = tx[0];
-                                               ppd->cpspec->pre[i] = tx[1];
-                                               ppd->cpspec->post[i] = tx[2];
-                                               ppd->cpspec->mainv[i] = tx[3];
-                                       }
-                                       adj_tx_serdes(ppd);
-                               }
-                       }
-                       ret = 0;
-                       goto done;
-               }
-       }
-       printk(KERN_INFO QIB_DRV_NAME
-              " %s: Only %u of %u fields provided, skipping\n",
-              __func__, nf, N_QME_FIELDS);
-done:
-       kfree(abuf);
-       return ret;
-}
-
 #define SJA_EN SYM_MASK(SPC_JTAG_ACCESS_REG, SPC_JTAG_ACCESS_EN)
 #define BISTEN_LSB SYM_LSB(SPC_JTAG_ACCESS_REG, bist_en)
 
index c0139c0..9b40f34 100644 (file)
@@ -1237,7 +1237,13 @@ static int __devinit qib_init_one(struct pci_dev *pdev,
         */
        switch (ent->device) {
        case PCI_DEVICE_ID_QLOGIC_IB_6120:
+#ifdef CONFIG_PCI_MSI
                dd = qib_init_iba6120_funcs(pdev, ent);
+#else
+               qib_early_err(&pdev->dev, "QLogic PCIE device 0x%x cannot "
+                     "work if CONFIG_PCI_MSI is not enabled\n",
+                     ent->device);
+#endif
                break;
 
        case PCI_DEVICE_ID_QLOGIC_IB_7220:
index b9f58ca..6703c6b 100644 (file)
@@ -590,4 +590,17 @@ config TOUCHSCREEN_PCAP
 
          To compile this driver as a module, choose M here: the
          module will be called pcap_ts.
+
+config TOUCHSCREEN_TPS6507X
+       tristate "TPS6507x based touchscreens"
+       depends on I2C
+       help
+         Say Y here if you have a TPS6507x based touchscreen
+         controller.
+
+         If unsure, say N.
+
+         To compile this driver as a module, choose M here: the
+         module will be called tps6507x_ts.
+
 endif
index 8ad36ee..497964a 100644 (file)
@@ -46,3 +46,4 @@ obj-$(CONFIG_TOUCHSCREEN_WM97XX_ATMEL)        += atmel-wm97xx.o
 obj-$(CONFIG_TOUCHSCREEN_WM97XX_MAINSTONE)     += mainstone-wm97xx.o
 obj-$(CONFIG_TOUCHSCREEN_WM97XX_ZYLONITE)      += zylonite-wm97xx.o
 obj-$(CONFIG_TOUCHSCREEN_W90X900)      += w90p910_ts.o
+obj-$(CONFIG_TOUCHSCREEN_TPS6507X)     += tps6507x-ts.o
diff --git a/drivers/input/touchscreen/tps6507x-ts.c b/drivers/input/touchscreen/tps6507x-ts.c
new file mode 100644 (file)
index 0000000..5de80a1
--- /dev/null
@@ -0,0 +1,400 @@
+/*
+ * drivers/input/touchscreen/tps6507x_ts.c
+ *
+ * Touchscreen driver for the tps6507x chip.
+ *
+ * Copyright (c) 2009 RidgeRun (todd.fischer@ridgerun.com)
+ *
+ * Credits:
+ *
+ *    Using code from tsc2007, MtekVision Co., Ltd.
+ *
+ * For licencing details see kernel-base/COPYING
+ *
+ * TPS65070, TPS65073, TPS650731, and TPS650732 support
+ * 10 bit touch screen interface.
+ */
+
+#include <linux/module.h>
+#include <linux/workqueue.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/tps6507x.h>
+#include <linux/input/tps6507x-ts.h>
+#include <linux/delay.h>
+
+#define TSC_DEFAULT_POLL_PERIOD 30 /* ms */
+#define TPS_DEFAULT_MIN_PRESSURE 0x30
+#define MAX_10BIT ((1 << 10) - 1)
+
+#define        TPS6507X_ADCONFIG_CONVERT_TS (TPS6507X_ADCONFIG_AD_ENABLE | \
+                                        TPS6507X_ADCONFIG_START_CONVERSION | \
+                                        TPS6507X_ADCONFIG_INPUT_REAL_TSC)
+#define        TPS6507X_ADCONFIG_POWER_DOWN_TS (TPS6507X_ADCONFIG_INPUT_REAL_TSC)
+
+struct ts_event {
+       u16     x;
+       u16     y;
+       u16     pressure;
+};
+
+struct tps6507x_ts {
+       struct input_dev        *input_dev;
+       struct device           *dev;
+       char                    phys[32];
+       struct workqueue_struct *wq;
+       struct delayed_work     work;
+       unsigned                polling;        /* polling is active */
+       struct ts_event         tc;
+       struct tps6507x_dev     *mfd;
+       u16                     model;
+       unsigned                pendown;
+       int                     irq;
+       void                    (*clear_penirq)(void);
+       unsigned long           poll_period;    /* ms */
+       u16                     min_pressure;
+       int                     vref;           /* non-zero to leave vref on */
+};
+
+static int tps6507x_read_u8(struct tps6507x_ts *tsc, u8 reg, u8 *data)
+{
+       int err;
+
+       err = tsc->mfd->read_dev(tsc->mfd, reg, 1, data);
+
+       if (err)
+               return err;
+
+       return 0;
+}
+
+static int tps6507x_write_u8(struct tps6507x_ts *tsc, u8 reg, u8 data)
+{
+       return tsc->mfd->write_dev(tsc->mfd, reg, 1, &data);
+}
+
+static s32 tps6507x_adc_conversion(struct tps6507x_ts *tsc,
+                                  u8 tsc_mode, u16 *value)
+{
+       s32 ret;
+       u8 adc_status;
+       u8 result;
+
+       /* Route input signal to A/D converter */
+
+       ret = tps6507x_write_u8(tsc, TPS6507X_REG_TSCMODE, tsc_mode);
+       if (ret) {
+               dev_err(tsc->dev, "TSC mode read failed\n");
+               goto err;
+       }
+
+       /* Start A/D conversion */
+
+       ret = tps6507x_write_u8(tsc, TPS6507X_REG_ADCONFIG,
+                               TPS6507X_ADCONFIG_CONVERT_TS);
+       if (ret) {
+               dev_err(tsc->dev, "ADC config write failed\n");
+               return ret;
+       }
+
+       do {
+               ret = tps6507x_read_u8(tsc, TPS6507X_REG_ADCONFIG,
+                                      &adc_status);
+               if (ret) {
+                       dev_err(tsc->dev, "ADC config read failed\n");
+                       goto err;
+               }
+       } while (adc_status & TPS6507X_ADCONFIG_START_CONVERSION);
+
+       ret = tps6507x_read_u8(tsc, TPS6507X_REG_ADRESULT_2, &result);
+       if (ret) {
+               dev_err(tsc->dev, "ADC result 2 read failed\n");
+               goto err;
+       }
+
+       *value = (result & TPS6507X_REG_ADRESULT_2_MASK) << 8;
+
+       ret = tps6507x_read_u8(tsc, TPS6507X_REG_ADRESULT_1, &result);
+       if (ret) {
+               dev_err(tsc->dev, "ADC result 1 read failed\n");
+               goto err;
+       }
+
+       *value |= result;
+
+       dev_dbg(tsc->dev, "TSC channel %d = 0x%X\n", tsc_mode, *value);
+
+err:
+       return ret;
+}
+
+/* Need to call tps6507x_adc_standby() after using A/D converter for the
+ * touch screen interrupt to work properly.
+ */
+
+static s32 tps6507x_adc_standby(struct tps6507x_ts *tsc)
+{
+       s32 ret;
+       s32 loops = 0;
+       u8 val;
+
+       ret = tps6507x_write_u8(tsc,  TPS6507X_REG_ADCONFIG,
+                               TPS6507X_ADCONFIG_INPUT_TSC);
+       if (ret)
+               return ret;
+
+       ret = tps6507x_write_u8(tsc, TPS6507X_REG_TSCMODE,
+                               TPS6507X_TSCMODE_STANDBY);
+       if (ret)
+               return ret;
+
+       ret = tps6507x_read_u8(tsc, TPS6507X_REG_INT, &val);
+       if (ret)
+               return ret;
+
+       while (val & TPS6507X_REG_TSC_INT) {
+               mdelay(10);
+               ret = tps6507x_read_u8(tsc, TPS6507X_REG_INT, &val);
+               if (ret)
+                       return ret;
+               loops++;
+       }
+
+       return ret;
+}
+
+static void tps6507x_ts_handler(struct work_struct *work)
+{
+       struct tps6507x_ts *tsc =  container_of(work,
+                               struct tps6507x_ts, work.work);
+       struct input_dev *input_dev = tsc->input_dev;
+       int pendown;
+       int schd;
+       int poll = 0;
+       s32 ret;
+
+       ret =  tps6507x_adc_conversion(tsc, TPS6507X_TSCMODE_PRESSURE,
+                                      &tsc->tc.pressure);
+       if (ret)
+               goto done;
+
+       pendown = tsc->tc.pressure > tsc->min_pressure;
+
+       if (unlikely(!pendown && tsc->pendown)) {
+               dev_dbg(tsc->dev, "UP\n");
+               input_report_key(input_dev, BTN_TOUCH, 0);
+               input_report_abs(input_dev, ABS_PRESSURE, 0);
+               input_sync(input_dev);
+               tsc->pendown = 0;
+       }
+
+       if (pendown) {
+
+               if (!tsc->pendown) {
+                       dev_dbg(tsc->dev, "DOWN\n");
+                       input_report_key(input_dev, BTN_TOUCH, 1);
+               } else
+                       dev_dbg(tsc->dev, "still down\n");
+
+               ret =  tps6507x_adc_conversion(tsc, TPS6507X_TSCMODE_X_POSITION,
+                                              &tsc->tc.x);
+               if (ret)
+                       goto done;
+
+               ret =  tps6507x_adc_conversion(tsc, TPS6507X_TSCMODE_Y_POSITION,
+                                              &tsc->tc.y);
+               if (ret)
+                       goto done;
+
+               input_report_abs(input_dev, ABS_X, tsc->tc.x);
+               input_report_abs(input_dev, ABS_Y, tsc->tc.y);
+               input_report_abs(input_dev, ABS_PRESSURE, tsc->tc.pressure);
+               input_sync(input_dev);
+               tsc->pendown = 1;
+               poll = 1;
+       }
+
+done:
+       /* always poll if not using interrupts */
+       poll = 1;
+
+       if (poll) {
+               schd = queue_delayed_work(tsc->wq, &tsc->work,
+                                         tsc->poll_period * HZ / 1000);
+               if (schd)
+                       tsc->polling = 1;
+               else {
+                       tsc->polling = 0;
+                       dev_err(tsc->dev, "re-schedule failed");
+               }
+       } else
+               tsc->polling = 0;
+
+       ret = tps6507x_adc_standby(tsc);
+}
+
+static int tps6507x_ts_probe(struct platform_device *pdev)
+{
+       int error;
+       struct tps6507x_ts *tsc;
+       struct tps6507x_dev *tps6507x_dev = dev_get_drvdata(pdev->dev.parent);
+       struct touchscreen_init_data *init_data;
+       struct input_dev *input_dev;
+       struct tps6507x_board *tps_board;
+       int schd;
+
+       /**
+        * tps_board points to pmic related constants
+        * coming from the board-evm file.
+        */
+
+       tps_board = (struct tps6507x_board *)tps6507x_dev->dev->platform_data;
+
+       if (!tps_board) {
+               dev_err(tps6507x_dev->dev,
+                       "Could not find tps6507x platform data\n");
+               return -EIO;
+       }
+
+       /**
+        * init_data points to array of regulator_init structures
+        * coming from the board-evm file.
+        */
+
+       init_data = tps_board->tps6507x_ts_init_data;
+
+       tsc = kzalloc(sizeof(struct tps6507x_ts), GFP_KERNEL);
+       if (!tsc) {
+               dev_err(tps6507x_dev->dev, "failed to allocate driver data\n");
+               error = -ENOMEM;
+               goto err0;
+       }
+
+       tps6507x_dev->ts = tsc;
+       tsc->mfd = tps6507x_dev;
+       tsc->dev = tps6507x_dev->dev;
+       input_dev = input_allocate_device();
+       if (!input_dev) {
+               dev_err(tsc->dev, "Failed to allocate input device.\n");
+               error = -ENOMEM;
+               goto err1;
+       }
+
+       input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+       input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
+
+       input_set_abs_params(input_dev, ABS_X, 0, MAX_10BIT, 0, 0);
+       input_set_abs_params(input_dev, ABS_Y, 0, MAX_10BIT, 0, 0);
+       input_set_abs_params(input_dev, ABS_PRESSURE, 0, MAX_10BIT, 0, 0);
+
+       input_dev->name = "TPS6507x Touchscreen";
+       input_dev->id.bustype = BUS_I2C;
+       input_dev->dev.parent = tsc->dev;
+
+       snprintf(tsc->phys, sizeof(tsc->phys),
+                "%s/input0", dev_name(tsc->dev));
+       input_dev->phys = tsc->phys;
+
+       dev_dbg(tsc->dev, "device: %s\n", input_dev->phys);
+
+       input_set_drvdata(input_dev, tsc);
+
+       tsc->input_dev = input_dev;
+
+       INIT_DELAYED_WORK(&tsc->work, tps6507x_ts_handler);
+       tsc->wq = create_workqueue("TPS6507x Touchscreen");
+
+       if (init_data) {
+               tsc->poll_period = init_data->poll_period;
+               tsc->vref = init_data->vref;
+               tsc->min_pressure = init_data->min_pressure;
+               input_dev->id.vendor = init_data->vendor;
+               input_dev->id.product = init_data->product;
+               input_dev->id.version = init_data->version;
+       } else {
+               tsc->poll_period = TSC_DEFAULT_POLL_PERIOD;
+               tsc->min_pressure = TPS_DEFAULT_MIN_PRESSURE;
+       }
+
+       error = tps6507x_adc_standby(tsc);
+       if (error)
+               goto err2;
+
+       error = input_register_device(input_dev);
+       if (error)
+               goto err2;
+
+       schd = queue_delayed_work(tsc->wq, &tsc->work,
+                                 tsc->poll_period * HZ / 1000);
+
+       if (schd)
+               tsc->polling = 1;
+       else {
+               tsc->polling = 0;
+               dev_err(tsc->dev, "schedule failed");
+               goto err2;
+        }
+
+       return 0;
+
+err2:
+       cancel_delayed_work(&tsc->work);
+       flush_workqueue(tsc->wq);
+       destroy_workqueue(tsc->wq);
+       tsc->wq = 0;
+       input_free_device(input_dev);
+err1:
+       kfree(tsc);
+       tps6507x_dev->ts = NULL;
+err0:
+       return error;
+}
+
+static int __devexit tps6507x_ts_remove(struct platform_device *pdev)
+{
+       struct tps6507x_dev *tps6507x_dev = platform_get_drvdata(pdev);
+       struct tps6507x_ts *tsc = tps6507x_dev->ts;
+       struct input_dev *input_dev = tsc->input_dev;
+
+       if (!tsc)
+               return 0;
+
+       cancel_delayed_work(&tsc->work);
+       flush_workqueue(tsc->wq);
+       destroy_workqueue(tsc->wq);
+       tsc->wq = 0;
+
+       input_free_device(input_dev);
+
+       tps6507x_dev->ts = NULL;
+       kfree(tsc);
+
+       return 0;
+}
+
+static struct platform_driver tps6507x_ts_driver = {
+       .driver = {
+               .name = "tps6507x-ts",
+               .owner = THIS_MODULE,
+       },
+       .probe = tps6507x_ts_probe,
+       .remove = __devexit_p(tps6507x_ts_remove),
+};
+
+static int __init tps6507x_ts_init(void)
+{
+       return platform_driver_register(&tps6507x_ts_driver);
+}
+module_init(tps6507x_ts_init);
+
+static void __exit tps6507x_ts_exit(void)
+{
+       platform_driver_unregister(&tps6507x_ts_driver);
+}
+module_exit(tps6507x_ts_exit);
+
+MODULE_AUTHOR("Todd Fischer <todd.fischer@ridgerun.com>");
+MODULE_DESCRIPTION("TPS6507x - TouchScreen driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:tps6507x-tsc");
index 505eb64..81bf25e 100644 (file)
@@ -21,7 +21,7 @@ comment "LED drivers"
 
 config LEDS_88PM860X
        tristate "LED Support for Marvell 88PM860x PMIC"
-       depends on LEDS_CLASS && MFD_88PM860X
+       depends on MFD_88PM860X
        help
          This option enables support for on-chip LED drivers found on Marvell
          Semiconductor 88PM8606 PMIC.
@@ -67,6 +67,16 @@ config LEDS_NET48XX
          This option enables support for the Soekris net4801 and net4826 error
          LED.
 
+config LEDS_NET5501
+       tristate "LED Support for Soekris net5501 series Error LED"
+       depends on LEDS_TRIGGERS
+       depends on X86 && LEDS_GPIO_PLATFORM && GPIO_CS5535
+       select LEDS_TRIGGER_DEFAULT_ON
+       default n
+       help
+         Add support for the Soekris net5501 board (detection, error led
+         and GPIO).
+
 config LEDS_FSG
        tristate "LED Support for the Freecom FSG-3"
        depends on MACH_FSG
@@ -285,6 +295,13 @@ config LEDS_DELL_NETBOOKS
          This adds support for the Latitude 2100 and similar
          notebooks that have an external LED.
 
+config LEDS_MC13783
+       tristate "LED Support for MC13783 PMIC"
+       depends on MFD_MC13783
+       help
+         This option enable support for on-chip LED drivers found
+         on Freescale Semiconductor MC13783 PMIC.
+
 config LEDS_TRIGGERS
        bool "LED Trigger support"
        help
index 0cd8b99..2493de4 100644 (file)
@@ -13,6 +13,7 @@ obj-$(CONFIG_LEDS_MIKROTIK_RB532)     += leds-rb532.o
 obj-$(CONFIG_LEDS_S3C24XX)             += leds-s3c24xx.o
 obj-$(CONFIG_LEDS_AMS_DELTA)           += leds-ams-delta.o
 obj-$(CONFIG_LEDS_NET48XX)             += leds-net48xx.o
+obj-$(CONFIG_LEDS_NET5501)             += leds-net5501.o
 obj-$(CONFIG_LEDS_WRAP)                        += leds-wrap.o
 obj-$(CONFIG_LEDS_ALIX2)               += leds-alix2.o
 obj-$(CONFIG_LEDS_H1940)               += leds-h1940.o
@@ -35,6 +36,7 @@ obj-$(CONFIG_LEDS_INTEL_SS4200)               += leds-ss4200.o
 obj-$(CONFIG_LEDS_LT3593)              += leds-lt3593.o
 obj-$(CONFIG_LEDS_ADP5520)             += leds-adp5520.o
 obj-$(CONFIG_LEDS_DELL_NETBOOKS)       += dell-led.o
+obj-$(CONFIG_LEDS_MC13783)             += leds-mc13783.o
 
 # LED SPI Drivers
 obj-$(CONFIG_LEDS_DAC124S085)          += leds-dac124s085.o
index 69e7d86..2606600 100644 (file)
@@ -74,7 +74,7 @@ static ssize_t led_max_brightness_show(struct device *dev,
 
 static struct device_attribute led_class_attrs[] = {
        __ATTR(brightness, 0644, led_brightness_show, led_brightness_store),
-       __ATTR(max_brightness, 0644, led_max_brightness_show, NULL),
+       __ATTR(max_brightness, 0444, led_max_brightness_show, NULL),
 #ifdef CONFIG_LEDS_TRIGGERS
        __ATTR(trigger, 0644, led_trigger_show, led_trigger_store),
 #endif
index 16a60c0..b767710 100644 (file)
@@ -256,8 +256,10 @@ static int pm860x_led_probe(struct platform_device *pdev)
        if (pdev->dev.parent->platform_data) {
                pm860x_pdata = pdev->dev.parent->platform_data;
                pdata = pm860x_pdata->led;
-       } else
-               pdata = NULL;
+       } else {
+               dev_err(&pdev->dev, "missing platform data\n");
+               return -EINVAL;
+       }
 
        data = kzalloc(sizeof(struct pm860x_led), GFP_KERNEL);
        if (data == NULL)
@@ -268,8 +270,11 @@ static int pm860x_led_probe(struct platform_device *pdev)
        data->i2c = (chip->id == CHIP_PM8606) ? chip->client : chip->companion;
        data->iset = pdata->iset;
        data->port = __check_device(pdata, data->name);
-       if (data->port < 0)
+       if (data->port < 0) {
+               dev_err(&pdev->dev, "check device failed\n");
+               kfree(data);
                return -EINVAL;
+       }
 
        data->current_brightness = 0;
        data->cdev.name = data->name;
index 6d94b0b..cc22eee 100644 (file)
@@ -26,7 +26,8 @@ struct gpio_led_data {
        u8 new_level;
        u8 can_sleep;
        u8 active_low;
-       int (*platform_gpio_blink_set)(unsigned gpio,
+       u8 blinking;
+       int (*platform_gpio_blink_set)(unsigned gpio, int state,
                        unsigned long *delay_on, unsigned long *delay_off);
 };
 
@@ -35,7 +36,13 @@ static void gpio_led_work(struct work_struct *work)
        struct gpio_led_data    *led_dat =
                container_of(work, struct gpio_led_data, work);
 
-       gpio_set_value_cansleep(led_dat->gpio, led_dat->new_level);
+       if (led_dat->blinking) {
+               led_dat->platform_gpio_blink_set(led_dat->gpio,
+                                                led_dat->new_level,
+                                                NULL, NULL);
+               led_dat->blinking = 0;
+       } else
+               gpio_set_value_cansleep(led_dat->gpio, led_dat->new_level);
 }
 
 static void gpio_led_set(struct led_classdev *led_cdev,
@@ -60,8 +67,14 @@ static void gpio_led_set(struct led_classdev *led_cdev,
        if (led_dat->can_sleep) {
                led_dat->new_level = level;
                schedule_work(&led_dat->work);
-       } else
-               gpio_set_value(led_dat->gpio, level);
+       } else {
+               if (led_dat->blinking) {
+                       led_dat->platform_gpio_blink_set(led_dat->gpio, level,
+                                                        NULL, NULL);
+                       led_dat->blinking = 0;
+               } else
+                       gpio_set_value(led_dat->gpio, level);
+       }
 }
 
 static int gpio_blink_set(struct led_classdev *led_cdev,
@@ -70,12 +83,14 @@ static int gpio_blink_set(struct led_classdev *led_cdev,
        struct gpio_led_data *led_dat =
                container_of(led_cdev, struct gpio_led_data, cdev);
 
-       return led_dat->platform_gpio_blink_set(led_dat->gpio, delay_on, delay_off);
+       led_dat->blinking = 1;
+       return led_dat->platform_gpio_blink_set(led_dat->gpio, GPIO_LED_BLINK,
+                                               delay_on, delay_off);
 }
 
 static int __devinit create_gpio_led(const struct gpio_led *template,
        struct gpio_led_data *led_dat, struct device *parent,
-       int (*blink_set)(unsigned, unsigned long *, unsigned long *))
+       int (*blink_set)(unsigned, int, unsigned long *, unsigned long *))
 {
        int ret, state;
 
@@ -97,6 +112,7 @@ static int __devinit create_gpio_led(const struct gpio_led *template,
        led_dat->gpio = template->gpio;
        led_dat->can_sleep = gpio_cansleep(template->gpio);
        led_dat->active_low = template->active_low;
+       led_dat->blinking = 0;
        if (blink_set) {
                led_dat->platform_gpio_blink_set = blink_set;
                led_dat->cdev.blink_set = gpio_blink_set;
@@ -113,7 +129,7 @@ static int __devinit create_gpio_led(const struct gpio_led *template,
        ret = gpio_direction_output(led_dat->gpio, led_dat->active_low ^ state);
        if (ret < 0)
                goto err;
-
+               
        INIT_WORK(&led_dat->work, gpio_led_work);
 
        ret = led_classdev_register(parent, &led_dat->cdev);
index 8d5ecce..932a58d 100644 (file)
@@ -379,6 +379,7 @@ static int __devinit lp3944_probe(struct i2c_client *client,
 {
        struct lp3944_platform_data *lp3944_pdata = client->dev.platform_data;
        struct lp3944_data *data;
+       int err;
 
        if (lp3944_pdata == NULL) {
                dev_err(&client->dev, "no platform data\n");
@@ -401,9 +402,13 @@ static int __devinit lp3944_probe(struct i2c_client *client,
 
        mutex_init(&data->lock);
 
-       dev_info(&client->dev, "lp3944 enabled\n");
+       err = lp3944_configure(client, data, lp3944_pdata);
+       if (err < 0) {
+               kfree(data);
+               return err;
+       }
 
-       lp3944_configure(client, data, lp3944_pdata);
+       dev_info(&client->dev, "lp3944 enabled\n");
        return 0;
 }
 
diff --git a/drivers/leds/leds-mc13783.c b/drivers/leds/leds-mc13783.c
new file mode 100644 (file)
index 0000000..f05bb08
--- /dev/null
@@ -0,0 +1,403 @@
+/*
+ * LEDs driver for Freescale MC13783
+ *
+ * Copyright (C) 2010 Philippe Rétornaz
+ *
+ * Based on leds-da903x:
+ * Copyright (C) 2008 Compulab, Ltd.
+ *      Mike Rapoport <mike@compulab.co.il>
+ *
+ * Copyright (C) 2006-2008 Marvell International Ltd.
+ *      Eric Miao <eric.miao@marvell.com>
+ *
+ * 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/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/leds.h>
+#include <linux/workqueue.h>
+#include <linux/mfd/mc13783.h>
+#include <linux/slab.h>
+
+struct mc13783_led {
+       struct led_classdev     cdev;
+       struct work_struct      work;
+       struct mc13783          *master;
+       enum led_brightness     new_brightness;
+       int                     id;
+};
+
+#define MC13783_REG_LED_CONTROL_0      51
+#define MC13783_LED_C0_ENABLE_BIT      (1 << 0)
+#define MC13783_LED_C0_TRIODE_MD_BIT   (1 << 7)
+#define MC13783_LED_C0_TRIODE_AD_BIT   (1 << 8)
+#define MC13783_LED_C0_TRIODE_KP_BIT   (1 << 9)
+#define MC13783_LED_C0_BOOST_BIT       (1 << 10)
+#define MC13783_LED_C0_ABMODE_MASK     0x7
+#define MC13783_LED_C0_ABMODE          11
+#define MC13783_LED_C0_ABREF_MASK      0x3
+#define MC13783_LED_C0_ABREF           14
+
+#define MC13783_REG_LED_CONTROL_1      52
+#define MC13783_LED_C1_TC1HALF_BIT     (1 << 18)
+
+#define MC13783_REG_LED_CONTROL_2      53
+#define MC13783_LED_C2_BL_P_MASK       0xf
+#define MC13783_LED_C2_MD_P            9
+#define MC13783_LED_C2_AD_P            13
+#define MC13783_LED_C2_KP_P            17
+#define MC13783_LED_C2_BL_C_MASK       0x7
+#define MC13783_LED_C2_MD_C            0
+#define MC13783_LED_C2_AD_C            3
+#define MC13783_LED_C2_KP_C            6
+
+#define MC13783_REG_LED_CONTROL_3      54
+#define MC13783_LED_C3_TC_P            6
+#define MC13783_LED_C3_TC_P_MASK       0x1f
+
+#define MC13783_REG_LED_CONTROL_4      55
+#define MC13783_REG_LED_CONTROL_5      56
+
+#define MC13783_LED_Cx_PERIOD          21
+#define MC13783_LED_Cx_PERIOD_MASK     0x3
+#define MC13783_LED_Cx_SLEWLIM_BIT      (1 << 23)
+#define MC13783_LED_Cx_TRIODE_TC_BIT   (1 << 23)
+#define MC13783_LED_Cx_TC_C_MASK       0x3
+
+static void mc13783_led_work(struct work_struct *work)
+{
+       struct mc13783_led *led = container_of(work, struct mc13783_led, work);
+       int reg = 0;
+       int mask = 0;
+       int value = 0;
+       int bank, off, shift;
+
+       switch (led->id) {
+       case MC13783_LED_MD:
+               reg = MC13783_REG_LED_CONTROL_2;
+               mask = MC13783_LED_C2_BL_P_MASK << MC13783_LED_C2_MD_P;
+               value = (led->new_brightness >> 4) << MC13783_LED_C2_MD_P;
+               break;
+       case MC13783_LED_AD:
+               reg = MC13783_REG_LED_CONTROL_2;
+               mask = MC13783_LED_C2_BL_P_MASK << MC13783_LED_C2_AD_P;
+               value = (led->new_brightness >> 4) << MC13783_LED_C2_AD_P;
+               break;
+       case MC13783_LED_KP:
+               reg = MC13783_REG_LED_CONTROL_2;
+               mask = MC13783_LED_C2_BL_P_MASK << MC13783_LED_C2_KP_P;
+               value = (led->new_brightness >> 4) << MC13783_LED_C2_KP_P;
+               break;
+       case MC13783_LED_R1:
+       case MC13783_LED_G1:
+       case MC13783_LED_B1:
+       case MC13783_LED_R2:
+       case MC13783_LED_G2:
+       case MC13783_LED_B2:
+       case MC13783_LED_R3:
+       case MC13783_LED_G3:
+       case MC13783_LED_B3:
+               off = led->id - MC13783_LED_R1;
+               bank = off/3;
+               reg = MC13783_REG_LED_CONTROL_3 + off/3;
+               shift = (off - bank * 3) * 5 + MC13783_LED_C3_TC_P;
+               value = (led->new_brightness >> 3) << shift;
+               mask = MC13783_LED_C3_TC_P_MASK << shift;
+               break;
+       }
+
+       mc13783_lock(led->master);
+
+       mc13783_reg_rmw(led->master, reg, mask, value);
+
+       mc13783_unlock(led->master);
+}
+
+static void mc13783_led_set(struct led_classdev *led_cdev,
+                          enum led_brightness value)
+{
+       struct mc13783_led *led;
+
+       led = container_of(led_cdev, struct mc13783_led, cdev);
+       led->new_brightness = value;
+       schedule_work(&led->work);
+}
+
+static int __devinit mc13783_led_setup(struct mc13783_led *led, int max_current)
+{
+       int shift = 0;
+       int mask = 0;
+       int value = 0;
+       int reg = 0;
+       int ret, bank;
+
+       switch (led->id) {
+       case MC13783_LED_MD:
+               shift = MC13783_LED_C2_MD_C;
+               mask = MC13783_LED_C2_BL_C_MASK;
+               value = max_current & MC13783_LED_C2_BL_C_MASK;
+               reg = MC13783_REG_LED_CONTROL_2;
+               break;
+       case MC13783_LED_AD:
+               shift = MC13783_LED_C2_AD_C;
+               mask = MC13783_LED_C2_BL_C_MASK;
+               value = max_current & MC13783_LED_C2_BL_C_MASK;
+               reg = MC13783_REG_LED_CONTROL_2;
+               break;
+       case MC13783_LED_KP:
+               shift = MC13783_LED_C2_KP_C;
+               mask = MC13783_LED_C2_BL_C_MASK;
+               value = max_current & MC13783_LED_C2_BL_C_MASK;
+               reg = MC13783_REG_LED_CONTROL_2;
+               break;
+       case MC13783_LED_R1:
+       case MC13783_LED_G1:
+       case MC13783_LED_B1:
+       case MC13783_LED_R2:
+       case MC13783_LED_G2:
+       case MC13783_LED_B2:
+       case MC13783_LED_R3:
+       case MC13783_LED_G3:
+       case MC13783_LED_B3:
+               bank = (led->id - MC13783_LED_R1)/3;
+               reg = MC13783_REG_LED_CONTROL_3 + bank;
+               shift = ((led->id - MC13783_LED_R1) - bank * 3) * 2;
+               mask = MC13783_LED_Cx_TC_C_MASK;
+               value = max_current & MC13783_LED_Cx_TC_C_MASK;
+               break;
+       }
+
+       mc13783_lock(led->master);
+
+       ret = mc13783_reg_rmw(led->master, reg, mask << shift,
+                                               value << shift);
+
+       mc13783_unlock(led->master);
+       return ret;
+}
+
+static int __devinit mc13783_leds_prepare(struct platform_device *pdev)
+{
+       struct mc13783_leds_platform_data *pdata = dev_get_platdata(&pdev->dev);
+       struct mc13783 *dev = dev_get_drvdata(pdev->dev.parent);
+       int ret = 0;
+       int reg = 0;
+
+       mc13783_lock(dev);
+
+       if (pdata->flags & MC13783_LED_TC1HALF)
+               reg |= MC13783_LED_C1_TC1HALF_BIT;
+
+       if (pdata->flags & MC13783_LED_SLEWLIMTC)
+               reg |= MC13783_LED_Cx_SLEWLIM_BIT;
+
+       ret = mc13783_reg_write(dev, MC13783_REG_LED_CONTROL_1, reg);
+       if (ret)
+               goto out;
+
+       reg = (pdata->bl_period & MC13783_LED_Cx_PERIOD_MASK) <<
+                                                       MC13783_LED_Cx_PERIOD;
+
+       if (pdata->flags & MC13783_LED_SLEWLIMBL)
+               reg |= MC13783_LED_Cx_SLEWLIM_BIT;
+
+       ret = mc13783_reg_write(dev, MC13783_REG_LED_CONTROL_2, reg);
+       if (ret)
+               goto out;
+
+       reg = (pdata->tc1_period & MC13783_LED_Cx_PERIOD_MASK) <<
+                                                       MC13783_LED_Cx_PERIOD;
+
+       if (pdata->flags & MC13783_LED_TRIODE_TC1)
+               reg |= MC13783_LED_Cx_TRIODE_TC_BIT;
+
+       ret = mc13783_reg_write(dev, MC13783_REG_LED_CONTROL_3, reg);
+       if (ret)
+               goto out;
+
+       reg = (pdata->tc2_period & MC13783_LED_Cx_PERIOD_MASK) <<
+                                                       MC13783_LED_Cx_PERIOD;
+
+       if (pdata->flags & MC13783_LED_TRIODE_TC2)
+               reg |= MC13783_LED_Cx_TRIODE_TC_BIT;
+
+       ret = mc13783_reg_write(dev, MC13783_REG_LED_CONTROL_4, reg);
+       if (ret)
+               goto out;
+
+       reg = (pdata->tc3_period & MC13783_LED_Cx_PERIOD_MASK) <<
+                                                       MC13783_LED_Cx_PERIOD;
+
+       if (pdata->flags & MC13783_LED_TRIODE_TC3)
+               reg |= MC13783_LED_Cx_TRIODE_TC_BIT;;
+
+       ret = mc13783_reg_write(dev, MC13783_REG_LED_CONTROL_5, reg);
+       if (ret)
+               goto out;
+
+       reg = MC13783_LED_C0_ENABLE_BIT;
+       if (pdata->flags & MC13783_LED_TRIODE_MD)
+               reg |= MC13783_LED_C0_TRIODE_MD_BIT;
+       if (pdata->flags & MC13783_LED_TRIODE_AD)
+               reg |= MC13783_LED_C0_TRIODE_AD_BIT;
+       if (pdata->flags & MC13783_LED_TRIODE_KP)
+               reg |= MC13783_LED_C0_TRIODE_KP_BIT;
+       if (pdata->flags & MC13783_LED_BOOST_EN)
+               reg |= MC13783_LED_C0_BOOST_BIT;
+
+       reg |= (pdata->abmode & MC13783_LED_C0_ABMODE_MASK) <<
+                                                       MC13783_LED_C0_ABMODE;
+       reg |= (pdata->abref & MC13783_LED_C0_ABREF_MASK) <<
+                                                       MC13783_LED_C0_ABREF;
+
+       ret = mc13783_reg_write(dev, MC13783_REG_LED_CONTROL_0, reg);
+
+out:
+       mc13783_unlock(dev);
+       return ret;
+}
+
+static int __devinit mc13783_led_probe(struct platform_device *pdev)
+{
+       struct mc13783_leds_platform_data *pdata = dev_get_platdata(&pdev->dev);
+       struct mc13783_led_platform_data *led_cur;
+       struct mc13783_led *led, *led_dat;
+       int ret, i;
+       int init_led = 0;
+
+       if (pdata == NULL) {
+               dev_err(&pdev->dev, "missing platform data\n");
+               return -ENODEV;
+       }
+
+       if (pdata->num_leds < 1 || pdata->num_leds > MC13783_LED_MAX) {
+               dev_err(&pdev->dev, "Invalid led count %d\n", pdata->num_leds);
+               return -EINVAL;
+       }
+
+       led = kzalloc(sizeof(*led) * pdata->num_leds, GFP_KERNEL);
+       if (led == NULL) {
+               dev_err(&pdev->dev, "failed to alloc memory\n");
+               return -ENOMEM;
+       }
+
+       ret = mc13783_leds_prepare(pdev);
+       if (ret) {
+               dev_err(&pdev->dev, "unable to init led driver\n");
+               goto err_free;
+       }
+
+       for (i = 0; i < pdata->num_leds; i++) {
+               led_dat = &led[i];
+               led_cur = &pdata->led[i];
+
+               if (led_cur->id > MC13783_LED_MAX || led_cur->id < 0) {
+                       dev_err(&pdev->dev, "invalid id %d\n", led_cur->id);
+                       ret = -EINVAL;
+                       goto err_register;
+               }
+
+               if (init_led & (1 << led_cur->id)) {
+                       dev_err(&pdev->dev, "led %d already initialized\n",
+                                       led_cur->id);
+                       ret = -EINVAL;
+                       goto err_register;
+               }
+
+               init_led |= 1 << led_cur->id;
+               led_dat->cdev.name = led_cur->name;
+               led_dat->cdev.default_trigger = led_cur->default_trigger;
+               led_dat->cdev.brightness_set = mc13783_led_set;
+               led_dat->cdev.brightness = LED_OFF;
+               led_dat->id = led_cur->id;
+               led_dat->master = dev_get_drvdata(pdev->dev.parent);
+
+               INIT_WORK(&led_dat->work, mc13783_led_work);
+
+               ret = led_classdev_register(pdev->dev.parent, &led_dat->cdev);
+               if (ret) {
+                       dev_err(&pdev->dev, "failed to register led %d\n",
+                                       led_dat->id);
+                       goto err_register;
+               }
+
+               ret = mc13783_led_setup(led_dat, led_cur->max_current);
+               if (ret) {
+                       dev_err(&pdev->dev, "unable to init led %d\n",
+                                       led_dat->id);
+                       i++;
+                       goto err_register;
+               }
+       }
+
+       platform_set_drvdata(pdev, led);
+       return 0;
+
+err_register:
+       for (i = i - 1; i >= 0; i--) {
+               led_classdev_unregister(&led[i].cdev);
+               cancel_work_sync(&led[i].work);
+       }
+
+err_free:
+       kfree(led);
+       return ret;
+}
+
+static int __devexit mc13783_led_remove(struct platform_device *pdev)
+{
+       struct mc13783_leds_platform_data *pdata = dev_get_platdata(&pdev->dev);
+       struct mc13783_led *led = platform_get_drvdata(pdev);
+       struct mc13783 *dev = dev_get_drvdata(pdev->dev.parent);
+       int i;
+
+       for (i = 0; i < pdata->num_leds; i++) {
+               led_classdev_unregister(&led[i].cdev);
+               cancel_work_sync(&led[i].work);
+       }
+
+       mc13783_lock(dev);
+
+       mc13783_reg_write(dev, MC13783_REG_LED_CONTROL_0, 0);
+       mc13783_reg_write(dev, MC13783_REG_LED_CONTROL_1, 0);
+       mc13783_reg_write(dev, MC13783_REG_LED_CONTROL_2, 0);
+       mc13783_reg_write(dev, MC13783_REG_LED_CONTROL_3, 0);
+       mc13783_reg_write(dev, MC13783_REG_LED_CONTROL_4, 0);
+       mc13783_reg_write(dev, MC13783_REG_LED_CONTROL_5, 0);
+
+       mc13783_unlock(dev);
+
+       kfree(led);
+       return 0;
+}
+
+static struct platform_driver mc13783_led_driver = {
+       .driver = {
+               .name   = "mc13783-led",
+               .owner  = THIS_MODULE,
+       },
+       .probe          = mc13783_led_probe,
+       .remove         = __devexit_p(mc13783_led_remove),
+};
+
+static int __init mc13783_led_init(void)
+{
+       return platform_driver_register(&mc13783_led_driver);
+}
+module_init(mc13783_led_init);
+
+static void __exit mc13783_led_exit(void)
+{
+       platform_driver_unregister(&mc13783_led_driver);
+}
+module_exit(mc13783_led_exit);
+
+MODULE_DESCRIPTION("LEDs driver for Freescale MC13783 PMIC");
+MODULE_AUTHOR("Philippe Retornaz <philippe.retornaz@epfl.ch>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:mc13783-led");
diff --git a/drivers/leds/leds-net5501.c b/drivers/leds/leds-net5501.c
new file mode 100644 (file)
index 0000000..3063f59
--- /dev/null
@@ -0,0 +1,94 @@
+/*
+ * Soekris board support code
+ *
+ * Copyright (C) 2008-2009 Tower Technologies
+ * Written by Alessandro Zummo <a.zummo@towertech.it>
+ *
+ * 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/io.h>
+#include <linux/string.h>
+#include <linux/leds.h>
+#include <linux/platform_device.h>
+#include <linux/gpio.h>
+
+#include <asm/geode.h>
+
+static struct gpio_led net5501_leds[] = {
+       {
+               .name = "error",
+               .gpio = 6,
+               .default_trigger = "default-on",
+       },
+};
+
+static struct gpio_led_platform_data net5501_leds_data = {
+       .num_leds = ARRAY_SIZE(net5501_leds),
+       .leds = net5501_leds,
+};
+
+static struct platform_device net5501_leds_dev = {
+       .name = "leds-gpio",
+       .id = -1,
+       .dev.platform_data = &net5501_leds_data,
+};
+
+static void __init init_net5501(void)
+{
+       platform_device_register(&net5501_leds_dev);
+}
+
+struct soekris_board {
+       u16     offset;
+       char    *sig;
+       u8      len;
+       void    (*init)(void);
+};
+
+static struct soekris_board __initdata boards[] = {
+       { 0xb7b, "net5501", 7, init_net5501 },  /* net5501 v1.33/1.33c */
+       { 0xb1f, "net5501", 7, init_net5501 },  /* net5501 v1.32i */
+};
+
+static int __init soekris_init(void)
+{
+       int i;
+       unsigned char *rombase, *bios;
+
+       if (!is_geode())
+               return 0;
+
+       rombase = ioremap(0xffff0000, 0xffff);
+       if (!rombase) {
+               printk(KERN_INFO "Soekris net5501 LED driver failed to get rombase");
+               return 0;
+       }
+
+       bios = rombase + 0x20;  /* null terminated */
+
+       if (strncmp(bios, "comBIOS", 7))
+               goto unmap;
+
+       for (i = 0; i < ARRAY_SIZE(boards); i++) {
+               unsigned char *model = rombase + boards[i].offset;
+
+               if (strncmp(model, boards[i].sig, boards[i].len) == 0) {
+                       printk(KERN_INFO "Soekris %s: %s\n", model, bios);
+
+                       if (boards[i].init)
+                               boards[i].init();
+                       break;
+               }
+       }
+
+unmap:
+       iounmap(rombase);
+       return 0;
+}
+
+arch_initcall(soekris_init);
index 51477ec..a688293 100644 (file)
@@ -534,7 +534,7 @@ static int __init nas_gpio_init(void)
        set_power_light_amber_noblink();
        return 0;
 out_err:
-       for (; i >= 0; i--)
+       for (i--; i >= 0; i--)
                unregister_nasgpio_led(i);
        pci_unregister_driver(&nas_gpio_pci_driver);
        return ret;
index 405d2d5..2c65a2c 100644 (file)
@@ -566,7 +566,7 @@ out:
        return ret;
 }
 
-static void __devexit device_irq_exit(struct pm860x_chip *chip)
+static void device_irq_exit(struct pm860x_chip *chip)
 {
        if (chip->core_irq)
                free_irq(chip->core_irq, chip);
@@ -703,7 +703,7 @@ out:
        return;
 }
 
-int pm860x_device_init(struct pm860x_chip *chip,
+int __devinit pm860x_device_init(struct pm860x_chip *chip,
                       struct pm860x_platform_data *pdata)
 {
        chip->core_irq = 0;
@@ -731,7 +731,7 @@ int pm860x_device_init(struct pm860x_chip *chip,
        return 0;
 }
 
-void pm860x_device_exit(struct pm860x_chip *chip)
+void __devexit pm860x_device_exit(struct pm860x_chip *chip)
 {
        device_irq_exit(chip);
        mfd_remove_devices(chip->dev);
index 4a6e718..c933b64 100644 (file)
@@ -200,8 +200,8 @@ static int __devexit pm860x_remove(struct i2c_client *client)
 
        pm860x_device_exit(chip);
        i2c_unregister_device(chip->companion);
-       i2c_set_clientdata(chip->companion, NULL);
        i2c_set_clientdata(chip->client, NULL);
+       i2c_set_clientdata(client, NULL);
        kfree(chip);
        return 0;
 }
index 3c6a986..9da0e50 100644 (file)
@@ -2,8 +2,14 @@
 # Multifunction miscellaneous devices
 #
 
-menu "Multifunction device drivers"
+menuconfig MFD_SUPPORT
+       bool "Multifunction device drivers"
        depends on HAS_IOMEM
+       default y
+       help
+         Configure MFD device drivers.
+
+if MFD_SUPPORT
 
 config MFD_CORE
        tristate
@@ -116,6 +122,18 @@ config TPS65010
          This driver can also be built as a module.  If so, the module
          will be called tps65010.
 
+config TPS6507X
+       tristate "TPS6507x Power Management / Touch Screen chips"
+       select MFD_CORE
+       depends on I2C
+       help
+         If you say yes here you get support for the TPS6507x series of
+         Power Management / Touch Screen chips.  These include voltage
+         regulators, lithium ion/polymer battery charging, touch screen
+         and other features that are often used in portable devices.
+         This driver can also be built as a module.  If so, the module
+         will be called tps6507x.
+
 config MENELAUS
        bool "Texas Instruments TWL92330/Menelaus PM chip"
        depends on I2C=y && ARCH_OMAP2
@@ -159,6 +177,17 @@ config TWL4030_CODEC
        select MFD_CORE
        default n
 
+config MFD_TC35892
+       bool "Support Toshiba TC35892"
+       depends on I2C=y && GENERIC_HARDIRQS
+       select MFD_CORE
+       help
+         Support for the Toshiba TC35892 I/O Expander.
+
+         This driver provides common support for accessing the device,
+         additional drivers must be enabled in order to use the
+         functionality of the device.
+
 config MFD_TMIO
        bool
        default n
@@ -351,9 +380,19 @@ config PCF50633_GPIO
         Say yes here if you want to include support GPIO for pins on
         the PCF50633 chip.
 
+config ABX500_CORE
+       bool "ST-Ericsson ABX500 Mixed Signal Circuit register functions"
+       default y if ARCH_U300
+       help
+         Say yes here if you have the ABX500 Mixed Signal IC family
+         chips. This core driver expose register access functions.
+         Functionality specific drivers using these functions can
+         remain unchanged when IC changes. Binding of the functions to
+         actual register access is done by the IC core driver.
+
 config AB3100_CORE
        bool "ST-Ericsson AB3100 Mixed Signal Circuit core functions"
-       depends on I2C=y
+       depends on I2C=y && ABX500_CORE
        default y if ARCH_U300
        help
          Select this to enable the AB3100 Mixed Signal IC core
@@ -381,15 +420,30 @@ config EZX_PCAP
          This enables the PCAP ASIC present on EZX Phones. This is
          needed for MMC, TouchScreen, Sound, USB, etc..
 
-config AB4500_CORE
-       tristate "ST-Ericsson's AB4500 Mixed Signal Power management chip"
-       depends on SPI
+config AB8500_CORE
+       bool "ST-Ericsson AB8500 Mixed Signal Power Management chip"
+       depends on SPI=y && GENERIC_HARDIRQS
+       select MFD_CORE
        help
-         Select this option to enable access to AB4500 power management
+         Select this option to enable access to AB8500 power management
          chip. This connects to U8500 on the SSP/SPI bus and exports
          read/write functions for the devices to get access to this chip.
          This chip embeds various other multimedia funtionalities as well.
 
+config AB3550_CORE
+        bool "ST-Ericsson AB3550 Mixed Signal Circuit core functions"
+       select MFD_CORE
+       depends on I2C=y && GENERIC_HARDIRQS && ABX500_CORE
+       help
+         Select this to enable the AB3550 Mixed Signal IC core
+         functionality. This connects to a AB3550 on the I2C bus
+         and expose a number of symbols needed for dependent devices
+         to read and write registers and subscribe to events from
+         this multi-functional IC. This is needed to use other features
+         of the AB3550 such as battery-backed RTC, charging control,
+         LEDs, vibrator, system power and temperature, power management
+         and ALSA sound.
+
 config MFD_TIMBERDALE
        tristate "Support for the Timberdale FPGA"
        select MFD_CORE
@@ -409,7 +463,26 @@ config LPC_SCH
          LPC bridge function of the Intel SCH provides support for
          System Management Bus and General Purpose I/O.
 
-endmenu
+config MFD_RDC321X
+       tristate "Support for RDC-R321x southbridge"
+       select MFD_CORE
+       depends on PCI
+       help
+         Say yes here if you want to have support for the RDC R-321x SoC
+         southbridge which provides access to GPIOs and Watchdog using the
+         southbridge PCI device configuration space.
+
+config MFD_JANZ_CMODIO
+       tristate "Support for Janz CMOD-IO PCI MODULbus Carrier Board"
+       select MFD_CORE
+       depends on PCI
+       help
+         This is the core driver for the Janz CMOD-IO PCI MODULbus
+         carrier board. This device is a PCI to MODULbus bridge which may
+         host many different types of MODULbus daughterboards, including
+         CAN and GPIO controllers.
+
+endif # MFD_SUPPORT
 
 menu "Multimedia Capabilities Port drivers"
        depends on ARCH_SA1100
index 87935f9..fb503e7 100644 (file)
@@ -15,6 +15,7 @@ obj-$(CONFIG_HTC_I2CPLD)      += htc-i2cpld.o
 obj-$(CONFIG_MFD_DAVINCI_VOICECODEC)   += davinci_voicecodec.o
 obj-$(CONFIG_MFD_DM355EVM_MSP) += dm355evm_msp.o
 
+obj-$(CONFIG_MFD_TC35892)      += tc35892.o
 obj-$(CONFIG_MFD_T7L66XB)      += t7l66xb.o tmio_core.o
 obj-$(CONFIG_MFD_TC6387XB)     += tc6387xb.o tmio_core.o
 obj-$(CONFIG_MFD_TC6393XB)     += tc6393xb.o tmio_core.o
@@ -29,6 +30,7 @@ obj-$(CONFIG_MFD_WM8350_I2C)  += wm8350-i2c.o
 obj-$(CONFIG_MFD_WM8994)       += wm8994-core.o wm8994-irq.o
 
 obj-$(CONFIG_TPS65010)         += tps65010.o
+obj-$(CONFIG_TPS6507X)         += tps6507x.o
 obj-$(CONFIG_MENELAUS)         += menelaus.o
 
 obj-$(CONFIG_TWL4030_CORE)     += twl-core.o twl4030-irq.o twl6030-irq.o
@@ -55,12 +57,17 @@ obj-$(CONFIG_PMIC_DA903X)   += da903x.o
 max8925-objs                   := max8925-core.o max8925-i2c.o
 obj-$(CONFIG_MFD_MAX8925)      += max8925.o
 
-obj-$(CONFIG_MFD_PCF50633)     += pcf50633-core.o
+pcf50633-objs                  := pcf50633-core.o pcf50633-irq.o
+obj-$(CONFIG_MFD_PCF50633)     += pcf50633.o
 obj-$(CONFIG_PCF50633_ADC)     += pcf50633-adc.o
 obj-$(CONFIG_PCF50633_GPIO)    += pcf50633-gpio.o
+obj-$(CONFIG_ABX500_CORE)      += abx500-core.o
 obj-$(CONFIG_AB3100_CORE)      += ab3100-core.o
 obj-$(CONFIG_AB3100_OTP)       += ab3100-otp.o
-obj-$(CONFIG_AB4500_CORE)      += ab4500-core.o
+obj-$(CONFIG_AB3550_CORE)      += ab3550-core.o
+obj-$(CONFIG_AB8500_CORE)      += ab8500-core.o ab8500-spi.o
 obj-$(CONFIG_MFD_TIMBERDALE)    += timberdale.o
 obj-$(CONFIG_PMIC_ADP5520)     += adp5520.o
-obj-$(CONFIG_LPC_SCH)          += lpc_sch.o
\ No newline at end of file
+obj-$(CONFIG_LPC_SCH)          += lpc_sch.o
+obj-$(CONFIG_MFD_RDC321X)      += rdc321x-southbridge.o
+obj-$(CONFIG_MFD_JANZ_CMODIO)  += janz-cmodio.o
index e4ca590..53ebfee 100644 (file)
@@ -19,7 +19,7 @@
 #include <linux/debugfs.h>
 #include <linux/seq_file.h>
 #include <linux/uaccess.h>
-#include <linux/mfd/ab3100.h>
+#include <linux/mfd/abx500.h>
 
 /* These are the only registers inside AB3100 used in this main file */
 
  * The AB3100 is usually assigned address 0x48 (7-bit)
  * The chip is defined in the platform i2c_board_data section.
  */
-
-u8 ab3100_get_chip_type(struct ab3100 *ab3100)
+static int ab3100_get_chip_id(struct device *dev)
 {
-       u8 chip = ABUNKNOWN;
-
-       switch (ab3100->chip_id & 0xf0) {
-       case  0xa0:
-               chip = AB3000;
-               break;
-       case  0xc0:
-               chip = AB3100;
-               break;
-       }
-       return chip;
+       struct ab3100 *ab3100 = dev_get_drvdata(dev->parent);
+
+       return (int)ab3100->chip_id;
 }
-EXPORT_SYMBOL(ab3100_get_chip_type);
 
-int ab3100_set_register_interruptible(struct ab3100 *ab3100, u8 reg, u8 regval)
+static int ab3100_set_register_interruptible(struct ab3100 *ab3100,
+       u8 reg, u8 regval)
 {
        u8 regandval[2] = {reg, regval};
        int err;
@@ -108,8 +99,14 @@ int ab3100_set_register_interruptible(struct ab3100 *ab3100, u8 reg, u8 regval)
        mutex_unlock(&ab3100->access_mutex);
        return err;
 }
-EXPORT_SYMBOL(ab3100_set_register_interruptible);
 
+static int set_register_interruptible(struct device *dev,
+       u8 bank, u8 reg, u8 value)
+{
+       struct ab3100 *ab3100 = dev_get_drvdata(dev->parent);
+
+       return ab3100_set_register_interruptible(ab3100, reg, value);
+}
 
 /*
  * The test registers exist at an I2C bus address up one
@@ -148,8 +145,8 @@ static int ab3100_set_test_register_interruptible(struct ab3100 *ab3100,
        return err;
 }
 
-
-int ab3100_get_register_interruptible(struct ab3100 *ab3100, u8 reg, u8 *regval)
+static int ab3100_get_register_interruptible(struct ab3100 *ab3100,
+       u8 reg, u8 *regval)
 {
        int err;
 
@@ -203,10 +200,16 @@ int ab3100_get_register_interruptible(struct ab3100 *ab3100, u8 reg, u8 *regval)
        mutex_unlock(&ab3100->access_mutex);
        return err;
 }
-EXPORT_SYMBOL(ab3100_get_register_interruptible);
 
+static int get_register_interruptible(struct device *dev, u8 bank, u8 reg,
+       u8 *value)
+{
+       struct ab3100 *ab3100 = dev_get_drvdata(dev->parent);
+
+       return ab3100_get_register_interruptible(ab3100, reg, value);
+}
 
-int ab3100_get_register_page_interruptible(struct ab3100 *ab3100,
+static int ab3100_get_register_page_interruptible(struct ab3100 *ab3100,
                             u8 first_reg, u8 *regvals, u8 numregs)
 {
        int err;
@@ -260,10 +263,17 @@ int ab3100_get_register_page_interruptible(struct ab3100 *ab3100,
        mutex_unlock(&ab3100->access_mutex);
        return err;
 }
-EXPORT_SYMBOL(ab3100_get_register_page_interruptible);
 
+static int get_register_page_interruptible(struct device *dev, u8 bank,
+       u8 first_reg, u8 *regvals, u8 numregs)
+{
+       struct ab3100 *ab3100 = dev_get_drvdata(dev->parent);
+
+       return ab3100_get_register_page_interruptible(ab3100,
+                       first_reg, regvals, numregs);
+}
 
-int ab3100_mask_and_set_register_interruptible(struct ab3100 *ab3100,
+static int ab3100_mask_and_set_register_interruptible(struct ab3100 *ab3100,
                                 u8 reg, u8 andmask, u8 ormask)
 {
        u8 regandval[2] = {reg, 0};
@@ -331,8 +341,15 @@ int ab3100_mask_and_set_register_interruptible(struct ab3100 *ab3100,
        mutex_unlock(&ab3100->access_mutex);
        return err;
 }
-EXPORT_SYMBOL(ab3100_mask_and_set_register_interruptible);
 
+static int mask_and_set_register_interruptible(struct device *dev, u8 bank,
+       u8 reg, u8 bitmask, u8 bitvalues)
+{
+       struct ab3100 *ab3100 = dev_get_drvdata(dev->parent);
+
+       return ab3100_mask_and_set_register_interruptible(ab3100,
+                       reg, bitmask, (bitmask & bitvalues));
+}
 
 /*
  * Register a simple callback for handling any AB3100 events.
@@ -357,15 +374,27 @@ int ab3100_event_unregister(struct ab3100 *ab3100,
 EXPORT_SYMBOL(ab3100_event_unregister);
 
 
-int ab3100_event_registers_startup_state_get(struct ab3100 *ab3100,
-                                            u32 *fatevent)
+static int ab3100_event_registers_startup_state_get(struct device *dev,
+                                            u8 *event)
 {
+       struct ab3100 *ab3100 = dev_get_drvdata(dev->parent);
        if (!ab3100->startup_events_read)
                return -EAGAIN; /* Try again later */
-       *fatevent = ab3100->startup_events;
+       memcpy(event, ab3100->startup_events, 3);
        return 0;
 }
-EXPORT_SYMBOL(ab3100_event_registers_startup_state_get);
+
+static struct abx500_ops ab3100_ops = {
+       .get_chip_id = ab3100_get_chip_id,
+       .set_register = set_register_interruptible,
+       .get_register = get_register_interruptible,
+       .get_register_page = get_register_page_interruptible,
+       .set_register_page = NULL,
+       .mask_and_set_register = mask_and_set_register_interruptible,
+       .event_registers_startup_state_get =
+               ab3100_event_registers_startup_state_get,
+       .startup_irq_enabled = NULL,
+};
 
 /*
  * This is a threaded interrupt handler so we can make some
@@ -390,7 +419,9 @@ static irqreturn_t ab3100_irq_handler(int irq, void *data)
                event_regs[2];
 
        if (!ab3100->startup_events_read) {
-               ab3100->startup_events = fatevent;
+               ab3100->startup_events[0] = event_regs[0];
+               ab3100->startup_events[1] = event_regs[1];
+               ab3100->startup_events[2] = event_regs[2];
                ab3100->startup_events_read = true;
        }
        /*
@@ -703,7 +734,8 @@ static int __init ab3100_setup(struct ab3100 *ab3100)
                dev_warn(ab3100->dev,
                         "AB3100 P1E variant detected, "
                         "forcing chip to 32KHz\n");
-               err = ab3100_set_test_register_interruptible(ab3100, 0x02, 0x08);
+               err = ab3100_set_test_register_interruptible(ab3100,
+                       0x02, 0x08);
        }
 
  exit_no_setup:
@@ -898,6 +930,10 @@ static int __init ab3100_probe(struct i2c_client *client,
        if (err)
                goto exit_no_irq;
 
+       err = abx500_register_ops(&client->dev, &ab3100_ops);
+       if (err)
+               goto exit_no_ops;
+
        /* Set parent and a pointer back to the container in device data */
        for (i = 0; i < ARRAY_SIZE(ab3100_platform_devs); i++) {
                ab3100_platform_devs[i]->dev.parent =
@@ -915,11 +951,13 @@ static int __init ab3100_probe(struct i2c_client *client,
 
        return 0;
 
+ exit_no_ops:
  exit_no_irq:
  exit_no_setup:
        i2c_unregister_device(ab3100->testreg_client);
  exit_no_testreg_client:
  exit_no_detect:
+       i2c_set_clientdata(client, NULL);
        kfree(ab3100);
        return err;
 }
@@ -941,6 +979,7 @@ static int __exit ab3100_remove(struct i2c_client *client)
         * their notifiers so deactivate IRQ
         */
        free_irq(client->irq, ab3100);
+       i2c_set_clientdata(client, NULL);
        kfree(ab3100);
        return 0;
 }
index 2d14655..63d2b72 100644 (file)
@@ -12,7 +12,7 @@
 #include <linux/slab.h>
 #include <linux/init.h>
 #include <linux/platform_device.h>
-#include <linux/mfd/ab3100.h>
+#include <linux/mfd/abx500.h>
 #include <linux/debugfs.h>
 #include <linux/seq_file.h>
 
@@ -30,7 +30,6 @@
 /**
  * struct ab3100_otp
  * @dev containing device
- * @ab3100 a pointer to the parent ab3100 device struct
  * @locked whether the OTP is locked, after locking, no more bits
  *       can be changed but before locking it is still possible
  *       to change bits from 1->0.
@@ -49,7 +48,6 @@
  */
 struct ab3100_otp {
        struct device *dev;
-       struct ab3100 *ab3100;
        bool locked;
        u32 freq;
        bool paf;
@@ -63,19 +61,19 @@ struct ab3100_otp {
 
 static int __init ab3100_otp_read(struct ab3100_otp *otp)
 {
-       struct ab3100 *ab = otp->ab3100;
        u8 otpval[8];
        u8 otpp;
        int err;
 
-       err = ab3100_get_register_interruptible(ab, AB3100_OTPP, &otpp);
+       err = abx500_get_register_interruptible(otp->dev, 0,
+               AB3100_OTPP, &otpp);
        if (err) {
                dev_err(otp->dev, "unable to read OTPP register\n");
                return err;
        }
 
-       err = ab3100_get_register_page_interruptible(ab, AB3100_OTP0,
-                                                    otpval, 8);
+       err = abx500_get_register_page_interruptible(otp->dev, 0,
+               AB3100_OTP0, otpval, 8);
        if (err) {
                dev_err(otp->dev, "unable to read OTP register page\n");
                return err;
@@ -197,7 +195,6 @@ static int __init ab3100_otp_probe(struct platform_device *pdev)
        otp->dev = &pdev->dev;
 
        /* Replace platform data coming in with a local struct */
-       otp->ab3100 = platform_get_drvdata(pdev);
        platform_set_drvdata(pdev, otp);
 
        err = ab3100_otp_read(otp);
diff --git a/drivers/mfd/ab3550-core.c b/drivers/mfd/ab3550-core.c
new file mode 100644 (file)
index 0000000..1060f8e
--- /dev/null
@@ -0,0 +1,1401 @@
+/*
+ * Copyright (C) 2007-2010 ST-Ericsson
+ * License terms: GNU General Public License (GPL) version 2
+ * Low-level core for exclusive access to the AB3550 IC on the I2C bus
+ * and some basic chip-configuration.
+ * Author: Bengt Jonsson <bengt.g.jonsson@stericsson.com>
+ * Author: Mattias Nilsson <mattias.i.nilsson@stericsson.com>
+ * Author: Mattias Wallin <mattias.wallin@stericsson.com>
+ * Author: Rickard Andersson <rickard.andersson@stericsson.com>
+ */
+
+#include <linux/i2c.h>
+#include <linux/mutex.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/device.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/random.h>
+#include <linux/workqueue.h>
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+#include <linux/uaccess.h>
+#include <linux/mfd/abx500.h>
+#include <linux/list.h>
+#include <linux/bitops.h>
+#include <linux/spinlock.h>
+#include <linux/mfd/core.h>
+
+#define AB3550_NAME_STRING "ab3550"
+#define AB3550_ID_FORMAT_STRING "AB3550 %s"
+#define AB3550_NUM_BANKS 2
+#define AB3550_NUM_EVENT_REG 5
+
+/* These are the only registers inside AB3550 used in this main file */
+
+/* Chip ID register */
+#define AB3550_CID_REG           0x20
+
+/* Interrupt event registers */
+#define AB3550_EVENT_BANK        0
+#define AB3550_EVENT_REG         0x22
+
+/* Read/write operation values. */
+#define AB3550_PERM_RD (0x01)
+#define AB3550_PERM_WR (0x02)
+
+/* Read/write permissions. */
+#define AB3550_PERM_RO (AB3550_PERM_RD)
+#define AB3550_PERM_RW (AB3550_PERM_RD | AB3550_PERM_WR)
+
+/**
+ * struct ab3550
+ * @access_mutex: lock out concurrent accesses to the AB registers
+ * @i2c_client: I2C client for this chip
+ * @chip_name: name of this chip variant
+ * @chip_id: 8 bit chip ID for this chip variant
+ * @mask_work: a worker for writing to mask registers
+ * @event_lock: a lock to protect the event_mask
+ * @event_mask: a local copy of the mask event registers
+ * @startup_events: a copy of the first reading of the event registers
+ * @startup_events_read: whether the first events have been read
+ */
+struct ab3550 {
+       struct mutex access_mutex;
+       struct i2c_client *i2c_client[AB3550_NUM_BANKS];
+       char chip_name[32];
+       u8 chip_id;
+       struct work_struct mask_work;
+       spinlock_t event_lock;
+       u8 event_mask[AB3550_NUM_EVENT_REG];
+       u8 startup_events[AB3550_NUM_EVENT_REG];
+       bool startup_events_read;
+#ifdef CONFIG_DEBUG_FS
+       unsigned int debug_bank;
+       unsigned int debug_address;
+#endif
+};
+
+/**
+ * struct ab3550_reg_range
+ * @first: the first address of the range
+ * @last: the last address of the range
+ * @perm: access permissions for the range
+ */
+struct ab3550_reg_range {
+       u8 first;
+       u8 last;
+       u8 perm;
+};
+
+/**
+ * struct ab3550_reg_ranges
+ * @count: the number of ranges in the list
+ * @range: the list of register ranges
+ */
+struct ab3550_reg_ranges {
+       u8 count;
+       const struct ab3550_reg_range *range;
+};
+
+/*
+ * Permissible register ranges for reading and writing per device and bank.
+ *
+ * The ranges must be listed in increasing address order, and no overlaps are
+ * allowed. It is assumed that write permission implies read permission
+ * (i.e. only RO and RW permissions should be used).  Ranges with write
+ * permission must not be split up.
+ */
+
+#define NO_RANGE {.count = 0, .range = NULL,}
+
+static struct
+ab3550_reg_ranges ab3550_reg_ranges[AB3550_NUM_DEVICES][AB3550_NUM_BANKS] = {
+       [AB3550_DEVID_DAC] = {
+               NO_RANGE,
+               {
+                       .count = 2,
+                       .range = (struct ab3550_reg_range[]) {
+                               {
+                                       .first = 0xb0,
+                                       .last = 0xba,
+                                       .perm = AB3550_PERM_RW,
+                               },
+                               {
+                                       .first = 0xbc,
+                                       .last = 0xc3,
+                                       .perm = AB3550_PERM_RW,
+                               },
+                       },
+               },
+       },
+       [AB3550_DEVID_LEDS] = {
+               NO_RANGE,
+               {
+                       .count = 2,
+                       .range = (struct ab3550_reg_range[]) {
+                               {
+                                       .first = 0x5a,
+                                       .last = 0x88,
+                                       .perm = AB3550_PERM_RW,
+                               },
+                               {
+                                       .first = 0x8a,
+                                       .last = 0xad,
+                                       .perm = AB3550_PERM_RW,
+                               },
+                       }
+               },
+       },
+       [AB3550_DEVID_POWER] = {
+               {
+                       .count = 1,
+                       .range = (struct ab3550_reg_range[]) {
+                               {
+                                       .first = 0x21,
+                                       .last = 0x21,
+                                       .perm = AB3550_PERM_RO,
+                               },
+                       }
+               },
+               NO_RANGE,
+       },
+       [AB3550_DEVID_REGULATORS] = {
+               {
+                       .count = 1,
+                       .range = (struct ab3550_reg_range[]) {
+                               {
+                                       .first = 0x69,
+                                       .last = 0xa3,
+                                       .perm = AB3550_PERM_RW,
+                               },
+                       }
+               },
+               {
+                       .count = 1,
+                       .range = (struct ab3550_reg_range[]) {
+                               {
+                                       .first = 0x14,
+                                       .last = 0x16,
+                                       .perm = AB3550_PERM_RW,
+                               },
+                       }
+               },
+       },
+       [AB3550_DEVID_SIM] = {
+               {
+                       .count = 1,
+                       .range = (struct ab3550_reg_range[]) {
+                               {
+                                       .first = 0x21,
+                                       .last = 0x21,
+                                       .perm = AB3550_PERM_RO,
+                               },
+                       }
+               },
+               {
+                       .count = 1,
+                       .range = (struct ab3550_reg_range[]) {
+                               {
+                                       .first = 0x14,
+                                       .last = 0x17,
+                                       .perm = AB3550_PERM_RW,
+                               },
+                       }
+
+               },
+       },
+       [AB3550_DEVID_UART] = {
+               NO_RANGE,
+               NO_RANGE,
+       },
+       [AB3550_DEVID_RTC] = {
+               {
+                       .count = 1,
+                       .range = (struct ab3550_reg_range[]) {
+                               {
+                                       .first = 0x00,
+                                       .last = 0x0c,
+                                       .perm = AB3550_PERM_RW,
+                               },
+                       }
+               },
+               NO_RANGE,
+       },
+       [AB3550_DEVID_CHARGER] = {
+               {
+                       .count = 2,
+                       .range = (struct ab3550_reg_range[]) {
+                               {
+                                       .first = 0x10,
+                                       .last = 0x1a,
+                                       .perm = AB3550_PERM_RW,
+                               },
+                               {
+                                       .first = 0x21,
+                                       .last = 0x21,
+                                       .perm = AB3550_PERM_RO,
+                               },
+                       }
+               },
+               NO_RANGE,
+       },
+       [AB3550_DEVID_ADC] = {
+               NO_RANGE,
+               {
+                       .count = 1,
+                       .range = (struct ab3550_reg_range[]) {
+                               {
+                                       .first = 0x20,
+                                       .last = 0x56,
+                                       .perm = AB3550_PERM_RW,
+                               },
+
+                       }
+               },
+       },
+       [AB3550_DEVID_FUELGAUGE] = {
+               {
+                       .count = 1,
+                       .range = (struct ab3550_reg_range[]) {
+                               {
+                                       .first = 0x21,
+                                       .last = 0x21,
+                                       .perm = AB3550_PERM_RO,
+                               },
+                       }
+               },
+               {
+                       .count = 1,
+                       .range = (struct ab3550_reg_range[]) {
+                               {
+                                       .first = 0x00,
+                                       .last = 0x0e,
+                                       .perm = AB3550_PERM_RW,
+                               },
+                       }
+               },
+       },
+       [AB3550_DEVID_VIBRATOR] = {
+               NO_RANGE,
+               {
+                       .count = 1,
+                       .range = (struct ab3550_reg_range[]) {
+                               {
+                                       .first = 0x10,
+                                       .last = 0x13,
+                                       .perm = AB3550_PERM_RW,
+                               },
+
+                       }
+               },
+       },
+       [AB3550_DEVID_CODEC] = {
+               {
+                       .count = 2,
+                       .range = (struct ab3550_reg_range[]) {
+                               {
+                                       .first = 0x31,
+                                       .last = 0x63,
+                                       .perm = AB3550_PERM_RW,
+                               },
+                               {
+                                       .first = 0x65,
+                                       .last = 0x68,
+                                       .perm = AB3550_PERM_RW,
+                               },
+                       }
+               },
+               NO_RANGE,
+       },
+};
+
+static struct mfd_cell ab3550_devs[AB3550_NUM_DEVICES] = {
+       [AB3550_DEVID_DAC] = {
+               .name = "ab3550-dac",
+               .id = AB3550_DEVID_DAC,
+               .num_resources = 0,
+       },
+       [AB3550_DEVID_LEDS] = {
+               .name = "ab3550-leds",
+               .id = AB3550_DEVID_LEDS,
+       },
+       [AB3550_DEVID_POWER] = {
+               .name = "ab3550-power",
+               .id = AB3550_DEVID_POWER,
+       },
+       [AB3550_DEVID_REGULATORS] = {
+               .name = "ab3550-regulators",
+               .id = AB3550_DEVID_REGULATORS,
+       },
+       [AB3550_DEVID_SIM] = {
+               .name = "ab3550-sim",
+               .id = AB3550_DEVID_SIM,
+       },
+       [AB3550_DEVID_UART] = {
+               .name = "ab3550-uart",
+               .id = AB3550_DEVID_UART,
+       },
+       [AB3550_DEVID_RTC] = {
+               .name = "ab3550-rtc",
+               .id = AB3550_DEVID_RTC,
+       },
+       [AB3550_DEVID_CHARGER] = {
+               .name = "ab3550-charger",
+               .id = AB3550_DEVID_CHARGER,
+       },
+       [AB3550_DEVID_ADC] = {
+               .name = "ab3550-adc",
+               .id = AB3550_DEVID_ADC,
+               .num_resources = 10,
+               .resources = (struct resource[]) {
+                       {
+                               .name = "TRIGGER-0",
+                               .flags = IORESOURCE_IRQ,
+                               .start = 16,
+                               .end = 16,
+                       },
+                       {
+                               .name = "TRIGGER-1",
+                               .flags = IORESOURCE_IRQ,
+                               .start = 17,
+                               .end = 17,
+                       },
+                       {
+                               .name = "TRIGGER-2",
+                               .flags = IORESOURCE_IRQ,
+                               .start = 18,
+                               .end = 18,
+                       },
+                       {
+                               .name = "TRIGGER-3",
+                               .flags = IORESOURCE_IRQ,
+                               .start = 19,
+                               .end = 19,
+                       },
+                       {
+                               .name = "TRIGGER-4",
+                               .flags = IORESOURCE_IRQ,
+                               .start = 20,
+                               .end = 20,
+                       },
+                       {
+                               .name = "TRIGGER-5",
+                               .flags = IORESOURCE_IRQ,
+                               .start = 21,
+                               .end = 21,
+                       },
+                       {
+                               .name = "TRIGGER-6",
+                               .flags = IORESOURCE_IRQ,
+                               .start = 22,
+                               .end = 22,
+                       },
+                       {
+                               .name = "TRIGGER-7",
+                               .flags = IORESOURCE_IRQ,
+                               .start = 23,
+                               .end = 23,
+                       },
+                       {
+                               .name = "TRIGGER-VBAT-TXON",
+                               .flags = IORESOURCE_IRQ,
+                               .start = 13,
+                               .end = 13,
+                       },
+                       {
+                               .name = "TRIGGER-VBAT",
+                               .flags = IORESOURCE_IRQ,
+                               .start = 12,
+                               .end = 12,
+                       },
+               },
+       },
+       [AB3550_DEVID_FUELGAUGE] = {
+               .name = "ab3550-fuelgauge",
+               .id = AB3550_DEVID_FUELGAUGE,
+       },
+       [AB3550_DEVID_VIBRATOR] = {
+               .name = "ab3550-vibrator",
+               .id = AB3550_DEVID_VIBRATOR,
+       },
+       [AB3550_DEVID_CODEC] = {
+               .name = "ab3550-codec",
+               .id = AB3550_DEVID_CODEC,
+       },
+};
+
+/*
+ * I2C transactions with error messages.
+ */
+static int ab3550_i2c_master_send(struct ab3550 *ab, u8 bank, u8 *data,
+       u8 count)
+{
+       int err;
+
+       err = i2c_master_send(ab->i2c_client[bank], data, count);
+       if (err < 0) {
+               dev_err(&ab->i2c_client[0]->dev, "send error: %d\n", err);
+               return err;
+       }
+       return 0;
+}
+
+static int ab3550_i2c_master_recv(struct ab3550 *ab, u8 bank, u8 *data,
+       u8 count)
+{
+       int err;
+
+       err = i2c_master_recv(ab->i2c_client[bank], data, count);
+       if (err < 0) {
+               dev_err(&ab->i2c_client[0]->dev, "receive error: %d\n", err);
+               return err;
+       }
+       return 0;
+}
+
+/*
+ * Functionality for getting/setting register values.
+ */
+static int get_register_interruptible(struct ab3550 *ab, u8 bank, u8 reg,
+       u8 *value)
+{
+       int err;
+
+       err = mutex_lock_interruptible(&ab->access_mutex);
+       if (err)
+               return err;
+
+       err = ab3550_i2c_master_send(ab, bank, &reg, 1);
+       if (!err)
+               err = ab3550_i2c_master_recv(ab, bank, value, 1);
+
+       mutex_unlock(&ab->access_mutex);
+       return err;
+}
+
+static int get_register_page_interruptible(struct ab3550 *ab, u8 bank,
+       u8 first_reg, u8 *regvals, u8 numregs)
+{
+       int err;
+
+       err = mutex_lock_interruptible(&ab->access_mutex);
+       if (err)
+               return err;
+
+       err = ab3550_i2c_master_send(ab, bank, &first_reg, 1);
+       if (!err)
+               err = ab3550_i2c_master_recv(ab, bank, regvals, numregs);
+
+       mutex_unlock(&ab->access_mutex);
+       return err;
+}
+
+static int mask_and_set_register_interruptible(struct ab3550 *ab, u8 bank,
+       u8 reg, u8 bitmask, u8 bitvalues)
+{
+       int err = 0;
+
+       if (likely(bitmask)) {
+               u8 reg_bits[2] = {reg, 0};
+
+               err = mutex_lock_interruptible(&ab->access_mutex);
+               if (err)
+                       return err;
+
+               if (bitmask == 0xFF) /* No need to read in this case. */
+                       reg_bits[1] = bitvalues;
+               else { /* Read and modify the register value. */
+                       u8 bits;
+
+                       err = ab3550_i2c_master_send(ab, bank, &reg, 1);
+                       if (err)
+                               goto unlock_and_return;
+                       err = ab3550_i2c_master_recv(ab, bank, &bits, 1);
+                       if (err)
+                               goto unlock_and_return;
+                       reg_bits[1] = ((~bitmask & bits) |
+                               (bitmask & bitvalues));
+               }
+               /* Write the new value. */
+               err = ab3550_i2c_master_send(ab, bank, reg_bits, 2);
+unlock_and_return:
+               mutex_unlock(&ab->access_mutex);
+       }
+       return err;
+}
+
+/*
+ * Read/write permission checking functions.
+ */
+static bool page_write_allowed(const struct ab3550_reg_ranges *ranges,
+       u8 first_reg, u8 last_reg)
+{
+       u8 i;
+
+       if (last_reg < first_reg)
+               return false;
+
+       for (i = 0; i < ranges->count; i++) {
+               if (first_reg < ranges->range[i].first)
+                       break;
+               if ((last_reg <= ranges->range[i].last) &&
+                       (ranges->range[i].perm & AB3550_PERM_WR))
+                       return true;
+       }
+       return false;
+}
+
+static bool reg_write_allowed(const struct ab3550_reg_ranges *ranges, u8 reg)
+{
+       return page_write_allowed(ranges, reg, reg);
+}
+
+static bool page_read_allowed(const struct ab3550_reg_ranges *ranges,
+       u8 first_reg, u8 last_reg)
+{
+       u8 i;
+
+       if (last_reg < first_reg)
+               return false;
+       /* Find the range (if it exists in the list) that includes first_reg. */
+       for (i = 0; i < ranges->count; i++) {
+               if (first_reg < ranges->range[i].first)
+                       return false;
+               if (first_reg <= ranges->range[i].last)
+                       break;
+       }
+       /* Make sure that the entire range up to and including last_reg is
+        * readable. This may span several of the ranges in the list.
+        */
+       while ((i < ranges->count) &&
+               (ranges->range[i].perm & AB3550_PERM_RD)) {
+               if (last_reg <= ranges->range[i].last)
+                       return true;
+               if ((++i >= ranges->count) ||
+                       (ranges->range[i].first !=
+                        (ranges->range[i - 1].last + 1))) {
+                       break;
+               }
+       }
+       return false;
+}
+
+static bool reg_read_allowed(const struct ab3550_reg_ranges *ranges, u8 reg)
+{
+       return page_read_allowed(ranges, reg, reg);
+}
+
+/*
+ * The exported register access functionality.
+ */
+int ab3550_get_chip_id(struct device *dev)
+{
+       struct ab3550 *ab = dev_get_drvdata(dev->parent);
+       return (int)ab->chip_id;
+}
+
+int ab3550_mask_and_set_register_interruptible(struct device *dev, u8 bank,
+       u8 reg, u8 bitmask, u8 bitvalues)
+{
+       struct ab3550 *ab;
+       struct platform_device *pdev = to_platform_device(dev);
+
+       if ((AB3550_NUM_BANKS <= bank) ||
+               !reg_write_allowed(&ab3550_reg_ranges[pdev->id][bank], reg))
+               return -EINVAL;
+
+       ab = dev_get_drvdata(dev->parent);
+       return mask_and_set_register_interruptible(ab, bank, reg,
+               bitmask, bitvalues);
+}
+
+int ab3550_set_register_interruptible(struct device *dev, u8 bank, u8 reg,
+       u8 value)
+{
+       return ab3550_mask_and_set_register_interruptible(dev, bank, reg, 0xFF,
+               value);
+}
+
+int ab3550_get_register_interruptible(struct device *dev, u8 bank, u8 reg,
+       u8 *value)
+{
+       struct ab3550 *ab;
+       struct platform_device *pdev = to_platform_device(dev);
+
+       if ((AB3550_NUM_BANKS <= bank) ||
+               !reg_read_allowed(&ab3550_reg_ranges[pdev->id][bank], reg))
+               return -EINVAL;
+
+       ab = dev_get_drvdata(dev->parent);
+       return get_register_interruptible(ab, bank, reg, value);
+}
+
+int ab3550_get_register_page_interruptible(struct device *dev, u8 bank,
+       u8 first_reg, u8 *regvals, u8 numregs)
+{
+       struct ab3550 *ab;
+       struct platform_device *pdev = to_platform_device(dev);
+
+       if ((AB3550_NUM_BANKS <= bank) ||
+               !page_read_allowed(&ab3550_reg_ranges[pdev->id][bank],
+                       first_reg, (first_reg + numregs - 1)))
+               return -EINVAL;
+
+       ab = dev_get_drvdata(dev->parent);
+       return get_register_page_interruptible(ab, bank, first_reg, regvals,
+               numregs);
+}
+
+int ab3550_event_registers_startup_state_get(struct device *dev, u8 *event)
+{
+       struct ab3550 *ab;
+
+       ab = dev_get_drvdata(dev->parent);
+       if (!ab->startup_events_read)
+               return -EAGAIN; /* Try again later */
+
+       memcpy(event, ab->startup_events, AB3550_NUM_EVENT_REG);
+       return 0;
+}
+
+int ab3550_startup_irq_enabled(struct device *dev, unsigned int irq)
+{
+       struct ab3550 *ab;
+       struct ab3550_platform_data *plf_data;
+       bool val;
+
+       ab = get_irq_chip_data(irq);
+       plf_data = ab->i2c_client[0]->dev.platform_data;
+       irq -= plf_data->irq.base;
+       val = ((ab->startup_events[irq / 8] & BIT(irq % 8)) != 0);
+
+       return val;
+}
+
+static struct abx500_ops ab3550_ops = {
+       .get_chip_id = ab3550_get_chip_id,
+       .get_register = ab3550_get_register_interruptible,
+       .set_register = ab3550_set_register_interruptible,
+       .get_register_page = ab3550_get_register_page_interruptible,
+       .set_register_page = NULL,
+       .mask_and_set_register = ab3550_mask_and_set_register_interruptible,
+       .event_registers_startup_state_get =
+               ab3550_event_registers_startup_state_get,
+       .startup_irq_enabled = ab3550_startup_irq_enabled,
+};
+
+static irqreturn_t ab3550_irq_handler(int irq, void *data)
+{
+       struct ab3550 *ab = data;
+       int err;
+       unsigned int i;
+       u8 e[AB3550_NUM_EVENT_REG];
+       u8 *events;
+       unsigned long flags;
+
+       events = (ab->startup_events_read ? e : ab->startup_events);
+
+       err = get_register_page_interruptible(ab, AB3550_EVENT_BANK,
+               AB3550_EVENT_REG, events, AB3550_NUM_EVENT_REG);
+       if (err)
+               goto err_event_rd;
+
+       if (!ab->startup_events_read) {
+               dev_info(&ab->i2c_client[0]->dev,
+                       "startup events 0x%x,0x%x,0x%x,0x%x,0x%x\n",
+                       ab->startup_events[0], ab->startup_events[1],
+                       ab->startup_events[2], ab->startup_events[3],
+                       ab->startup_events[4]);
+               ab->startup_events_read = true;
+               goto out;
+       }
+
+       /* The two highest bits in event[4] are not used. */
+       events[4] &= 0x3f;
+
+       spin_lock_irqsave(&ab->event_lock, flags);
+       for (i = 0; i < AB3550_NUM_EVENT_REG; i++)
+               events[i] &= ~ab->event_mask[i];
+       spin_unlock_irqrestore(&ab->event_lock, flags);
+
+       for (i = 0; i < AB3550_NUM_EVENT_REG; i++) {
+               u8 bit;
+               u8 event_reg;
+
+               dev_dbg(&ab->i2c_client[0]->dev, "IRQ Event[%d]: 0x%2x\n",
+                       i, events[i]);
+
+               event_reg = events[i];
+               for (bit = 0; event_reg; bit++, event_reg /= 2) {
+                       if (event_reg % 2) {
+                               unsigned int irq;
+                               struct ab3550_platform_data *plf_data;
+
+                               plf_data = ab->i2c_client[0]->dev.platform_data;
+                               irq = plf_data->irq.base + (i * 8) + bit;
+                               handle_nested_irq(irq);
+                       }
+               }
+       }
+out:
+       return IRQ_HANDLED;
+
+err_event_rd:
+       dev_dbg(&ab->i2c_client[0]->dev, "error reading event registers\n");
+       return IRQ_HANDLED;
+}
+
+#ifdef CONFIG_DEBUG_FS
+static struct ab3550_reg_ranges debug_ranges[AB3550_NUM_BANKS] = {
+       {
+               .count = 6,
+               .range = (struct ab3550_reg_range[]) {
+                       {
+                               .first = 0x00,
+                               .last = 0x0e,
+                       },
+                       {
+                               .first = 0x10,
+                               .last = 0x1a,
+                       },
+                       {
+                               .first = 0x1e,
+                               .last = 0x4f,
+                       },
+                       {
+                               .first = 0x51,
+                               .last = 0x63,
+                       },
+                       {
+                               .first = 0x65,
+                               .last = 0xa3,
+                       },
+                       {
+                               .first = 0xa5,
+                               .last = 0xa8,
+                       },
+               }
+       },
+       {
+               .count = 8,
+               .range = (struct ab3550_reg_range[]) {
+                       {
+                               .first = 0x00,
+                               .last = 0x0e,
+                       },
+                       {
+                               .first = 0x10,
+                               .last = 0x17,
+                       },
+                       {
+                               .first = 0x1a,
+                               .last = 0x1c,
+                       },
+                       {
+                               .first = 0x20,
+                               .last = 0x56,
+                       },
+                       {
+                               .first = 0x5a,
+                               .last = 0x88,
+                       },
+                       {
+                               .first = 0x8a,
+                               .last = 0xad,
+                       },
+                       {
+                               .first = 0xb0,
+                               .last = 0xba,
+                       },
+                       {
+                               .first = 0xbc,
+                               .last = 0xc3,
+                       },
+               }
+       },
+};
+
+static int ab3550_registers_print(struct seq_file *s, void *p)
+{
+       struct ab3550 *ab = s->private;
+       int bank;
+
+       seq_printf(s, AB3550_NAME_STRING " register values:\n");
+
+       for (bank = 0; bank < AB3550_NUM_BANKS; bank++) {
+               unsigned int i;
+
+               seq_printf(s, " bank %d:\n", bank);
+               for (i = 0; i < debug_ranges[bank].count; i++) {
+                       u8 reg;
+
+                       for (reg = debug_ranges[bank].range[i].first;
+                               reg <= debug_ranges[bank].range[i].last;
+                               reg++) {
+                               u8 value;
+
+                               get_register_interruptible(ab, bank, reg,
+                                       &value);
+                               seq_printf(s, "  [%d/0x%02X]: 0x%02X\n", bank,
+                                       reg, value);
+                       }
+               }
+       }
+       return 0;
+}
+
+static int ab3550_registers_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, ab3550_registers_print, inode->i_private);
+}
+
+static const struct file_operations ab3550_registers_fops = {
+       .open = ab3550_registers_open,
+       .read = seq_read,
+       .llseek = seq_lseek,
+       .release = single_release,
+       .owner = THIS_MODULE,
+};
+
+static int ab3550_bank_print(struct seq_file *s, void *p)
+{
+       struct ab3550 *ab = s->private;
+
+       seq_printf(s, "%d\n", ab->debug_bank);
+       return 0;
+}
+
+static int ab3550_bank_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, ab3550_bank_print, inode->i_private);
+}
+
+static ssize_t ab3550_bank_write(struct file *file,
+       const char __user *user_buf,
+       size_t count, loff_t *ppos)
+{
+       struct ab3550 *ab = ((struct seq_file *)(file->private_data))->private;
+       char buf[32];
+       int buf_size;
+       unsigned long user_bank;
+       int err;
+
+       /* Get userspace string and assure termination */
+       buf_size = min(count, (sizeof(buf) - 1));
+       if (copy_from_user(buf, user_buf, buf_size))
+               return -EFAULT;
+       buf[buf_size] = 0;
+
+       err = strict_strtoul(buf, 0, &user_bank);
+       if (err)
+               return -EINVAL;
+
+       if (user_bank >= AB3550_NUM_BANKS) {
+               dev_err(&ab->i2c_client[0]->dev,
+                       "debugfs error input > number of banks\n");
+               return -EINVAL;
+       }
+
+       ab->debug_bank = user_bank;
+
+       return buf_size;
+}
+
+static int ab3550_address_print(struct seq_file *s, void *p)
+{
+       struct ab3550 *ab = s->private;
+
+       seq_printf(s, "0x%02X\n", ab->debug_address);
+       return 0;
+}
+
+static int ab3550_address_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, ab3550_address_print, inode->i_private);
+}
+
+static ssize_t ab3550_address_write(struct file *file,
+       const char __user *user_buf,
+       size_t count, loff_t *ppos)
+{
+       struct ab3550 *ab = ((struct seq_file *)(file->private_data))->private;
+       char buf[32];
+       int buf_size;
+       unsigned long user_address;
+       int err;
+
+       /* Get userspace string and assure termination */
+       buf_size = min(count, (sizeof(buf) - 1));
+       if (copy_from_user(buf, user_buf, buf_size))
+               return -EFAULT;
+       buf[buf_size] = 0;
+
+       err = strict_strtoul(buf, 0, &user_address);
+       if (err)
+               return -EINVAL;
+       if (user_address > 0xff) {
+               dev_err(&ab->i2c_client[0]->dev,
+                       "debugfs error input > 0xff\n");
+               return -EINVAL;
+       }
+       ab->debug_address = user_address;
+       return buf_size;
+}
+
+static int ab3550_val_print(struct seq_file *s, void *p)
+{
+       struct ab3550 *ab = s->private;
+       int err;
+       u8 regvalue;
+
+       err = get_register_interruptible(ab, (u8)ab->debug_bank,
+               (u8)ab->debug_address, &regvalue);
+       if (err)
+               return -EINVAL;
+       seq_printf(s, "0x%02X\n", regvalue);
+
+       return 0;
+}
+
+static int ab3550_val_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, ab3550_val_print, inode->i_private);
+}
+
+static ssize_t ab3550_val_write(struct file *file,
+       const char __user *user_buf,
+       size_t count, loff_t *ppos)
+{
+       struct ab3550 *ab = ((struct seq_file *)(file->private_data))->private;
+       char buf[32];
+       int buf_size;
+       unsigned long user_val;
+       int err;
+       u8 regvalue;
+
+       /* Get userspace string and assure termination */
+       buf_size = min(count, (sizeof(buf)-1));
+       if (copy_from_user(buf, user_buf, buf_size))
+               return -EFAULT;
+       buf[buf_size] = 0;
+
+       err = strict_strtoul(buf, 0, &user_val);
+       if (err)
+               return -EINVAL;
+       if (user_val > 0xff) {
+               dev_err(&ab->i2c_client[0]->dev,
+                       "debugfs error input > 0xff\n");
+               return -EINVAL;
+       }
+       err = mask_and_set_register_interruptible(
+               ab, (u8)ab->debug_bank,
+               (u8)ab->debug_address, 0xFF, (u8)user_val);
+       if (err)
+               return -EINVAL;
+
+       get_register_interruptible(ab, (u8)ab->debug_bank,
+               (u8)ab->debug_address, &regvalue);
+       if (err)
+               return -EINVAL;
+
+       return buf_size;
+}
+
+static const struct file_operations ab3550_bank_fops = {
+       .open = ab3550_bank_open,
+       .write = ab3550_bank_write,
+       .read = seq_read,
+       .llseek = seq_lseek,
+       .release = single_release,
+       .owner = THIS_MODULE,
+};
+
+static const struct file_operations ab3550_address_fops = {
+       .open = ab3550_address_open,
+       .write = ab3550_address_write,
+       .read = seq_read,
+       .llseek = seq_lseek,
+       .release = single_release,
+       .owner = THIS_MODULE,
+};
+
+static const struct file_operations ab3550_val_fops = {
+       .open = ab3550_val_open,
+       .write = ab3550_val_write,
+       .read = seq_read,
+       .llseek = seq_lseek,
+       .release = single_release,
+       .owner = THIS_MODULE,
+};
+
+static struct dentry *ab3550_dir;
+static struct dentry *ab3550_reg_file;
+static struct dentry *ab3550_bank_file;
+static struct dentry *ab3550_address_file;
+static struct dentry *ab3550_val_file;
+
+static inline void ab3550_setup_debugfs(struct ab3550 *ab)
+{
+       ab->debug_bank = 0;
+       ab->debug_address = 0x00;
+
+       ab3550_dir = debugfs_create_dir(AB3550_NAME_STRING, NULL);
+       if (!ab3550_dir)
+               goto exit_no_debugfs;
+
+       ab3550_reg_file = debugfs_create_file("all-registers",
+               S_IRUGO, ab3550_dir, ab, &ab3550_registers_fops);
+       if (!ab3550_reg_file)
+               goto exit_destroy_dir;
+
+       ab3550_bank_file = debugfs_create_file("register-bank",
+               (S_IRUGO | S_IWUGO), ab3550_dir, ab, &ab3550_bank_fops);
+       if (!ab3550_bank_file)
+               goto exit_destroy_reg;
+
+       ab3550_address_file = debugfs_create_file("register-address",
+               (S_IRUGO | S_IWUGO), ab3550_dir, ab, &ab3550_address_fops);
+       if (!ab3550_address_file)
+               goto exit_destroy_bank;
+
+       ab3550_val_file = debugfs_create_file("register-value",
+               (S_IRUGO | S_IWUGO), ab3550_dir, ab, &ab3550_val_fops);
+       if (!ab3550_val_file)
+               goto exit_destroy_address;
+
+       return;
+
+exit_destroy_address:
+       debugfs_remove(ab3550_address_file);
+exit_destroy_bank:
+       debugfs_remove(ab3550_bank_file);
+exit_destroy_reg:
+       debugfs_remove(ab3550_reg_file);
+exit_destroy_dir:
+       debugfs_remove(ab3550_dir);
+exit_no_debugfs:
+       dev_err(&ab->i2c_client[0]->dev, "failed to create debugfs entries.\n");
+       return;
+}
+
+static inline void ab3550_remove_debugfs(void)
+{
+       debugfs_remove(ab3550_val_file);
+       debugfs_remove(ab3550_address_file);
+       debugfs_remove(ab3550_bank_file);
+       debugfs_remove(ab3550_reg_file);
+       debugfs_remove(ab3550_dir);
+}
+
+#else /* !CONFIG_DEBUG_FS */
+static inline void ab3550_setup_debugfs(struct ab3550 *ab)
+{
+}
+static inline void ab3550_remove_debugfs(void)
+{
+}
+#endif
+
+/*
+ * Basic set-up, datastructure creation/destruction and I2C interface.
+ * This sets up a default config in the AB3550 chip so that it
+ * will work as expected.
+ */
+static int __init ab3550_setup(struct ab3550 *ab)
+{
+       int err = 0;
+       int i;
+       struct ab3550_platform_data *plf_data;
+       struct abx500_init_settings *settings;
+
+       plf_data = ab->i2c_client[0]->dev.platform_data;
+       settings = plf_data->init_settings;
+
+       for (i = 0; i < plf_data->init_settings_sz; i++) {
+               err = mask_and_set_register_interruptible(ab,
+                       settings[i].bank,
+                       settings[i].reg,
+                       0xFF, settings[i].setting);
+               if (err)
+                       goto exit_no_setup;
+
+               /* If event mask register update the event mask in ab3550 */
+               if ((settings[i].bank == 0) &&
+                       (AB3550_IMR1 <= settings[i].reg) &&
+                       (settings[i].reg <= AB3550_IMR5)) {
+                       ab->event_mask[settings[i].reg - AB3550_IMR1] =
+                               settings[i].setting;
+               }
+       }
+exit_no_setup:
+       return err;
+}
+
+static void ab3550_mask_work(struct work_struct *work)
+{
+       struct ab3550 *ab = container_of(work, struct ab3550, mask_work);
+       int i;
+       unsigned long flags;
+       u8 mask[AB3550_NUM_EVENT_REG];
+
+       spin_lock_irqsave(&ab->event_lock, flags);
+       for (i = 0; i < AB3550_NUM_EVENT_REG; i++)
+               mask[i] = ab->event_mask[i];
+       spin_unlock_irqrestore(&ab->event_lock, flags);
+
+       for (i = 0; i < AB3550_NUM_EVENT_REG; i++) {
+               int err;
+
+               err = mask_and_set_register_interruptible(ab, 0,
+                       (AB3550_IMR1 + i), ~0, mask[i]);
+               if (err)
+                       dev_err(&ab->i2c_client[0]->dev,
+                               "ab3550_mask_work failed 0x%x,0x%x\n",
+                               (AB3550_IMR1 + i), mask[i]);
+       }
+}
+
+static void ab3550_mask(unsigned int irq)
+{
+       unsigned long flags;
+       struct ab3550 *ab;
+       struct ab3550_platform_data *plf_data;
+
+       ab = get_irq_chip_data(irq);
+       plf_data = ab->i2c_client[0]->dev.platform_data;
+       irq -= plf_data->irq.base;
+
+       spin_lock_irqsave(&ab->event_lock, flags);
+       ab->event_mask[irq / 8] |= BIT(irq % 8);
+       spin_unlock_irqrestore(&ab->event_lock, flags);
+
+       schedule_work(&ab->mask_work);
+}
+
+static void ab3550_unmask(unsigned int irq)
+{
+       unsigned long flags;
+       struct ab3550 *ab;
+       struct ab3550_platform_data *plf_data;
+
+       ab = get_irq_chip_data(irq);
+       plf_data = ab->i2c_client[0]->dev.platform_data;
+       irq -= plf_data->irq.base;
+
+       spin_lock_irqsave(&ab->event_lock, flags);
+       ab->event_mask[irq / 8] &= ~BIT(irq % 8);
+       spin_unlock_irqrestore(&ab->event_lock, flags);
+
+       schedule_work(&ab->mask_work);
+}
+
+static void noop(unsigned int irq)
+{
+}
+
+static struct irq_chip ab3550_irq_chip = {
+       .name           = "ab3550-core", /* Keep the same name as the request */
+       .startup        = NULL, /* defaults to enable */
+       .shutdown       = NULL, /* defaults to disable */
+       .enable         = NULL, /* defaults to unmask */
+       .disable        = ab3550_mask, /* No default to mask in chip.c */
+       .ack            = noop,
+       .mask           = ab3550_mask,
+       .unmask         = ab3550_unmask,
+       .end            = NULL,
+};
+
+struct ab_family_id {
+       u8      id;
+       char    *name;
+};
+
+static const struct ab_family_id ids[] __initdata = {
+       /* AB3550 */
+       {
+               .id = AB3550_P1A,
+               .name = "P1A"
+       },
+       /* Terminator */
+       {
+               .id = 0x00,
+       }
+};
+
+static int __init ab3550_probe(struct i2c_client *client,
+       const struct i2c_device_id *id)
+{
+       struct ab3550 *ab;
+       struct ab3550_platform_data *ab3550_plf_data =
+               client->dev.platform_data;
+       int err;
+       int i;
+       int num_i2c_clients = 0;
+
+       ab = kzalloc(sizeof(struct ab3550), GFP_KERNEL);
+       if (!ab) {
+               dev_err(&client->dev,
+                       "could not allocate " AB3550_NAME_STRING " device\n");
+               return -ENOMEM;
+       }
+
+       /* Initialize data structure */
+       mutex_init(&ab->access_mutex);
+       spin_lock_init(&ab->event_lock);
+       ab->i2c_client[0] = client;
+
+       i2c_set_clientdata(client, ab);
+
+       /* Read chip ID register */
+       err = get_register_interruptible(ab, 0, AB3550_CID_REG, &ab->chip_id);
+       if (err) {
+               dev_err(&client->dev, "could not communicate with the analog "
+                       "baseband chip\n");
+               goto exit_no_detect;
+       }
+
+       for (i = 0; ids[i].id != 0x0; i++) {
+               if (ids[i].id == ab->chip_id) {
+                       snprintf(&ab->chip_name[0], sizeof(ab->chip_name) - 1,
+                               AB3550_ID_FORMAT_STRING, ids[i].name);
+                       break;
+               }
+       }
+
+       if (ids[i].id == 0x0) {
+               dev_err(&client->dev, "unknown analog baseband chip id: 0x%x\n",
+                       ab->chip_id);
+               dev_err(&client->dev, "driver not started!\n");
+               goto exit_no_detect;
+       }
+
+       dev_info(&client->dev, "detected AB chip: %s\n", &ab->chip_name[0]);
+
+       /* Attach other dummy I2C clients. */
+       while (++num_i2c_clients < AB3550_NUM_BANKS) {
+               ab->i2c_client[num_i2c_clients] =
+                       i2c_new_dummy(client->adapter,
+                               (client->addr + num_i2c_clients));
+               if (!ab->i2c_client[num_i2c_clients]) {
+                       err = -ENOMEM;
+                       goto exit_no_dummy_client;
+               }
+               strlcpy(ab->i2c_client[num_i2c_clients]->name, id->name,
+                       sizeof(ab->i2c_client[num_i2c_clients]->name));
+       }
+
+       err = ab3550_setup(ab);
+       if (err)
+               goto exit_no_setup;
+
+       INIT_WORK(&ab->mask_work, ab3550_mask_work);
+
+       for (i = 0; i < ab3550_plf_data->irq.count; i++) {
+               unsigned int irq;
+
+               irq = ab3550_plf_data->irq.base + i;
+               set_irq_chip_data(irq, ab);
+               set_irq_chip_and_handler(irq, &ab3550_irq_chip,
+                       handle_simple_irq);
+               set_irq_nested_thread(irq, 1);
+#ifdef CONFIG_ARM
+               set_irq_flags(irq, IRQF_VALID);
+#else
+               set_irq_noprobe(irq);
+#endif
+       }
+
+       err = request_threaded_irq(client->irq, NULL, ab3550_irq_handler,
+               IRQF_ONESHOT, "ab3550-core", ab);
+       /* This real unpredictable IRQ is of course sampled for entropy */
+       rand_initialize_irq(client->irq);
+
+       if (err)
+               goto exit_no_irq;
+
+       err = abx500_register_ops(&client->dev, &ab3550_ops);
+       if (err)
+               goto exit_no_ops;
+
+       /* Set up and register the platform devices. */
+       for (i = 0; i < AB3550_NUM_DEVICES; i++) {
+               ab3550_devs[i].platform_data = ab3550_plf_data->dev_data[i];
+               ab3550_devs[i].data_size = ab3550_plf_data->dev_data_sz[i];
+       }
+
+       err = mfd_add_devices(&client->dev, 0, ab3550_devs,
+               ARRAY_SIZE(ab3550_devs), NULL,
+               ab3550_plf_data->irq.base);
+
+       ab3550_setup_debugfs(ab);
+
+       return 0;
+
+exit_no_ops:
+exit_no_irq:
+exit_no_setup:
+exit_no_dummy_client:
+       /* Unregister the dummy i2c clients. */
+       while (--num_i2c_clients)
+               i2c_unregister_device(ab->i2c_client[num_i2c_clients]);
+exit_no_detect:
+       kfree(ab);
+       return err;
+}
+
+static int __exit ab3550_remove(struct i2c_client *client)
+{
+       struct ab3550 *ab = i2c_get_clientdata(client);
+       int num_i2c_clients = AB3550_NUM_BANKS;
+
+       mfd_remove_devices(&client->dev);
+       ab3550_remove_debugfs();
+
+       while (--num_i2c_clients)
+               i2c_unregister_device(ab->i2c_client[num_i2c_clients]);
+
+       /*
+        * At this point, all subscribers should have unregistered
+        * their notifiers so deactivate IRQ
+        */
+       free_irq(client->irq, ab);
+       i2c_set_clientdata(client, NULL);
+       kfree(ab);
+       return 0;
+}
+
+static const struct i2c_device_id ab3550_id[] = {
+       {AB3550_NAME_STRING, 0},
+       {}
+};
+MODULE_DEVICE_TABLE(i2c, ab3550_id);
+
+static struct i2c_driver ab3550_driver = {
+       .driver = {
+               .name   = AB3550_NAME_STRING,
+               .owner  = THIS_MODULE,
+       },
+       .id_table       = ab3550_id,
+       .probe          = ab3550_probe,
+       .remove         = __exit_p(ab3550_remove),
+};
+
+static int __init ab3550_i2c_init(void)
+{
+       return i2c_add_driver(&ab3550_driver);
+}
+
+static void __exit ab3550_i2c_exit(void)
+{
+       i2c_del_driver(&ab3550_driver);
+}
+
+subsys_initcall(ab3550_i2c_init);
+module_exit(ab3550_i2c_exit);
+
+MODULE_AUTHOR("Mattias Wallin <mattias.wallin@stericsson.com>");
+MODULE_DESCRIPTION("AB3550 core driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mfd/ab4500-core.c b/drivers/mfd/ab4500-core.c
deleted file mode 100644 (file)
index c275daa..0000000
+++ /dev/null
@@ -1,209 +0,0 @@
-/*
- * Copyright (C) 2009 ST-Ericsson
- *
- * Author: Srinidhi KASAGAR <srinidhi.kasagar@stericsson.com>
- *
- * 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.
- *
- * AB4500 is a companion power management chip used with U8500.
- * On this platform, this is interfaced with SSP0 controller
- * which is a ARM primecell pl022.
- *
- * At the moment the module just exports read/write features.
- * Interrupt management to be added - TODO.
- */
-#include <linux/kernel.h>
-#include <linux/slab.h>
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/platform_device.h>
-#include <linux/spi/spi.h>
-#include <linux/mfd/ab4500.h>
-
-/* just required if probe fails, we need to
- * unregister the device
- */
-static struct spi_driver ab4500_driver;
-
-/*
- * This funtion writes to any AB4500 registers using
- * SPI protocol &  before it writes it packs the data
- * in the below 24 bit frame format
- *
- *      *|------------------------------------|
- *      *| 23|22...18|17.......10|9|8|7......0|
- *      *| r/w  bank       adr          data  |
- *      * ------------------------------------
- *
- * This function shouldn't be called from interrupt
- * context
- */
-int ab4500_write(struct ab4500 *ab4500, unsigned char block,
-               unsigned long addr, unsigned char data)
-{
-       struct spi_transfer xfer;
-       struct spi_message      msg;
-       int err;
-       unsigned long spi_data =
-               block << 18 | addr << 10 | data;
-
-       mutex_lock(&ab4500->lock);
-       ab4500->tx_buf[0] = spi_data;
-       ab4500->rx_buf[0] = 0;
-
-       xfer.tx_buf     = ab4500->tx_buf;
-       xfer.rx_buf     = NULL;
-       xfer.len        = sizeof(unsigned long);
-
-       spi_message_init(&msg);
-       spi_message_add_tail(&xfer, &msg);
-
-       err = spi_sync(ab4500->spi, &msg);
-       mutex_unlock(&ab4500->lock);
-
-       return err;
-}
-EXPORT_SYMBOL(ab4500_write);
-
-int ab4500_read(struct ab4500 *ab4500, unsigned char block,
-               unsigned long addr)
-{
-       struct spi_transfer xfer;
-       struct spi_message      msg;
-       unsigned long spi_data =
-               1 << 23 | block << 18 | addr << 10;
-
-       mutex_lock(&ab4500->lock);
-       ab4500->tx_buf[0] = spi_data;
-       ab4500->rx_buf[0] = 0;
-
-       xfer.tx_buf     = ab4500->tx_buf;
-       xfer.rx_buf     = ab4500->rx_buf;
-       xfer.len        = sizeof(unsigned long);
-
-       spi_message_init(&msg);
-       spi_message_add_tail(&xfer, &msg);
-
-       spi_sync(ab4500->spi, &msg);
-       mutex_unlock(&ab4500->lock);
-
-       return  ab4500->rx_buf[0];
-}
-EXPORT_SYMBOL(ab4500_read);
-
-/* ref: ab3100 core */
-#define AB4500_DEVICE(devname, devid)                          \
-static struct platform_device ab4500_##devname##_device = {    \
-       .name   = devid,                                        \
-       .id     = -1,                                           \
-}
-
-/* list of childern devices of ab4500 - all are
- * not populated here - TODO
- */
-AB4500_DEVICE(charger, "ab4500-charger");
-AB4500_DEVICE(audio, "ab4500-audio");
-AB4500_DEVICE(usb, "ab4500-usb");
-AB4500_DEVICE(tvout, "ab4500-tvout");
-AB4500_DEVICE(sim, "ab4500-sim");
-AB4500_DEVICE(gpadc, "ab4500-gpadc");
-AB4500_DEVICE(clkmgt, "ab4500-clkmgt");
-AB4500_DEVICE(misc, "ab4500-misc");
-
-static struct platform_device *ab4500_platform_devs[] = {
-       &ab4500_charger_device,
-       &ab4500_audio_device,
-       &ab4500_usb_device,
-       &ab4500_tvout_device,
-       &ab4500_sim_device,
-       &ab4500_gpadc_device,
-       &ab4500_clkmgt_device,
-       &ab4500_misc_device,
-};
-
-static int __init ab4500_probe(struct spi_device *spi)
-{
-       struct ab4500   *ab4500;
-       unsigned char revision;
-       int err = 0;
-       int i;
-
-       ab4500 = kzalloc(sizeof *ab4500, GFP_KERNEL);
-       if (!ab4500) {
-               dev_err(&spi->dev, "could not allocate AB4500\n");
-               err = -ENOMEM;
-               goto not_detect;
-       }
-
-       ab4500->spi = spi;
-       spi_set_drvdata(spi, ab4500);
-
-       mutex_init(&ab4500->lock);
-
-       /* read the revision register */
-       revision = ab4500_read(ab4500, AB4500_MISC, AB4500_REV_REG);
-
-       /* revision id 0x0 is for early drop, 0x10 is for cut1.0 */
-       if (revision == 0x0 || revision == 0x10)
-               dev_info(&spi->dev, "Detected chip: %s, revision = %x\n",
-                       ab4500_driver.driver.name, revision);
-       else    {
-               dev_err(&spi->dev, "unknown chip: 0x%x\n", revision);
-               goto not_detect;
-       }
-
-       for (i = 0; i < ARRAY_SIZE(ab4500_platform_devs); i++)  {
-               ab4500_platform_devs[i]->dev.parent =
-                       &spi->dev;
-               platform_set_drvdata(ab4500_platform_devs[i], ab4500);
-       }
-
-       /* register the ab4500 platform devices */
-       platform_add_devices(ab4500_platform_devs,
-                       ARRAY_SIZE(ab4500_platform_devs));
-
-       return err;
-
- not_detect:
-       spi_unregister_driver(&ab4500_driver);
-       kfree(ab4500);
-       return err;
-}
-
-static int __devexit ab4500_remove(struct spi_device *spi)
-{
-       struct ab4500 *ab4500 =
-               spi_get_drvdata(spi);
-
-       kfree(ab4500);
-
-       return 0;
-}
-
-static struct spi_driver ab4500_driver = {
-       .driver = {
-               .name = "ab4500",
-               .owner = THIS_MODULE,
-       },
-       .probe = ab4500_probe,
-       .remove = __devexit_p(ab4500_remove)
-};
-
-static int __devinit ab4500_init(void)
-{
-       return spi_register_driver(&ab4500_driver);
-}
-
-static void __exit ab4500_exit(void)
-{
-       spi_unregister_driver(&ab4500_driver);
-}
-
-subsys_initcall(ab4500_init);
-module_exit(ab4500_exit);
-
-MODULE_AUTHOR("Srinidhi KASAGAR <srinidhi.kasagar@stericsson.com");
-MODULE_DESCRIPTION("AB4500 core driver");
-MODULE_LICENSE("GPL");
diff --git a/drivers/mfd/ab8500-core.c b/drivers/mfd/ab8500-core.c
new file mode 100644 (file)
index 0000000..f3d26fa
--- /dev/null
@@ -0,0 +1,444 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ *
+ * License Terms: GNU General Public License v2
+ * Author: Srinidhi Kasagar <srinidhi.kasagar@stericsson.com>
+ * Author: Rabin Vincent <rabin.vincent@stericsson.com>
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/irq.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/ab8500.h>
+
+/*
+ * Interrupt register offsets
+ * Bank : 0x0E
+ */
+#define AB8500_IT_SOURCE1_REG          0x0E00
+#define AB8500_IT_SOURCE2_REG          0x0E01
+#define AB8500_IT_SOURCE3_REG          0x0E02
+#define AB8500_IT_SOURCE4_REG          0x0E03
+#define AB8500_IT_SOURCE5_REG          0x0E04
+#define AB8500_IT_SOURCE6_REG          0x0E05
+#define AB8500_IT_SOURCE7_REG          0x0E06
+#define AB8500_IT_SOURCE8_REG          0x0E07
+#define AB8500_IT_SOURCE19_REG         0x0E12
+#define AB8500_IT_SOURCE20_REG         0x0E13
+#define AB8500_IT_SOURCE21_REG         0x0E14
+#define AB8500_IT_SOURCE22_REG         0x0E15
+#define AB8500_IT_SOURCE23_REG         0x0E16
+#define AB8500_IT_SOURCE24_REG         0x0E17
+
+/*
+ * latch registers
+ */
+#define AB8500_IT_LATCH1_REG           0x0E20
+#define AB8500_IT_LATCH2_REG           0x0E21
+#define AB8500_IT_LATCH3_REG           0x0E22
+#define AB8500_IT_LATCH4_REG           0x0E23
+#define AB8500_IT_LATCH5_REG           0x0E24
+#define AB8500_IT_LATCH6_REG           0x0E25
+#define AB8500_IT_LATCH7_REG           0x0E26
+#define AB8500_IT_LATCH8_REG           0x0E27
+#define AB8500_IT_LATCH9_REG           0x0E28
+#define AB8500_IT_LATCH10_REG          0x0E29
+#define AB8500_IT_LATCH19_REG          0x0E32
+#define AB8500_IT_LATCH20_REG          0x0E33
+#define AB8500_IT_LATCH21_REG          0x0E34
+#define AB8500_IT_LATCH22_REG          0x0E35
+#define AB8500_IT_LATCH23_REG          0x0E36
+#define AB8500_IT_LATCH24_REG          0x0E37
+
+/*
+ * mask registers
+ */
+
+#define AB8500_IT_MASK1_REG            0x0E40
+#define AB8500_IT_MASK2_REG            0x0E41
+#define AB8500_IT_MASK3_REG            0x0E42
+#define AB8500_IT_MASK4_REG            0x0E43
+#define AB8500_IT_MASK5_REG            0x0E44
+#define AB8500_IT_MASK6_REG            0x0E45
+#define AB8500_IT_MASK7_REG            0x0E46
+#define AB8500_IT_MASK8_REG            0x0E47
+#define AB8500_IT_MASK9_REG            0x0E48
+#define AB8500_IT_MASK10_REG           0x0E49
+#define AB8500_IT_MASK11_REG           0x0E4A
+#define AB8500_IT_MASK12_REG           0x0E4B
+#define AB8500_IT_MASK13_REG           0x0E4C
+#define AB8500_IT_MASK14_REG           0x0E4D
+#define AB8500_IT_MASK15_REG           0x0E4E
+#define AB8500_IT_MASK16_REG           0x0E4F
+#define AB8500_IT_MASK17_REG           0x0E50
+#define AB8500_IT_MASK18_REG           0x0E51
+#define AB8500_IT_MASK19_REG           0x0E52
+#define AB8500_IT_MASK20_REG           0x0E53
+#define AB8500_IT_MASK21_REG           0x0E54
+#define AB8500_IT_MASK22_REG           0x0E55
+#define AB8500_IT_MASK23_REG           0x0E56
+#define AB8500_IT_MASK24_REG           0x0E57
+
+#define AB8500_REV_REG                 0x1080
+
+/*
+ * Map interrupt numbers to the LATCH and MASK register offsets, Interrupt
+ * numbers are indexed into this array with (num / 8).
+ *
+ * This is one off from the register names, i.e. AB8500_IT_MASK1_REG is at
+ * offset 0.
+ */
+static const int ab8500_irq_regoffset[AB8500_NUM_IRQ_REGS] = {
+       0, 1, 2, 3, 4, 6, 7, 8, 9, 18, 19, 20, 21,
+};
+
+static int __ab8500_write(struct ab8500 *ab8500, u16 addr, u8 data)
+{
+       int ret;
+
+       dev_vdbg(ab8500->dev, "wr: addr %#x <= %#x\n", addr, data);
+
+       ret = ab8500->write(ab8500, addr, data);
+       if (ret < 0)
+               dev_err(ab8500->dev, "failed to write reg %#x: %d\n",
+                       addr, ret);
+
+       return ret;
+}
+
+/**
+ * ab8500_write() - write an AB8500 register
+ * @ab8500: device to write to
+ * @addr: address of the register
+ * @data: value to write
+ */
+int ab8500_write(struct ab8500 *ab8500, u16 addr, u8 data)
+{
+       int ret;
+
+       mutex_lock(&ab8500->lock);
+       ret = __ab8500_write(ab8500, addr, data);
+       mutex_unlock(&ab8500->lock);
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(ab8500_write);
+
+static int __ab8500_read(struct ab8500 *ab8500, u16 addr)
+{
+       int ret;
+
+       ret = ab8500->read(ab8500, addr);
+       if (ret < 0)
+               dev_err(ab8500->dev, "failed to read reg %#x: %d\n",
+                       addr, ret);
+
+       dev_vdbg(ab8500->dev, "rd: addr %#x => data %#x\n", addr, ret);
+
+       return ret;
+}
+
+/**
+ * ab8500_read() - read an AB8500 register
+ * @ab8500: device to read from
+ * @addr: address of the register
+ */
+int ab8500_read(struct ab8500 *ab8500, u16 addr)
+{
+       int ret;
+
+       mutex_lock(&ab8500->lock);
+       ret = __ab8500_read(ab8500, addr);
+       mutex_unlock(&ab8500->lock);
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(ab8500_read);
+
+/**
+ * ab8500_set_bits() - set a bitfield in an AB8500 register
+ * @ab8500: device to read from
+ * @addr: address of the register
+ * @mask: mask of the bitfield to modify
+ * @data: value to set to the bitfield
+ */
+int ab8500_set_bits(struct ab8500 *ab8500, u16 addr, u8 mask, u8 data)
+{
+       int ret;
+
+       mutex_lock(&ab8500->lock);
+
+       ret = __ab8500_read(ab8500, addr);
+       if (ret < 0)
+               goto out;
+
+       ret &= ~mask;
+       ret |= data;
+
+       ret = __ab8500_write(ab8500, addr, ret);
+
+out:
+       mutex_unlock(&ab8500->lock);
+       return ret;
+}
+EXPORT_SYMBOL_GPL(ab8500_set_bits);
+
+static void ab8500_irq_lock(unsigned int irq)
+{
+       struct ab8500 *ab8500 = get_irq_chip_data(irq);
+
+       mutex_lock(&ab8500->irq_lock);
+}
+
+static void ab8500_irq_sync_unlock(unsigned int irq)
+{
+       struct ab8500 *ab8500 = get_irq_chip_data(irq);
+       int i;
+
+       for (i = 0; i < AB8500_NUM_IRQ_REGS; i++) {
+               u8 old = ab8500->oldmask[i];
+               u8 new = ab8500->mask[i];
+               int reg;
+
+               if (new == old)
+                       continue;
+
+               ab8500->oldmask[i] = new;
+
+               reg = AB8500_IT_MASK1_REG + ab8500_irq_regoffset[i];
+               ab8500_write(ab8500, reg, new);
+       }
+
+       mutex_unlock(&ab8500->irq_lock);
+}
+
+static void ab8500_irq_mask(unsigned int irq)
+{
+       struct ab8500 *ab8500 = get_irq_chip_data(irq);
+       int offset = irq - ab8500->irq_base;
+       int index = offset / 8;
+       int mask = 1 << (offset % 8);
+
+       ab8500->mask[index] |= mask;
+}
+
+static void ab8500_irq_unmask(unsigned int irq)
+{
+       struct ab8500 *ab8500 = get_irq_chip_data(irq);
+       int offset = irq - ab8500->irq_base;
+       int index = offset / 8;
+       int mask = 1 << (offset % 8);
+
+       ab8500->mask[index] &= ~mask;
+}
+
+static struct irq_chip ab8500_irq_chip = {
+       .name                   = "ab8500",
+       .bus_lock               = ab8500_irq_lock,
+       .bus_sync_unlock        = ab8500_irq_sync_unlock,
+       .mask                   = ab8500_irq_mask,
+       .unmask                 = ab8500_irq_unmask,
+};
+
+static irqreturn_t ab8500_irq(int irq, void *dev)
+{
+       struct ab8500 *ab8500 = dev;
+       int i;
+
+       dev_vdbg(ab8500->dev, "interrupt\n");
+
+       for (i = 0; i < AB8500_NUM_IRQ_REGS; i++) {
+               int regoffset = ab8500_irq_regoffset[i];
+               int status;
+
+               status = ab8500_read(ab8500, AB8500_IT_LATCH1_REG + regoffset);
+               if (status <= 0)
+                       continue;
+
+               do {
+                       int bit = __ffs(status);
+                       int line = i * 8 + bit;
+
+                       handle_nested_irq(ab8500->irq_base + line);
+                       status &= ~(1 << bit);
+               } while (status);
+       }
+
+       return IRQ_HANDLED;
+}
+
+static int ab8500_irq_init(struct ab8500 *ab8500)
+{
+       int base = ab8500->irq_base;
+       int irq;
+
+       for (irq = base; irq < base + AB8500_NR_IRQS; irq++) {
+               set_irq_chip_data(irq, ab8500);
+               set_irq_chip_and_handler(irq, &ab8500_irq_chip,
+                                        handle_simple_irq);
+               set_irq_nested_thread(irq, 1);
+#ifdef CONFIG_ARM
+               set_irq_flags(irq, IRQF_VALID);
+#else
+               set_irq_noprobe(irq);
+#endif
+       }
+
+       return 0;
+}
+
+static void ab8500_irq_remove(struct ab8500 *ab8500)
+{
+       int base = ab8500->irq_base;
+       int irq;
+
+       for (irq = base; irq < base + AB8500_NR_IRQS; irq++) {
+#ifdef CONFIG_ARM
+               set_irq_flags(irq, 0);
+#endif
+               set_irq_chip_and_handler(irq, NULL, NULL);
+               set_irq_chip_data(irq, NULL);
+       }
+}
+
+static struct resource ab8500_gpadc_resources[] = {
+       {
+               .name   = "HW_CONV_END",
+               .start  = AB8500_INT_GP_HW_ADC_CONV_END,
+               .end    = AB8500_INT_GP_HW_ADC_CONV_END,
+               .flags  = IORESOURCE_IRQ,
+       },
+       {
+               .name   = "SW_CONV_END",
+               .start  = AB8500_INT_GP_SW_ADC_CONV_END,
+               .end    = AB8500_INT_GP_SW_ADC_CONV_END,
+               .flags  = IORESOURCE_IRQ,
+       },
+};
+
+static struct resource ab8500_rtc_resources[] = {
+       {
+               .name   = "60S",
+               .start  = AB8500_INT_RTC_60S,
+               .end    = AB8500_INT_RTC_60S,
+               .flags  = IORESOURCE_IRQ,
+       },
+       {
+               .name   = "ALARM",
+               .start  = AB8500_INT_RTC_ALARM,
+               .end    = AB8500_INT_RTC_ALARM,
+               .flags  = IORESOURCE_IRQ,
+       },
+};
+
+static struct mfd_cell ab8500_devs[] = {
+       {
+               .name = "ab8500-gpadc",
+               .num_resources = ARRAY_SIZE(ab8500_gpadc_resources),
+               .resources = ab8500_gpadc_resources,
+       },
+       {
+               .name = "ab8500-rtc",
+               .num_resources = ARRAY_SIZE(ab8500_rtc_resources),
+               .resources = ab8500_rtc_resources,
+       },
+       { .name = "ab8500-charger", },
+       { .name = "ab8500-audio", },
+       { .name = "ab8500-usb", },
+       { .name = "ab8500-pwm", },
+};
+
+int __devinit ab8500_init(struct ab8500 *ab8500)
+{
+       struct ab8500_platform_data *plat = dev_get_platdata(ab8500->dev);
+       int ret;
+       int i;
+
+       if (plat)
+               ab8500->irq_base = plat->irq_base;
+
+       mutex_init(&ab8500->lock);
+       mutex_init(&ab8500->irq_lock);
+
+       ret = ab8500_read(ab8500, AB8500_REV_REG);
+       if (ret < 0)
+               return ret;
+
+       /*
+        * 0x0 - Early Drop
+        * 0x10 - Cut 1.0
+        * 0x11 - Cut 1.1
+        */
+       if (ret == 0x0 || ret == 0x10 || ret == 0x11) {
+               ab8500->revision = ret;
+               dev_info(ab8500->dev, "detected chip, revision: %#x\n", ret);
+       } else {
+               dev_err(ab8500->dev, "unknown chip, revision: %#x\n", ret);
+               return -EINVAL;
+       }
+
+       if (plat && plat->init)
+               plat->init(ab8500);
+
+       /* Clear and mask all interrupts */
+       for (i = 0; i < 10; i++) {
+               ab8500_read(ab8500, AB8500_IT_LATCH1_REG + i);
+               ab8500_write(ab8500, AB8500_IT_MASK1_REG + i, 0xff);
+       }
+
+       for (i = 18; i < 24; i++) {
+               ab8500_read(ab8500, AB8500_IT_LATCH1_REG + i);
+               ab8500_write(ab8500, AB8500_IT_MASK1_REG + i, 0xff);
+       }
+
+       for (i = 0; i < AB8500_NUM_IRQ_REGS; i++)
+               ab8500->mask[i] = ab8500->oldmask[i] = 0xff;
+
+       if (ab8500->irq_base) {
+               ret = ab8500_irq_init(ab8500);
+               if (ret)
+                       return ret;
+
+               ret = request_threaded_irq(ab8500->irq, NULL, ab8500_irq,
+                                          IRQF_ONESHOT, "ab8500", ab8500);
+               if (ret)
+                       goto out_removeirq;
+       }
+
+       ret = mfd_add_devices(ab8500->dev, -1, ab8500_devs,
+                             ARRAY_SIZE(ab8500_devs), NULL,
+                             ab8500->irq_base);
+       if (ret)
+               goto out_freeirq;
+
+       return ret;
+
+out_freeirq:
+       if (ab8500->irq_base) {
+               free_irq(ab8500->irq, ab8500);
+out_removeirq:
+               ab8500_irq_remove(ab8500);
+       }
+       return ret;
+}
+
+int __devexit ab8500_exit(struct ab8500 *ab8500)
+{
+       mfd_remove_devices(ab8500->dev);
+       if (ab8500->irq_base) {
+               free_irq(ab8500->irq, ab8500);
+               ab8500_irq_remove(ab8500);
+       }
+
+       return 0;
+}
+
+MODULE_AUTHOR("Srinidhi Kasagar, Rabin Vincent");
+MODULE_DESCRIPTION("AB8500 MFD core");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/mfd/ab8500-spi.c b/drivers/mfd/ab8500-spi.c
new file mode 100644 (file)
index 0000000..b81d4f7
--- /dev/null
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ *
+ * License Terms: GNU General Public License v2
+ * Author: Srinidhi Kasagar <srinidhi.kasagar@stericsson.com>
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/spi/spi.h>
+#include <linux/mfd/ab8500.h>
+
+/*
+ * This funtion writes to any AB8500 registers using
+ * SPI protocol &  before it writes it packs the data
+ * in the below 24 bit frame format
+ *
+ *      *|------------------------------------|
+ *      *| 23|22...18|17.......10|9|8|7......0|
+ *      *| r/w  bank       adr          data  |
+ *      * ------------------------------------
+ *
+ * This function shouldn't be called from interrupt
+ * context
+ */
+static int ab8500_spi_write(struct ab8500 *ab8500, u16 addr, u8 data)
+{
+       struct spi_device *spi = container_of(ab8500->dev, struct spi_device,
+                                             dev);
+       unsigned long spi_data = addr << 10 | data;
+       struct spi_transfer xfer;
+       struct spi_message msg;
+
+       ab8500->tx_buf[0] = spi_data;
+       ab8500->rx_buf[0] = 0;
+
+       xfer.tx_buf     = ab8500->tx_buf;
+       xfer.rx_buf     = NULL;
+       xfer.len        = sizeof(unsigned long);
+
+       spi_message_init(&msg);
+       spi_message_add_tail(&xfer, &msg);
+
+       return spi_sync(spi, &msg);
+}
+
+static int ab8500_spi_read(struct ab8500 *ab8500, u16 addr)
+{
+       struct spi_device *spi = container_of(ab8500->dev, struct spi_device,
+                                             dev);
+       unsigned long spi_data = 1 << 23 | addr << 10;
+       struct spi_transfer xfer;
+       struct spi_message msg;
+       int ret;
+
+       ab8500->tx_buf[0] = spi_data;
+       ab8500->rx_buf[0] = 0;
+
+       xfer.tx_buf     = ab8500->tx_buf;
+       xfer.rx_buf     = ab8500->rx_buf;
+       xfer.len        = sizeof(unsigned long);
+
+       spi_message_init(&msg);
+       spi_message_add_tail(&xfer, &msg);
+
+       ret = spi_sync(spi, &msg);
+       if (!ret)
+               ret = ab8500->rx_buf[0];
+
+       return ret;
+}
+
+static int __devinit ab8500_spi_probe(struct spi_device *spi)
+{
+       struct ab8500 *ab8500;
+       int ret;
+
+       ab8500 = kzalloc(sizeof *ab8500, GFP_KERNEL);
+       if (!ab8500)
+               return -ENOMEM;
+
+       ab8500->dev = &spi->dev;
+       ab8500->irq = spi->irq;
+
+       ab8500->read = ab8500_spi_read;
+       ab8500->write = ab8500_spi_write;
+
+       spi_set_drvdata(spi, ab8500);
+
+       ret = ab8500_init(ab8500);
+       if (ret)
+               kfree(ab8500);
+
+       return ret;
+}
+
+static int __devexit ab8500_spi_remove(struct spi_device *spi)
+{
+       struct ab8500 *ab8500 = spi_get_drvdata(spi);
+
+       ab8500_exit(ab8500);
+       kfree(ab8500);
+
+       return 0;
+}
+
+static struct spi_driver ab8500_spi_driver = {
+       .driver = {
+               .name = "ab8500",
+               .owner = THIS_MODULE,
+       },
+       .probe  = ab8500_spi_probe,
+       .remove = __devexit_p(ab8500_spi_remove)
+};
+
+static int __init ab8500_spi_init(void)
+{
+       return spi_register_driver(&ab8500_spi_driver);
+}
+subsys_initcall(ab8500_spi_init);
+
+static void __exit ab8500_spi_exit(void)
+{
+       spi_unregister_driver(&ab8500_spi_driver);
+}
+module_exit(ab8500_spi_exit);
+
+MODULE_AUTHOR("Srinidhi KASAGAR <srinidhi.kasagar@stericsson.com");
+MODULE_DESCRIPTION("AB8500 SPI");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/mfd/abx500-core.c b/drivers/mfd/abx500-core.c
new file mode 100644 (file)
index 0000000..3b3b97e
--- /dev/null
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 2007-2010 ST-Ericsson
+ * License terms: GNU General Public License (GPL) version 2
+ * Register access functions for the ABX500 Mixed Signal IC family.
+ * Author: Mattias Wallin <mattias.wallin@stericsson.com>
+ */
+
+#include <linux/list.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/mfd/abx500.h>
+
+static LIST_HEAD(abx500_list);
+
+struct abx500_device_entry {
+       struct list_head list;
+       struct abx500_ops ops;
+       struct device *dev;
+};
+
+static void lookup_ops(struct device *dev, struct abx500_ops **ops)
+{
+       struct abx500_device_entry *dev_entry;
+
+       *ops = NULL;
+       list_for_each_entry(dev_entry, &abx500_list, list) {
+               if (dev_entry->dev == dev) {
+                       *ops = &dev_entry->ops;
+                       return;
+               }
+       }
+}
+
+int abx500_register_ops(struct device *dev, struct abx500_ops *ops)
+{
+       struct abx500_device_entry *dev_entry;
+
+       dev_entry = kzalloc(sizeof(struct abx500_device_entry), GFP_KERNEL);
+       if (IS_ERR(dev_entry)) {
+               dev_err(dev, "register_ops kzalloc failed");
+               return -ENOMEM;
+       }
+       dev_entry->dev = dev;
+       memcpy(&dev_entry->ops, ops, sizeof(struct abx500_ops));
+
+       list_add_tail(&dev_entry->list, &abx500_list);
+       return 0;
+}
+EXPORT_SYMBOL(abx500_register_ops);
+
+void abx500_remove_ops(struct device *dev)
+{
+       struct abx500_device_entry *dev_entry, *tmp;
+
+       list_for_each_entry_safe(dev_entry, tmp, &abx500_list, list)
+       {
+               if (dev_entry->dev == dev) {
+                       list_del(&dev_entry->list);
+                       kfree(dev_entry);
+               }
+       }
+}
+EXPORT_SYMBOL(abx500_remove_ops);
+
+int abx500_set_register_interruptible(struct device *dev, u8 bank, u8 reg,
+       u8 value)
+{
+       struct abx500_ops *ops;
+
+       lookup_ops(dev->parent, &ops);
+       if ((ops != NULL) && (ops->set_register != NULL))
+               return ops->set_register(dev, bank, reg, value);
+       else
+               return -ENOTSUPP;
+}
+EXPORT_SYMBOL(abx500_set_register_interruptible);
+
+int abx500_get_register_interruptible(struct device *dev, u8 bank, u8 reg,
+       u8 *value)
+{
+       struct abx500_ops *ops;
+
+       lookup_ops(dev->parent, &ops);
+       if ((ops != NULL) && (ops->get_register != NULL))
+               return ops->get_register(dev, bank, reg, value);
+       else
+               return -ENOTSUPP;
+}
+EXPORT_SYMBOL(abx500_get_register_interruptible);
+
+int abx500_get_register_page_interruptible(struct device *dev, u8 bank,
+       u8 first_reg, u8 *regvals, u8 numregs)
+{
+       struct abx500_ops *ops;
+
+       lookup_ops(dev->parent, &ops);
+       if ((ops != NULL) && (ops->get_register_page != NULL))
+               return ops->get_register_page(dev, bank,
+                       first_reg, regvals, numregs);
+       else
+               return -ENOTSUPP;
+}
+EXPORT_SYMBOL(abx500_get_register_page_interruptible);
+
+int abx500_mask_and_set_register_interruptible(struct device *dev, u8 bank,
+       u8 reg, u8 bitmask, u8 bitvalues)
+{
+       struct abx500_ops *ops;
+
+       lookup_ops(dev->parent, &ops);
+       if ((ops != NULL) && (ops->mask_and_set_register != NULL))
+               return ops->mask_and_set_register(dev, bank,
+                       reg, bitmask, bitvalues);
+       else
+               return -ENOTSUPP;
+}
+EXPORT_SYMBOL(abx500_mask_and_set_register_interruptible);
+
+int abx500_get_chip_id(struct device *dev)
+{
+       struct abx500_ops *ops;
+
+       lookup_ops(dev->parent, &ops);
+       if ((ops != NULL) && (ops->get_chip_id != NULL))
+               return ops->get_chip_id(dev);
+       else
+               return -ENOTSUPP;
+}
+EXPORT_SYMBOL(abx500_get_chip_id);
+
+int abx500_event_registers_startup_state_get(struct device *dev, u8 *event)
+{
+       struct abx500_ops *ops;
+
+       lookup_ops(dev->parent, &ops);
+       if ((ops != NULL) && (ops->event_registers_startup_state_get != NULL))
+               return ops->event_registers_startup_state_get(dev, event);
+       else
+               return -ENOTSUPP;
+}
+EXPORT_SYMBOL(abx500_event_registers_startup_state_get);
+
+int abx500_startup_irq_enabled(struct device *dev, unsigned int irq)
+{
+       struct abx500_ops *ops;
+
+       lookup_ops(dev->parent, &ops);
+       if ((ops != NULL) && (ops->startup_irq_enabled != NULL))
+               return ops->startup_irq_enabled(dev, irq);
+       else
+               return -ENOTSUPP;
+}
+EXPORT_SYMBOL(abx500_startup_irq_enabled);
+
+MODULE_AUTHOR("Mattias Wallin <mattias.wallin@stericsson.com>");
+MODULE_DESCRIPTION("ABX500 core driver");
+MODULE_LICENSE("GPL");
index 67181b1..3ad915d 100644 (file)
@@ -544,6 +544,7 @@ static int __devexit da903x_remove(struct i2c_client *client)
        struct da903x_chip *chip = i2c_get_clientdata(client);
 
        da903x_remove_subdevs(chip);
+       i2c_set_clientdata(client, NULL);
        kfree(chip);
        return 0;
 }
diff --git a/drivers/mfd/janz-cmodio.c b/drivers/mfd/janz-cmodio.c
new file mode 100644 (file)
index 0000000..9ed6307
--- /dev/null
@@ -0,0 +1,304 @@
+/*
+ * Janz CMOD-IO MODULbus Carrier Board PCI Driver
+ *
+ * Copyright (c) 2010 Ira W. Snyder <iws@ovro.caltech.edu>
+ *
+ * Lots of inspiration and code was copied from drivers/mfd/sm501.c
+ *
+ * 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/pci.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/core.h>
+
+#include <linux/mfd/janz.h>
+
+#define DRV_NAME "janz-cmodio"
+
+/* Size of each MODULbus module in PCI BAR4 */
+#define CMODIO_MODULBUS_SIZE   0x200
+
+/* Maximum number of MODULbus modules on a CMOD-IO carrier board */
+#define CMODIO_MAX_MODULES     4
+
+/* Module Parameters */
+static unsigned int num_modules = CMODIO_MAX_MODULES;
+static unsigned char *modules[CMODIO_MAX_MODULES] = {
+       "empty", "empty", "empty", "empty",
+};
+
+module_param_array(modules, charp, &num_modules, S_IRUGO);
+MODULE_PARM_DESC(modules, "MODULbus modules attached to the carrier board");
+
+/* Unique Device Id */
+static unsigned int cmodio_id;
+
+struct cmodio_device {
+       /* Parent PCI device */
+       struct pci_dev *pdev;
+
+       /* PLX control registers */
+       struct janz_cmodio_onboard_regs __iomem *ctrl;
+
+       /* hex switch position */
+       u8 hex;
+
+       /* mfd-core API */
+       struct mfd_cell cells[CMODIO_MAX_MODULES];
+       struct resource resources[3 * CMODIO_MAX_MODULES];
+       struct janz_platform_data pdata[CMODIO_MAX_MODULES];
+};
+
+/*
+ * Subdevices using the mfd-core API
+ */
+
+static int __devinit cmodio_setup_subdevice(struct cmodio_device *priv,
+                                           char *name, unsigned int devno,
+                                           unsigned int modno)
+{
+       struct janz_platform_data *pdata;
+       struct mfd_cell *cell;
+       struct resource *res;
+       struct pci_dev *pci;
+
+       pci = priv->pdev;
+       cell = &priv->cells[devno];
+       res = &priv->resources[devno * 3];
+       pdata = &priv->pdata[devno];
+
+       cell->name = name;
+       cell->resources = res;
+       cell->num_resources = 3;
+
+       /* Setup the subdevice ID -- must be unique */
+       cell->id = cmodio_id++;
+
+       /* Add platform data */
+       pdata->modno = modno;
+       cell->platform_data = pdata;
+       cell->data_size = sizeof(*pdata);
+
+       /* MODULbus registers -- PCI BAR3 is big-endian MODULbus access */
+       res->flags = IORESOURCE_MEM;
+       res->parent = &pci->resource[3];
+       res->start = pci->resource[3].start + (CMODIO_MODULBUS_SIZE * modno);
+       res->end = res->start + CMODIO_MODULBUS_SIZE - 1;
+       res++;
+
+       /* PLX Control Registers -- PCI BAR4 is interrupt and other registers */
+       res->flags = IORESOURCE_MEM;
+       res->parent = &pci->resource[4];
+       res->start = pci->resource[4].start;
+       res->end = pci->resource[4].end;
+       res++;
+
+       /*
+        * IRQ
+        *
+        * The start and end fields are used as an offset to the irq_base
+        * parameter passed into the mfd_add_devices() function call. All
+        * devices share the same IRQ.
+        */
+       res->flags = IORESOURCE_IRQ;
+       res->parent = NULL;
+       res->start = 0;
+       res->end = 0;
+       res++;
+
+       return 0;
+}
+
+/* Probe each submodule using kernel parameters */
+static int __devinit cmodio_probe_submodules(struct cmodio_device *priv)
+{
+       struct pci_dev *pdev = priv->pdev;
+       unsigned int num_probed = 0;
+       char *name;
+       int i;
+
+       for (i = 0; i < num_modules; i++) {
+               name = modules[i];
+               if (!strcmp(name, "") || !strcmp(name, "empty"))
+                       continue;
+
+               dev_dbg(&priv->pdev->dev, "MODULbus %d: name %s\n", i, name);
+               cmodio_setup_subdevice(priv, name, num_probed, i);
+               num_probed++;
+       }
+
+       /* print an error message if no modules were probed */
+       if (num_probed == 0) {
+               dev_err(&priv->pdev->dev, "no MODULbus modules specified, "
+                                         "please set the ``modules'' kernel "
+                                         "parameter according to your "
+                                         "hardware configuration\n");
+               return -ENODEV;
+       }
+
+       return mfd_add_devices(&pdev->dev, 0, priv->cells,
+                              num_probed, NULL, pdev->irq);
+}
+
+/*
+ * SYSFS Attributes
+ */
+
+static ssize_t mbus_show(struct device *dev, struct device_attribute *attr,
+                        char *buf)
+{
+       struct cmodio_device *priv = dev_get_drvdata(dev);
+
+       return snprintf(buf, PAGE_SIZE, "%x\n", priv->hex);
+}
+
+static DEVICE_ATTR(modulbus_number, S_IRUGO, mbus_show, NULL);
+
+static struct attribute *cmodio_sysfs_attrs[] = {
+       &dev_attr_modulbus_number.attr,
+       NULL,
+};
+
+static const struct attribute_group cmodio_sysfs_attr_group = {
+       .attrs = cmodio_sysfs_attrs,
+};
+
+/*
+ * PCI Driver
+ */
+
+static int __devinit cmodio_pci_probe(struct pci_dev *dev,
+                                     const struct pci_device_id *id)
+{
+       struct cmodio_device *priv;
+       int ret;
+
+       priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+       if (!priv) {
+               dev_err(&dev->dev, "unable to allocate private data\n");
+               ret = -ENOMEM;
+               goto out_return;
+       }
+
+       pci_set_drvdata(dev, priv);
+       priv->pdev = dev;
+
+       /* Hardware Initialization */
+       ret = pci_enable_device(dev);
+       if (ret) {
+               dev_err(&dev->dev, "unable to enable device\n");
+               goto out_free_priv;
+       }
+
+       pci_set_master(dev);
+       ret = pci_request_regions(dev, DRV_NAME);
+       if (ret) {
+               dev_err(&dev->dev, "unable to request regions\n");
+               goto out_pci_disable_device;
+       }
+
+       /* Onboard configuration registers */
+       priv->ctrl = pci_ioremap_bar(dev, 4);
+       if (!priv->ctrl) {
+               dev_err(&dev->dev, "unable to remap onboard regs\n");
+               ret = -ENOMEM;
+               goto out_pci_release_regions;
+       }
+
+       /* Read the hex switch on the carrier board */
+       priv->hex = ioread8(&priv->ctrl->int_enable);
+
+       /* Add the MODULbus number (hex switch value) to the device's sysfs */
+       ret = sysfs_create_group(&dev->dev.kobj, &cmodio_sysfs_attr_group);
+       if (ret) {
+               dev_err(&dev->dev, "unable to create sysfs attributes\n");
+               goto out_unmap_ctrl;
+       }
+
+       /*
+        * Disable all interrupt lines, each submodule will enable its
+        * own interrupt line if needed
+        */
+       iowrite8(0xf, &priv->ctrl->int_disable);
+
+       /* Register drivers for all submodules */
+       ret = cmodio_probe_submodules(priv);
+       if (ret) {
+               dev_err(&dev->dev, "unable to probe submodules\n");
+               goto out_sysfs_remove_group;
+       }
+
+       return 0;
+
+out_sysfs_remove_group:
+       sysfs_remove_group(&dev->dev.kobj, &cmodio_sysfs_attr_group);
+out_unmap_ctrl:
+       iounmap(priv->ctrl);
+out_pci_release_regions:
+       pci_release_regions(dev);
+out_pci_disable_device:
+       pci_disable_device(dev);
+out_free_priv:
+       kfree(priv);
+out_return:
+       return ret;
+}
+
+static void __devexit cmodio_pci_remove(struct pci_dev *dev)
+{
+       struct cmodio_device *priv = pci_get_drvdata(dev);
+
+       mfd_remove_devices(&dev->dev);
+       sysfs_remove_group(&dev->dev.kobj, &cmodio_sysfs_attr_group);
+       iounmap(priv->ctrl);
+       pci_release_regions(dev);
+       pci_disable_device(dev);
+       kfree(priv);
+}
+
+#define PCI_VENDOR_ID_JANZ             0x13c3
+
+/* The list of devices that this module will support */
+static DEFINE_PCI_DEVICE_TABLE(cmodio_pci_ids) = {
+       { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9030, PCI_VENDOR_ID_JANZ, 0x0101 },
+       { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050, PCI_VENDOR_ID_JANZ, 0x0100 },
+       { 0, }
+};
+MODULE_DEVICE_TABLE(pci, cmodio_pci_ids);
+
+static struct pci_driver cmodio_pci_driver = {
+       .name     = DRV_NAME,
+       .id_table = cmodio_pci_ids,
+       .probe    = cmodio_pci_probe,
+       .remove   = __devexit_p(cmodio_pci_remove),
+};
+
+/*
+ * Module Init / Exit
+ */
+
+static int __init cmodio_init(void)
+{
+       return pci_register_driver(&cmodio_pci_driver);
+}
+
+static void __exit cmodio_exit(void)
+{
+       pci_unregister_driver(&cmodio_pci_driver);
+}
+
+MODULE_AUTHOR("Ira W. Snyder <iws@ovro.caltech.edu>");
+MODULE_DESCRIPTION("Janz CMOD-IO PCI MODULbus Carrier Board Driver");
+MODULE_LICENSE("GPL");
+
+module_init(cmodio_init);
+module_exit(cmodio_exit);
index 85d63c0..f621bce 100644 (file)
@@ -508,7 +508,7 @@ static int max8925_irq_init(struct max8925_chip *chip, int irq,
        max8925_reg_read(chip->i2c, MAX8925_ON_OFF_IRQ2);
        max8925_reg_read(chip->rtc, MAX8925_RTC_IRQ);
        max8925_reg_read(chip->adc, MAX8925_TSC_IRQ);
-       /* mask all interrupts */
+       /* mask all interrupts except for TSC */
        max8925_reg_write(chip->rtc, MAX8925_ALARM0_CNTL, 0);
        max8925_reg_write(chip->rtc, MAX8925_ALARM1_CNTL, 0);
        max8925_reg_write(chip->i2c, MAX8925_CHG_IRQ1_MASK, 0xff);
@@ -516,7 +516,6 @@ static int max8925_irq_init(struct max8925_chip *chip, int irq,
        max8925_reg_write(chip->i2c, MAX8925_ON_OFF_IRQ1_MASK, 0xff);
        max8925_reg_write(chip->i2c, MAX8925_ON_OFF_IRQ2_MASK, 0xff);
        max8925_reg_write(chip->rtc, MAX8925_RTC_IRQ_MASK, 0xff);
-       max8925_reg_write(chip->adc, MAX8925_TSC_IRQ_MASK, 0xff);
 
        mutex_init(&chip->irq_lock);
        chip->core_irq = irq;
@@ -547,7 +546,11 @@ static int max8925_irq_init(struct max8925_chip *chip, int irq,
                dev_err(chip->dev, "Failed to request core IRQ: %d\n", ret);
                chip->core_irq = 0;
        }
+
 tsc_irq:
+       /* mask TSC interrupt */
+       max8925_reg_write(chip->adc, MAX8925_TSC_IRQ_MASK, 0x0f);
+
        if (!pdata->tsc_irq) {
                dev_warn(chip->dev, "No interrupt support on TSC IRQ\n");
                return 0;
index d9fd878..e73f3f5 100644 (file)
@@ -173,8 +173,6 @@ static int __devexit max8925_remove(struct i2c_client *client)
        max8925_device_exit(chip);
        i2c_unregister_device(chip->adc);
        i2c_unregister_device(chip->rtc);
-       i2c_set_clientdata(chip->adc, NULL);
-       i2c_set_clientdata(chip->rtc, NULL);
        i2c_set_clientdata(chip->i2c, NULL);
        kfree(chip);
        return 0;
index 1f68eca..fecf38a 100644 (file)
@@ -679,6 +679,10 @@ err_revision:
        if (pdata->flags & MC13783_USE_TOUCHSCREEN)
                mc13783_add_subdevice(mc13783, "mc13783-ts");
 
+       if (pdata->flags & MC13783_USE_LED)
+               mc13783_add_subdevice_pdata(mc13783, "mc13783-led",
+                                       pdata->leds, sizeof(*pdata->leds));
+
        return 0;
 }
 
index a94b131..721948b 100644 (file)
@@ -1228,6 +1228,7 @@ fail2:
        free_irq(client->irq, menelaus);
        flush_scheduled_work();
 fail1:
+       i2c_set_clientdata(client, NULL);
        kfree(menelaus);
        return err;
 }
@@ -1237,8 +1238,8 @@ static int __exit menelaus_remove(struct i2c_client *client)
        struct menelaus_chip    *menelaus = i2c_get_clientdata(client);
 
        free_irq(client->irq, menelaus);
-       kfree(menelaus);
        i2c_set_clientdata(client, NULL);
+       kfree(menelaus);
        the_menelaus = NULL;
        return 0;
 }
index 8ffbb7a..7dd76bc 100644 (file)
@@ -48,7 +48,7 @@ static int mfd_add_device(struct device *parent, int id,
                res[r].flags = cell->resources[r].flags;
 
                /* Find out base to use */
-               if (cell->resources[r].flags & IORESOURCE_MEM) {
+               if ((cell->resources[r].flags & IORESOURCE_MEM) && mem_base) {
                        res[r].parent = mem_base;
                        res[r].start = mem_base->start +
                                cell->resources[r].start;
index fe8f922..aed0d2a 100644 (file)
 struct pcf50633_adc_request {
        int mux;
        int avg;
-       int result;
        void (*callback)(struct pcf50633 *, void *, int);
        void *callback_param;
+};
 
-       /* Used in case of sync requests */
+struct pcf50633_adc_sync_request {
+       int result;
        struct completion completion;
-
 };
 
 #define PCF50633_MAX_ADC_FIFO_DEPTH 8
@@ -109,10 +109,10 @@ adc_enqueue_request(struct pcf50633 *pcf, struct pcf50633_adc_request *req)
        return 0;
 }
 
-static void
-pcf50633_adc_sync_read_callback(struct pcf50633 *pcf, void *param, int result)
+static void pcf50633_adc_sync_read_callback(struct pcf50633 *pcf, void *param,
+       int result)
 {
-       struct pcf50633_adc_request *req = param;
+       struct pcf50633_adc_sync_request *req = param;
 
        req->result = result;
        complete(&req->completion);
@@ -120,28 +120,19 @@ pcf50633_adc_sync_read_callback(struct pcf50633 *pcf, void *param, int result)
 
 int pcf50633_adc_sync_read(struct pcf50633 *pcf, int mux, int avg)
 {
-       struct pcf50633_adc_request *req;
-       int err;
+       struct pcf50633_adc_sync_request req;
+       int ret;
 
-       /* req is freed when the result is ready, in interrupt handler */
-       req = kzalloc(sizeof(*req), GFP_KERNEL);
-       if (!req)
-               return -ENOMEM;
-
-       req->mux = mux;
-       req->avg = avg;
-       req->callback =  pcf50633_adc_sync_read_callback;
-       req->callback_param = req;
+       init_completion(&req.completion);
 
-       init_completion(&req->completion);
-       err = adc_enqueue_request(pcf, req);
-       if (err)
-               return err;
+       ret = pcf50633_adc_async_read(pcf, mux, avg,
+               pcf50633_adc_sync_read_callback, &req);
+       if (ret)
+               return ret;
 
-       wait_for_completion(&req->completion);
+       wait_for_completion(&req.completion);
 
-       /* FIXME by this time req might be already freed */
-       return req->result;
+       return req.result;
 }
 EXPORT_SYMBOL_GPL(pcf50633_adc_sync_read);
 
index 63a614d..704736e 100644 (file)
 #include <linux/workqueue.h>
 #include <linux/platform_device.h>
 #include <linux/i2c.h>
-#include <linux/irq.h>
 #include <linux/slab.h>
 
 #include <linux/mfd/pcf50633/core.h>
 
-/* Two MBCS registers used during cold start */
-#define PCF50633_REG_MBCS1             0x4b
-#define PCF50633_REG_MBCS2             0x4c
-#define PCF50633_MBCS1_USBPRES                 0x01
-#define PCF50633_MBCS1_ADAPTPRES       0x01
+int pcf50633_irq_init(struct pcf50633 *pcf, int irq);
+void pcf50633_irq_free(struct pcf50633 *pcf);
+#ifdef CONFIG_PM
+int pcf50633_irq_suspend(struct pcf50633 *pcf);
+int pcf50633_irq_resume(struct pcf50633 *pcf);
+#endif
 
 static int __pcf50633_read(struct pcf50633 *pcf, u8 reg, int num, u8 *data)
 {
@@ -215,244 +215,6 @@ static struct attribute_group pcf_attr_group = {
        .attrs  = pcf_sysfs_entries,
 };
 
-int pcf50633_register_irq(struct pcf50633 *pcf, int irq,
-                       void (*handler) (int, void *), void *data)
-{
-       if (irq < 0 || irq > PCF50633_NUM_IRQ || !handler)
-               return -EINVAL;
-
-       if (WARN_ON(pcf->irq_handler[irq].handler))
-               return -EBUSY;
-
-       mutex_lock(&pcf->lock);
-       pcf->irq_handler[irq].handler = handler;
-       pcf->irq_handler[irq].data = data;
-       mutex_unlock(&pcf->lock);
-
-       return 0;
-}
-EXPORT_SYMBOL_GPL(pcf50633_register_irq);
-
-int pcf50633_free_irq(struct pcf50633 *pcf, int irq)
-{
-       if (irq < 0 || irq > PCF50633_NUM_IRQ)
-               return -EINVAL;
-
-       mutex_lock(&pcf->lock);
-       pcf->irq_handler[irq].handler = NULL;
-       mutex_unlock(&pcf->lock);
-
-       return 0;
-}
-EXPORT_SYMBOL_GPL(pcf50633_free_irq);
-
-static int __pcf50633_irq_mask_set(struct pcf50633 *pcf, int irq, u8 mask)
-{
-       u8 reg, bits, tmp;
-       int ret = 0, idx;
-
-       idx = irq >> 3;
-       reg =  PCF50633_REG_INT1M + idx;
-       bits = 1 << (irq & 0x07);
-
-       mutex_lock(&pcf->lock);
-
-       if (mask) {
-               ret = __pcf50633_read(pcf, reg, 1, &tmp);
-               if (ret < 0)
-                       goto out;
-
-               tmp |= bits;
-
-               ret = __pcf50633_write(pcf, reg, 1, &tmp);
-               if (ret < 0)
-                       goto out;
-
-               pcf->mask_regs[idx] &= ~bits;
-               pcf->mask_regs[idx] |= bits;
-       } else {
-               ret = __pcf50633_read(pcf, reg, 1, &tmp);
-               if (ret < 0)
-                       goto out;
-
-               tmp &= ~bits;
-
-               ret = __pcf50633_write(pcf, reg, 1, &tmp);
-               if (ret < 0)
-                       goto out;
-
-               pcf->mask_regs[idx] &= ~bits;
-       }
-out:
-       mutex_unlock(&pcf->lock);
-
-       return ret;
-}
-
-int pcf50633_irq_mask(struct pcf50633 *pcf, int irq)
-{
-       dev_dbg(pcf->dev, "Masking IRQ %d\n", irq);
-
-       return __pcf50633_irq_mask_set(pcf, irq, 1);
-}
-EXPORT_SYMBOL_GPL(pcf50633_irq_mask);
-
-int pcf50633_irq_unmask(struct pcf50633 *pcf, int irq)
-{
-       dev_dbg(pcf->dev, "Unmasking IRQ %d\n", irq);
-
-       return __pcf50633_irq_mask_set(pcf, irq, 0);
-}
-EXPORT_SYMBOL_GPL(pcf50633_irq_unmask);
-
-int pcf50633_irq_mask_get(struct pcf50633 *pcf, int irq)
-{
-       u8 reg, bits;
-
-       reg =  irq >> 3;
-       bits = 1 << (irq & 0x07);
-
-       return pcf->mask_regs[reg] & bits;
-}
-EXPORT_SYMBOL_GPL(pcf50633_irq_mask_get);
-
-static void pcf50633_irq_call_handler(struct pcf50633 *pcf, int irq)
-{
-       if (pcf->irq_handler[irq].handler)
-               pcf->irq_handler[irq].handler(irq, pcf->irq_handler[irq].data);
-}
-
-/* Maximum amount of time ONKEY is held before emergency action is taken */
-#define PCF50633_ONKEY1S_TIMEOUT 8
-
-static void pcf50633_irq_worker(struct work_struct *work)
-{
-       struct pcf50633 *pcf;
-       int ret, i, j;
-       u8 pcf_int[5], chgstat;
-
-       pcf = container_of(work, struct pcf50633, irq_work);
-
-       /* Read the 5 INT regs in one transaction */
-       ret = pcf50633_read_block(pcf, PCF50633_REG_INT1,
-                                               ARRAY_SIZE(pcf_int), pcf_int);
-       if (ret != ARRAY_SIZE(pcf_int)) {
-               dev_err(pcf->dev, "Error reading INT registers\n");
-
-               /*
-                * If this doesn't ACK the interrupt to the chip, we'll be
-                * called once again as we're level triggered.
-                */
-               goto out;
-       }
-
-       /* defeat 8s death from lowsys on A5 */
-       pcf50633_reg_write(pcf, PCF50633_REG_OOCSHDWN,  0x04);
-
-       /* We immediately read the usb and adapter status. We thus make sure
-        * only of USBINS/USBREM IRQ handlers are called */
-       if (pcf_int[0] & (PCF50633_INT1_USBINS | PCF50633_INT1_USBREM)) {
-               chgstat = pcf50633_reg_read(pcf, PCF50633_REG_MBCS2);
-               if (chgstat & (0x3 << 4))
-                       pcf_int[0] &= ~(1 << PCF50633_INT1_USBREM);
-               else
-                       pcf_int[0] &= ~(1 << PCF50633_INT1_USBINS);
-       }
-
-       /* Make sure only one of ADPINS or ADPREM is set */
-       if (pcf_int[0] & (PCF50633_INT1_ADPINS | PCF50633_INT1_ADPREM)) {
-               chgstat = pcf50633_reg_read(pcf, PCF50633_REG_MBCS2);
-               if (chgstat & (0x3 << 4))
-                       pcf_int[0] &= ~(1 << PCF50633_INT1_ADPREM);
-               else
-                       pcf_int[0] &= ~(1 << PCF50633_INT1_ADPINS);
-       }
-
-       dev_dbg(pcf->dev, "INT1=0x%02x INT2=0x%02x INT3=0x%02x "
-                       "INT4=0x%02x INT5=0x%02x\n", pcf_int[0],
-                       pcf_int[1], pcf_int[2], pcf_int[3], pcf_int[4]);
-
-       /* Some revisions of the chip don't have a 8s standby mode on
-        * ONKEY1S press. We try to manually do it in such cases. */
-       if ((pcf_int[0] & PCF50633_INT1_SECOND) && pcf->onkey1s_held) {
-               dev_info(pcf->dev, "ONKEY1S held for %d secs\n",
-                                                       pcf->onkey1s_held);
-               if (pcf->onkey1s_held++ == PCF50633_ONKEY1S_TIMEOUT)
-                       if (pcf->pdata->force_shutdown)
-                               pcf->pdata->force_shutdown(pcf);
-       }
-
-       if (pcf_int[2] & PCF50633_INT3_ONKEY1S) {
-               dev_info(pcf->dev, "ONKEY1S held\n");
-               pcf->onkey1s_held = 1 ;
-
-               /* Unmask IRQ_SECOND */
-               pcf50633_reg_clear_bits(pcf, PCF50633_REG_INT1M,
-                                               PCF50633_INT1_SECOND);
-
-               /* Unmask IRQ_ONKEYR */
-               pcf50633_reg_clear_bits(pcf, PCF50633_REG_INT2M,
-                                               PCF50633_INT2_ONKEYR);
-       }
-
-       if ((pcf_int[1] & PCF50633_INT2_ONKEYR) && pcf->onkey1s_held) {
-               pcf->onkey1s_held = 0;
-
-               /* Mask SECOND and ONKEYR interrupts */
-               if (pcf->mask_regs[0] & PCF50633_INT1_SECOND)
-                       pcf50633_reg_set_bit_mask(pcf,
-                                       PCF50633_REG_INT1M,
-                                       PCF50633_INT1_SECOND,
-                                       PCF50633_INT1_SECOND);
-
-               if (pcf->mask_regs[1] & PCF50633_INT2_ONKEYR)
-                       pcf50633_reg_set_bit_mask(pcf,
-                                       PCF50633_REG_INT2M,
-                                       PCF50633_INT2_ONKEYR,
-                                       PCF50633_INT2_ONKEYR);
-       }
-
-       /* Have we just resumed ? */
-       if (pcf->is_suspended) {
-               pcf->is_suspended = 0;
-
-               /* Set the resume reason filtering out non resumers */
-               for (i = 0; i < ARRAY_SIZE(pcf_int); i++)
-                       pcf->resume_reason[i] = pcf_int[i] &
-                                               pcf->pdata->resumers[i];
-
-               /* Make sure we don't pass on any ONKEY events to
-                * userspace now */
-               pcf_int[1] &= ~(PCF50633_INT2_ONKEYR | PCF50633_INT2_ONKEYF);
-       }
-
-       for (i = 0; i < ARRAY_SIZE(pcf_int); i++) {
-               /* Unset masked interrupts */
-               pcf_int[i] &= ~pcf->mask_regs[i];
-
-               for (j = 0; j < 8 ; j++)
-                       if (pcf_int[i] & (1 << j))
-                               pcf50633_irq_call_handler(pcf, (i * 8) + j);
-       }
-
-out:
-       put_device(pcf->dev);
-       enable_irq(pcf->irq);
-}
-
-static irqreturn_t pcf50633_irq(int irq, void *data)
-{
-       struct pcf50633 *pcf = data;
-
-       dev_dbg(pcf->dev, "pcf50633_irq\n");
-
-       get_device(pcf->dev);
-       disable_irq_nosync(pcf->irq);
-       queue_work(pcf->work_queue, &pcf->irq_work);
-
-       return IRQ_HANDLED;
-}
-
 static void
 pcf50633_client_dev_register(struct pcf50633 *pcf, const char *name,
                                                struct platform_device **pdev)
@@ -479,70 +241,17 @@ pcf50633_client_dev_register(struct pcf50633 *pcf, const char *name,
 static int pcf50633_suspend(struct i2c_client *client, pm_message_t state)
 {
        struct pcf50633 *pcf;
-       int ret = 0, i;
-       u8 res[5];
-
        pcf = i2c_get_clientdata(client);
 
-       /* Make sure our interrupt handlers are not called
-        * henceforth */
-       disable_irq(pcf->irq);
-
-       /* Make sure that any running IRQ worker has quit */
-       cancel_work_sync(&pcf->irq_work);
-
-       /* Save the masks */
-       ret = pcf50633_read_block(pcf, PCF50633_REG_INT1M,
-                               ARRAY_SIZE(pcf->suspend_irq_masks),
-                                       pcf->suspend_irq_masks);
-       if (ret < 0) {
-               dev_err(pcf->dev, "error saving irq masks\n");
-               goto out;
-       }
-
-       /* Write wakeup irq masks */
-       for (i = 0; i < ARRAY_SIZE(res); i++)
-               res[i] = ~pcf->pdata->resumers[i];
-
-       ret = pcf50633_write_block(pcf, PCF50633_REG_INT1M,
-                                       ARRAY_SIZE(res), &res[0]);
-       if (ret < 0) {
-               dev_err(pcf->dev, "error writing wakeup irq masks\n");
-               goto out;
-       }
-
-       pcf->is_suspended = 1;
-
-out:
-       return ret;
+       return pcf50633_irq_suspend(pcf);
 }
 
 static int pcf50633_resume(struct i2c_client *client)
 {
        struct pcf50633 *pcf;
-       int ret;
-
        pcf = i2c_get_clientdata(client);
 
-       /* Write the saved mask registers */
-       ret = pcf50633_write_block(pcf, PCF50633_REG_INT1M,
-                               ARRAY_SIZE(pcf->suspend_irq_masks),
-                                       pcf->suspend_irq_masks);
-       if (ret < 0)
-               dev_err(pcf->dev, "Error restoring saved suspend masks\n");
-
-       /* Restore regulators' state */
-
-
-       get_device(pcf->dev);
-
-       /*
-        * Clear any pending interrupts and set resume reason if any.
-        * This will leave with enable_irq()
-        */
-       pcf50633_irq_worker(&pcf->irq_work);
-
-       return 0;
+       return pcf50633_irq_resume(pcf);
 }
 #else
 #define pcf50633_suspend NULL
@@ -573,43 +282,19 @@ static int __devinit pcf50633_probe(struct i2c_client *client,
        i2c_set_clientdata(client, pcf);
        pcf->dev = &client->dev;
        pcf->i2c_client = client;
-       pcf->irq = client->irq;
-       pcf->work_queue = create_singlethread_workqueue("pcf50633");
-
-       if (!pcf->work_queue) {
-               dev_err(&client->dev, "Failed to alloc workqueue\n");
-               ret = -ENOMEM;
-               goto err_free;
-       }
-
-       INIT_WORK(&pcf->irq_work, pcf50633_irq_worker);
 
        version = pcf50633_reg_read(pcf, 0);
        variant = pcf50633_reg_read(pcf, 1);
        if (version < 0 || variant < 0) {
                dev_err(pcf->dev, "Unable to probe pcf50633\n");
                ret = -ENODEV;
-               goto err_destroy_workqueue;
+               goto err_free;
        }
 
        dev_info(pcf->dev, "Probed device version %d variant %d\n",
                                                        version, variant);
 
-       /* Enable all interrupts except RTC SECOND */
-       pcf->mask_regs[0] = 0x80;
-       pcf50633_reg_write(pcf, PCF50633_REG_INT1M, pcf->mask_regs[0]);
-       pcf50633_reg_write(pcf, PCF50633_REG_INT2M, 0x00);
-       pcf50633_reg_write(pcf, PCF50633_REG_INT3M, 0x00);
-       pcf50633_reg_write(pcf, PCF50633_REG_INT4M, 0x00);
-       pcf50633_reg_write(pcf, PCF50633_REG_INT5M, 0x00);
-
-       ret = request_irq(client->irq, pcf50633_irq,
-                                       IRQF_TRIGGER_LOW, "pcf50633", pcf);
-
-       if (ret) {
-               dev_err(pcf->dev, "Failed to request IRQ %d\n", ret);
-               goto err_destroy_workqueue;
-       }
+       pcf50633_irq_init(pcf, client->irq);
 
        /* Create sub devices */
        pcf50633_client_dev_register(pcf, "pcf50633-input",
@@ -620,6 +305,9 @@ static int __devinit pcf50633_probe(struct i2c_client *client,
                                                &pcf->mbc_pdev);
        pcf50633_client_dev_register(pcf, "pcf50633-adc",
                                                &pcf->adc_pdev);
+       pcf50633_client_dev_register(pcf, "pcf50633-backlight",
+                                               &pcf->bl_pdev);
+
 
        for (i = 0; i < PCF50633_NUM_REGULATORS; i++) {
                struct platform_device *pdev;
@@ -638,10 +326,6 @@ static int __devinit pcf50633_probe(struct i2c_client *client,
                platform_device_add(pdev);
        }
 
-       if (enable_irq_wake(client->irq) < 0)
-               dev_err(pcf->dev, "IRQ %u cannot be enabled as wake-up source"
-                       "in this hardware revision", client->irq);
-
        ret = sysfs_create_group(&client->dev.kobj, &pcf_attr_group);
        if (ret)
                dev_err(pcf->dev, "error creating sysfs entries\n");
@@ -651,8 +335,6 @@ static int __devinit pcf50633_probe(struct i2c_client *client,
 
        return 0;
 
-err_destroy_workqueue:
-       destroy_workqueue(pcf->work_queue);
 err_free:
        i2c_set_clientdata(client, NULL);
        kfree(pcf);
@@ -665,8 +347,7 @@ static int __devexit pcf50633_remove(struct i2c_client *client)
        struct pcf50633 *pcf = i2c_get_clientdata(client);
        int i;
 
-       free_irq(pcf->irq, pcf);
-       destroy_workqueue(pcf->work_queue);
+       pcf50633_irq_free(pcf);
 
        platform_device_unregister(pcf->input_pdev);
        platform_device_unregister(pcf->rtc_pdev);
@@ -676,6 +357,7 @@ static int __devexit pcf50633_remove(struct i2c_client *client)
        for (i = 0; i < PCF50633_NUM_REGULATORS; i++)
                platform_device_unregister(pcf->regulator_pdev[i]);
 
+       i2c_set_clientdata(client, NULL);
        kfree(pcf);
 
        return 0;
diff --git a/drivers/mfd/pcf50633-irq.c b/drivers/mfd/pcf50633-irq.c
new file mode 100644 (file)
index 0000000..1b0192f
--- /dev/null
@@ -0,0 +1,318 @@
+/* NXP PCF50633 Power Management Unit (PMU) driver
+ *
+ * (C) 2006-2008 by Openmoko, Inc.
+ * Author: Harald Welte <laforge@openmoko.org>
+ *        Balaji Rao <balajirrao@openmoko.org>
+ * 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.
+ *
+ */
+
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+
+#include <linux/mfd/pcf50633/core.h>
+
+/* Two MBCS registers used during cold start */
+#define PCF50633_REG_MBCS1             0x4b
+#define PCF50633_REG_MBCS2             0x4c
+#define PCF50633_MBCS1_USBPRES                 0x01
+#define PCF50633_MBCS1_ADAPTPRES       0x01
+
+int pcf50633_register_irq(struct pcf50633 *pcf, int irq,
+                       void (*handler) (int, void *), void *data)
+{
+       if (irq < 0 || irq >= PCF50633_NUM_IRQ || !handler)
+               return -EINVAL;
+
+       if (WARN_ON(pcf->irq_handler[irq].handler))
+               return -EBUSY;
+
+       mutex_lock(&pcf->lock);
+       pcf->irq_handler[irq].handler = handler;
+       pcf->irq_handler[irq].data = data;
+       mutex_unlock(&pcf->lock);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(pcf50633_register_irq);
+
+int pcf50633_free_irq(struct pcf50633 *pcf, int irq)
+{
+       if (irq < 0 || irq >= PCF50633_NUM_IRQ)
+               return -EINVAL;
+
+       mutex_lock(&pcf->lock);
+       pcf->irq_handler[irq].handler = NULL;
+       mutex_unlock(&pcf->lock);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(pcf50633_free_irq);
+
+static int __pcf50633_irq_mask_set(struct pcf50633 *pcf, int irq, u8 mask)
+{
+       u8 reg, bit;
+       int ret = 0, idx;
+
+       idx = irq >> 3;
+       reg = PCF50633_REG_INT1M + idx;
+       bit = 1 << (irq & 0x07);
+
+       pcf50633_reg_set_bit_mask(pcf, reg, bit, mask ? bit : 0);
+
+       mutex_lock(&pcf->lock);
+
+       if (mask)
+               pcf->mask_regs[idx] |= bit;
+       else
+               pcf->mask_regs[idx] &= ~bit;
+
+       mutex_unlock(&pcf->lock);
+
+       return ret;
+}
+
+int pcf50633_irq_mask(struct pcf50633 *pcf, int irq)
+{
+       dev_dbg(pcf->dev, "Masking IRQ %d\n", irq);
+
+       return __pcf50633_irq_mask_set(pcf, irq, 1);
+}
+EXPORT_SYMBOL_GPL(pcf50633_irq_mask);
+
+int pcf50633_irq_unmask(struct pcf50633 *pcf, int irq)
+{
+       dev_dbg(pcf->dev, "Unmasking IRQ %d\n", irq);
+
+       return __pcf50633_irq_mask_set(pcf, irq, 0);
+}
+EXPORT_SYMBOL_GPL(pcf50633_irq_unmask);
+
+int pcf50633_irq_mask_get(struct pcf50633 *pcf, int irq)
+{
+       u8 reg, bits;
+
+       reg =  irq >> 3;
+       bits = 1 << (irq & 0x07);
+
+       return pcf->mask_regs[reg] & bits;
+}
+EXPORT_SYMBOL_GPL(pcf50633_irq_mask_get);
+
+static void pcf50633_irq_call_handler(struct pcf50633 *pcf, int irq)
+{
+       if (pcf->irq_handler[irq].handler)
+               pcf->irq_handler[irq].handler(irq, pcf->irq_handler[irq].data);
+}
+
+/* Maximum amount of time ONKEY is held before emergency action is taken */
+#define PCF50633_ONKEY1S_TIMEOUT 8
+
+static irqreturn_t pcf50633_irq(int irq, void *data)
+{
+       struct pcf50633 *pcf = data;
+       int ret, i, j;
+       u8 pcf_int[5], chgstat;
+
+       /* Read the 5 INT regs in one transaction */
+       ret = pcf50633_read_block(pcf, PCF50633_REG_INT1,
+                                               ARRAY_SIZE(pcf_int), pcf_int);
+       if (ret != ARRAY_SIZE(pcf_int)) {
+               dev_err(pcf->dev, "Error reading INT registers\n");
+
+               /*
+                * If this doesn't ACK the interrupt to the chip, we'll be
+                * called once again as we're level triggered.
+                */
+               goto out;
+       }
+
+       /* defeat 8s death from lowsys on A5 */
+       pcf50633_reg_write(pcf, PCF50633_REG_OOCSHDWN,  0x04);
+
+       /* We immediately read the usb and adapter status. We thus make sure
+        * only of USBINS/USBREM IRQ handlers are called */
+       if (pcf_int[0] & (PCF50633_INT1_USBINS | PCF50633_INT1_USBREM)) {
+               chgstat = pcf50633_reg_read(pcf, PCF50633_REG_MBCS2);
+               if (chgstat & (0x3 << 4))
+                       pcf_int[0] &= ~PCF50633_INT1_USBREM;
+               else
+                       pcf_int[0] &= ~PCF50633_INT1_USBINS;
+       }
+
+       /* Make sure only one of ADPINS or ADPREM is set */
+       if (pcf_int[0] & (PCF50633_INT1_ADPINS | PCF50633_INT1_ADPREM)) {
+               chgstat = pcf50633_reg_read(pcf, PCF50633_REG_MBCS2);
+               if (chgstat & (0x3 << 4))
+                       pcf_int[0] &= ~PCF50633_INT1_ADPREM;
+               else
+                       pcf_int[0] &= ~PCF50633_INT1_ADPINS;
+       }
+
+       dev_dbg(pcf->dev, "INT1=0x%02x INT2=0x%02x INT3=0x%02x "
+                       "INT4=0x%02x INT5=0x%02x\n", pcf_int[0],
+                       pcf_int[1], pcf_int[2], pcf_int[3], pcf_int[4]);
+
+       /* Some revisions of the chip don't have a 8s standby mode on
+        * ONKEY1S press. We try to manually do it in such cases. */
+       if ((pcf_int[0] & PCF50633_INT1_SECOND) && pcf->onkey1s_held) {
+               dev_info(pcf->dev, "ONKEY1S held for %d secs\n",
+                                                       pcf->onkey1s_held);
+               if (pcf->onkey1s_held++ == PCF50633_ONKEY1S_TIMEOUT)
+                       if (pcf->pdata->force_shutdown)
+                               pcf->pdata->force_shutdown(pcf);
+       }
+
+       if (pcf_int[2] & PCF50633_INT3_ONKEY1S) {
+               dev_info(pcf->dev, "ONKEY1S held\n");
+               pcf->onkey1s_held = 1 ;
+
+               /* Unmask IRQ_SECOND */
+               pcf50633_reg_clear_bits(pcf, PCF50633_REG_INT1M,
+                                               PCF50633_INT1_SECOND);
+
+               /* Unmask IRQ_ONKEYR */
+               pcf50633_reg_clear_bits(pcf, PCF50633_REG_INT2M,
+                                               PCF50633_INT2_ONKEYR);
+       }
+
+       if ((pcf_int[1] & PCF50633_INT2_ONKEYR) && pcf->onkey1s_held) {
+               pcf->onkey1s_held = 0;
+
+               /* Mask SECOND and ONKEYR interrupts */
+               if (pcf->mask_regs[0] & PCF50633_INT1_SECOND)
+                       pcf50633_reg_set_bit_mask(pcf,
+                                       PCF50633_REG_INT1M,
+                                       PCF50633_INT1_SECOND,
+                                       PCF50633_INT1_SECOND);
+
+               if (pcf->mask_regs[1] & PCF50633_INT2_ONKEYR)
+                       pcf50633_reg_set_bit_mask(pcf,
+                                       PCF50633_REG_INT2M,
+                                       PCF50633_INT2_ONKEYR,
+                                       PCF50633_INT2_ONKEYR);
+       }
+
+       /* Have we just resumed ? */
+       if (pcf->is_suspended) {
+               pcf->is_suspended = 0;
+
+               /* Set the resume reason filtering out non resumers */
+               for (i = 0; i < ARRAY_SIZE(pcf_int); i++)
+                       pcf->resume_reason[i] = pcf_int[i] &
+                                               pcf->pdata->resumers[i];
+
+               /* Make sure we don't pass on any ONKEY events to
+                * userspace now */
+               pcf_int[1] &= ~(PCF50633_INT2_ONKEYR | PCF50633_INT2_ONKEYF);
+       }
+
+       for (i = 0; i < ARRAY_SIZE(pcf_int); i++) {
+               /* Unset masked interrupts */
+               pcf_int[i] &= ~pcf->mask_regs[i];
+
+               for (j = 0; j < 8 ; j++)
+                       if (pcf_int[i] & (1 << j))
+                               pcf50633_irq_call_handler(pcf, (i * 8) + j);
+       }
+
+out:
+       return IRQ_HANDLED;
+}
+
+#ifdef CONFIG_PM
+
+int pcf50633_irq_suspend(struct pcf50633 *pcf)
+{
+       int ret;
+       int i;
+       u8 res[5];
+
+
+       /* Make sure our interrupt handlers are not called
+        * henceforth */
+       disable_irq(pcf->irq);
+
+       /* Save the masks */
+       ret = pcf50633_read_block(pcf, PCF50633_REG_INT1M,
+                               ARRAY_SIZE(pcf->suspend_irq_masks),
+                                       pcf->suspend_irq_masks);
+       if (ret < 0) {
+               dev_err(pcf->dev, "error saving irq masks\n");
+               goto out;
+       }
+
+       /* Write wakeup irq masks */
+       for (i = 0; i < ARRAY_SIZE(res); i++)
+               res[i] = ~pcf->pdata->resumers[i];
+
+       ret = pcf50633_write_block(pcf, PCF50633_REG_INT1M,
+                                       ARRAY_SIZE(res), &res[0]);
+       if (ret < 0) {
+               dev_err(pcf->dev, "error writing wakeup irq masks\n");
+               goto out;
+       }
+
+       pcf->is_suspended = 1;
+
+out:
+       return ret;
+}
+
+int pcf50633_irq_resume(struct pcf50633 *pcf)
+{
+       int ret;
+
+       /* Write the saved mask registers */
+       ret = pcf50633_write_block(pcf, PCF50633_REG_INT1M,
+                               ARRAY_SIZE(pcf->suspend_irq_masks),
+                                       pcf->suspend_irq_masks);
+       if (ret < 0)
+               dev_err(pcf->dev, "Error restoring saved suspend masks\n");
+
+       enable_irq(pcf->irq);
+
+       return ret;
+}
+
+#endif
+
+int pcf50633_irq_init(struct pcf50633 *pcf, int irq)
+{
+       int ret;
+
+       pcf->irq = irq;
+
+       /* Enable all interrupts except RTC SECOND */
+       pcf->mask_regs[0] = 0x80;
+       pcf50633_reg_write(pcf, PCF50633_REG_INT1M, pcf->mask_regs[0]);
+       pcf50633_reg_write(pcf, PCF50633_REG_INT2M, 0x00);
+       pcf50633_reg_write(pcf, PCF50633_REG_INT3M, 0x00);
+       pcf50633_reg_write(pcf, PCF50633_REG_INT4M, 0x00);
+       pcf50633_reg_write(pcf, PCF50633_REG_INT5M, 0x00);
+
+       ret = request_threaded_irq(irq, NULL, pcf50633_irq,
+                                       IRQF_TRIGGER_LOW | IRQF_ONESHOT,
+                                       "pcf50633", pcf);
+
+       if (ret)
+               dev_err(pcf->dev, "Failed to request IRQ %d\n", ret);
+
+       if (enable_irq_wake(irq) < 0)
+               dev_err(pcf->dev, "IRQ %u cannot be enabled as wake-up source"
+                       "in this hardware revision", irq);
+
+       return ret;
+}
+
+void pcf50633_irq_free(struct pcf50633 *pcf)
+{
+       free_irq(pcf->irq, pcf);
+}
diff --git a/drivers/mfd/rdc321x-southbridge.c b/drivers/mfd/rdc321x-southbridge.c
new file mode 100644 (file)
index 0000000..5092297
--- /dev/null
@@ -0,0 +1,123 @@
+/*
+ * RDC321x MFD southbrige driver
+ *
+ * Copyright (C) 2007-2010 Florian Fainelli <florian@openwrt.org>
+ * Copyright (C) 2010 Bernhard Loos <bernhardloos@googlemail.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.
+ *
+ * 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.
+ *
+ */
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/pci.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/rdc321x.h>
+
+static struct rdc321x_wdt_pdata rdc321x_wdt_pdata;
+
+static struct resource rdc321x_wdt_resource[] = {
+       {
+               .name   = "wdt-reg",
+               .start  = RDC321X_WDT_CTRL,
+               .end    = RDC321X_WDT_CTRL + 0x3,
+               .flags  = IORESOURCE_IO,
+       }
+};
+
+static struct rdc321x_gpio_pdata rdc321x_gpio_pdata = {
+       .max_gpios      = RDC321X_MAX_GPIO,
+};
+
+static struct resource rdc321x_gpio_resources[] = {
+       {
+               .name   = "gpio-reg1",
+               .start  = RDC321X_GPIO_CTRL_REG1,
+               .end    = RDC321X_GPIO_CTRL_REG1 + 0x7,
+               .flags  = IORESOURCE_IO,
+       }, {
+               .name   = "gpio-reg2",
+               .start  = RDC321X_GPIO_CTRL_REG2,
+               .end    = RDC321X_GPIO_CTRL_REG2 + 0x7,
+               .flags  = IORESOURCE_IO,
+       }
+};
+
+static struct mfd_cell rdc321x_sb_cells[] = {
+       {
+               .name           = "rdc321x-wdt",
+               .resources      = rdc321x_wdt_resource,
+               .num_resources  = ARRAY_SIZE(rdc321x_wdt_resource),
+               .driver_data    = &rdc321x_wdt_pdata,
+       }, {
+               .name           = "rdc321x-gpio",
+               .resources      = rdc321x_gpio_resources,
+               .num_resources  = ARRAY_SIZE(rdc321x_gpio_resources),
+               .driver_data    = &rdc321x_gpio_pdata,
+       },
+};
+
+static int __devinit rdc321x_sb_probe(struct pci_dev *pdev,
+                                       const struct pci_device_id *ent)
+{
+       int err;
+
+       err = pci_enable_device(pdev);
+       if (err) {
+               dev_err(&pdev->dev, "failed to enable device\n");
+               return err;
+       }
+
+       rdc321x_gpio_pdata.sb_pdev = pdev;
+       rdc321x_wdt_pdata.sb_pdev = pdev;
+
+       return mfd_add_devices(&pdev->dev, -1,
+               rdc321x_sb_cells, ARRAY_SIZE(rdc321x_sb_cells), NULL, 0);
+}
+
+static void __devexit rdc321x_sb_remove(struct pci_dev *pdev)
+{
+       mfd_remove_devices(&pdev->dev);
+}
+
+static DEFINE_PCI_DEVICE_TABLE(rdc321x_sb_table) = {
+       { PCI_DEVICE(PCI_VENDOR_ID_RDC, PCI_DEVICE_ID_RDC_R6030) },
+       {}
+};
+
+static struct pci_driver rdc321x_sb_driver = {
+       .name           = "RDC321x Southbridge",
+       .id_table       = rdc321x_sb_table,
+       .probe          = rdc321x_sb_probe,
+       .remove         = __devexit_p(rdc321x_sb_remove),
+};
+
+static int __init rdc321x_sb_init(void)
+{
+       return pci_register_driver(&rdc321x_sb_driver);
+}
+
+static void __exit rdc321x_sb_exit(void)
+{
+       pci_unregister_driver(&rdc321x_sb_driver);
+}
+
+module_init(rdc321x_sb_init);
+module_exit(rdc321x_sb_exit);
+
+MODULE_AUTHOR("Florian Fainelli <florian@openwrt.org>");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("RDC R-321x MFD southbridge driver");
index da6383a..5041d33 100644 (file)
@@ -318,6 +318,9 @@ static int t7l66xb_probe(struct platform_device *dev)
        struct resource *iomem, *rscr;
        int ret;
 
+       if (pdata == NULL)
+               return -EINVAL;
+
        iomem = platform_get_resource(dev, IORESOURCE_MEM, 0);
        if (!iomem)
                return -EINVAL;
diff --git a/drivers/mfd/tc35892.c b/drivers/mfd/tc35892.c
new file mode 100644 (file)
index 0000000..715f095
--- /dev/null
@@ -0,0 +1,347 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ *
+ * License Terms: GNU General Public License, version 2
+ * Author: Hanumath Prasad <hanumath.prasad@stericsson.com> for ST-Ericsson
+ * Author: Rabin Vincent <rabin.vincent@stericsson.com> for ST-Ericsson
+ */
+
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/tc35892.h>
+
+/**
+ * tc35892_reg_read() - read a single TC35892 register
+ * @tc35892:   Device to read from
+ * @reg:       Register to read
+ */
+int tc35892_reg_read(struct tc35892 *tc35892, u8 reg)
+{
+       int ret;
+
+       ret = i2c_smbus_read_byte_data(tc35892->i2c, reg);
+       if (ret < 0)
+               dev_err(tc35892->dev, "failed to read reg %#x: %d\n",
+                       reg, ret);
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(tc35892_reg_read);
+
+/**
+ * tc35892_reg_read() - write a single TC35892 register
+ * @tc35892:   Device to write to
+ * @reg:       Register to read
+ * @data:      Value to write
+ */
+int tc35892_reg_write(struct tc35892 *tc35892, u8 reg, u8 data)
+{
+       int ret;
+
+       ret = i2c_smbus_write_byte_data(tc35892->i2c, reg, data);
+       if (ret < 0)
+               dev_err(tc35892->dev, "failed to write reg %#x: %d\n",
+                       reg, ret);
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(tc35892_reg_write);
+
+/**
+ * tc35892_block_read() - read multiple TC35892 registers
+ * @tc35892:   Device to read from
+ * @reg:       First register
+ * @length:    Number of registers
+ * @values:    Buffer to write to
+ */
+int tc35892_block_read(struct tc35892 *tc35892, u8 reg, u8 length, u8 *values)
+{
+       int ret;
+
+       ret = i2c_smbus_read_i2c_block_data(tc35892->i2c, reg, length, values);
+       if (ret < 0)
+               dev_err(tc35892->dev, "failed to read regs %#x: %d\n",
+                       reg, ret);
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(tc35892_block_read);
+
+/**
+ * tc35892_block_write() - write multiple TC35892 registers
+ * @tc35892:   Device to write to
+ * @reg:       First register
+ * @length:    Number of registers
+ * @values:    Values to write
+ */
+int tc35892_block_write(struct tc35892 *tc35892, u8 reg, u8 length,
+                       const u8 *values)
+{
+       int ret;
+
+       ret = i2c_smbus_write_i2c_block_data(tc35892->i2c, reg, length,
+                                            values);
+       if (ret < 0)
+               dev_err(tc35892->dev, "failed to write regs %#x: %d\n",
+                       reg, ret);
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(tc35892_block_write);
+
+/**
+ * tc35892_set_bits() - set the value of a bitfield in a TC35892 register
+ * @tc35892:   Device to write to
+ * @reg:       Register to write
+ * @mask:      Mask of bits to set
+ * @values:    Value to set
+ */
+int tc35892_set_bits(struct tc35892 *tc35892, u8 reg, u8 mask, u8 val)
+{
+       int ret;
+
+       mutex_lock(&tc35892->lock);
+
+       ret = tc35892_reg_read(tc35892, reg);
+       if (ret < 0)
+               goto out;
+
+       ret &= ~mask;
+       ret |= val;
+
+       ret = tc35892_reg_write(tc35892, reg, ret);
+
+out:
+       mutex_unlock(&tc35892->lock);
+       return ret;
+}
+EXPORT_SYMBOL_GPL(tc35892_set_bits);
+
+static struct resource gpio_resources[] = {
+       {
+               .start  = TC35892_INT_GPIIRQ,
+               .end    = TC35892_INT_GPIIRQ,
+               .flags  = IORESOURCE_IRQ,
+       },
+};
+
+static struct mfd_cell tc35892_devs[] = {
+       {
+               .name           = "tc35892-gpio",
+               .num_resources  = ARRAY_SIZE(gpio_resources),
+               .resources      = &gpio_resources[0],
+       },
+};
+
+static irqreturn_t tc35892_irq(int irq, void *data)
+{
+       struct tc35892 *tc35892 = data;
+       int status;
+
+       status = tc35892_reg_read(tc35892, TC35892_IRQST);
+       if (status < 0)
+               return IRQ_NONE;
+
+       while (status) {
+               int bit = __ffs(status);
+
+               handle_nested_irq(tc35892->irq_base + bit);
+               status &= ~(1 << bit);
+       }
+
+       /*
+        * A dummy read or write (to any register) appears to be necessary to
+        * have the last interrupt clear (for example, GPIO IC write) take
+        * effect.
+        */
+       tc35892_reg_read(tc35892, TC35892_IRQST);
+
+       return IRQ_HANDLED;
+}
+
+static void tc35892_irq_dummy(unsigned int irq)
+{
+       /* No mask/unmask at this level */
+}
+
+static struct irq_chip tc35892_irq_chip = {
+       .name   = "tc35892",
+       .mask   = tc35892_irq_dummy,
+       .unmask = tc35892_irq_dummy,
+};
+
+static int tc35892_irq_init(struct tc35892 *tc35892)
+{
+       int base = tc35892->irq_base;
+       int irq;
+
+       for (irq = base; irq < base + TC35892_NR_INTERNAL_IRQS; irq++) {
+               set_irq_chip_data(irq, tc35892);
+               set_irq_chip_and_handler(irq, &tc35892_irq_chip,
+                                        handle_edge_irq);
+               set_irq_nested_thread(irq, 1);
+#ifdef CONFIG_ARM
+               set_irq_flags(irq, IRQF_VALID);
+#else
+               set_irq_noprobe(irq);
+#endif
+       }
+
+       return 0;
+}
+
+static void tc35892_irq_remove(struct tc35892 *tc35892)
+{
+       int base = tc35892->irq_base;
+       int irq;
+
+       for (irq = base; irq < base + TC35892_NR_INTERNAL_IRQS; irq++) {
+#ifdef CONFIG_ARM
+               set_irq_flags(irq, 0);
+#endif
+               set_irq_chip_and_handler(irq, NULL, NULL);
+               set_irq_chip_data(irq, NULL);
+       }
+}
+
+static int tc35892_chip_init(struct tc35892 *tc35892)
+{
+       int manf, ver, ret;
+
+       manf = tc35892_reg_read(tc35892, TC35892_MANFCODE);
+       if (manf < 0)
+               return manf;
+
+       ver = tc35892_reg_read(tc35892, TC35892_VERSION);
+       if (ver < 0)
+               return ver;
+
+       if (manf != TC35892_MANFCODE_MAGIC) {
+               dev_err(tc35892->dev, "unknown manufacturer: %#x\n", manf);
+               return -EINVAL;
+       }
+
+       dev_info(tc35892->dev, "manufacturer: %#x, version: %#x\n", manf, ver);
+
+       /* Put everything except the IRQ module into reset */
+       ret = tc35892_reg_write(tc35892, TC35892_RSTCTRL,
+                               TC35892_RSTCTRL_TIMRST
+                               | TC35892_RSTCTRL_ROTRST
+                               | TC35892_RSTCTRL_KBDRST
+                               | TC35892_RSTCTRL_GPIRST);
+       if (ret < 0)
+               return ret;
+
+       /* Clear the reset interrupt. */
+       return tc35892_reg_write(tc35892, TC35892_RSTINTCLR, 0x1);
+}
+
+static int __devinit tc35892_probe(struct i2c_client *i2c,
+                                  const struct i2c_device_id *id)
+{
+       struct tc35892_platform_data *pdata = i2c->dev.platform_data;
+       struct tc35892 *tc35892;
+       int ret;
+
+       if (!i2c_check_functionality(i2c->adapter, I2C_FUNC_SMBUS_BYTE_DATA
+                                    | I2C_FUNC_SMBUS_I2C_BLOCK))
+               return -EIO;
+
+       tc35892 = kzalloc(sizeof(struct tc35892), GFP_KERNEL);
+       if (!tc35892)
+               return -ENOMEM;
+
+       mutex_init(&tc35892->lock);
+
+       tc35892->dev = &i2c->dev;
+       tc35892->i2c = i2c;
+       tc35892->pdata = pdata;
+       tc35892->irq_base = pdata->irq_base;
+       tc35892->num_gpio = id->driver_data;
+
+       i2c_set_clientdata(i2c, tc35892);
+
+       ret = tc35892_chip_init(tc35892);
+       if (ret)
+               goto out_free;
+
+       ret = tc35892_irq_init(tc35892);
+       if (ret)
+               goto out_free;
+
+       ret = request_threaded_irq(tc35892->i2c->irq, NULL, tc35892_irq,
+                                  IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+                                  "tc35892", tc35892);
+       if (ret) {
+               dev_err(tc35892->dev, "failed to request IRQ: %d\n", ret);
+               goto out_removeirq;
+       }
+
+       ret = mfd_add_devices(tc35892->dev, -1, tc35892_devs,
+                             ARRAY_SIZE(tc35892_devs), NULL,
+                             tc35892->irq_base);
+       if (ret) {
+               dev_err(tc35892->dev, "failed to add children\n");
+               goto out_freeirq;
+       }
+
+       return 0;
+
+out_freeirq:
+       free_irq(tc35892->i2c->irq, tc35892);
+out_removeirq:
+       tc35892_irq_remove(tc35892);
+out_free:
+       i2c_set_clientdata(i2c, NULL);
+       kfree(tc35892);
+       return ret;
+}
+
+static int __devexit tc35892_remove(struct i2c_client *client)
+{
+       struct tc35892 *tc35892 = i2c_get_clientdata(client);
+
+       mfd_remove_devices(tc35892->dev);
+
+       free_irq(tc35892->i2c->irq, tc35892);
+       tc35892_irq_remove(tc35892);
+
+       i2c_set_clientdata(client, NULL);
+       kfree(tc35892);
+
+       return 0;
+}
+
+static const struct i2c_device_id tc35892_id[] = {
+       { "tc35892", 24 },
+       { }
+};
+MODULE_DEVICE_TABLE(i2c, tc35892_id);
+
+static struct i2c_driver tc35892_driver = {
+       .driver.name    = "tc35892",
+       .driver.owner   = THIS_MODULE,
+       .probe          = tc35892_probe,
+       .remove         = __devexit_p(tc35892_remove),
+       .id_table       = tc35892_id,
+};
+
+static int __init tc35892_init(void)
+{
+       return i2c_add_driver(&tc35892_driver);
+}
+subsys_initcall(tc35892_init);
+
+static void __exit tc35892_exit(void)
+{
+       i2c_del_driver(&tc35892_driver);
+}
+module_exit(tc35892_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("TC35892 MFD core driver");
+MODULE_AUTHOR("Hanumath Prasad, Rabin Vincent");
index 7f478ec..ac59950 100644 (file)
@@ -31,6 +31,7 @@
 
 #include <linux/i2c.h>
 #include <linux/i2c-ocores.h>
+#include <linux/i2c-xiic.h>
 #include <linux/i2c/tsc2007.h>
 
 #include <linux/spi/spi.h>
@@ -40,6 +41,8 @@
 
 #include <media/timb_radio.h>
 
+#include <linux/timb_dma.h>
+
 #include "timberdale.h"
 
 #define DRIVER_NAME "timberdale"
@@ -69,6 +72,12 @@ static struct i2c_board_info timberdale_i2c_board_info[] = {
        },
 };
 
+static __devinitdata struct xiic_i2c_platform_data
+timberdale_xiic_platform_data = {
+       .devices = timberdale_i2c_board_info,
+       .num_devices = ARRAY_SIZE(timberdale_i2c_board_info)
+};
+
 static __devinitdata struct ocores_i2c_platform_data
 timberdale_ocores_platform_data = {
        .regstep = 4,
@@ -77,7 +86,20 @@ timberdale_ocores_platform_data = {
        .num_devices = ARRAY_SIZE(timberdale_i2c_board_info)
 };
 
-const static __devinitconst struct resource timberdale_ocores_resources[] = {
+static const __devinitconst struct resource timberdale_xiic_resources[] = {
+       {
+               .start  = XIICOFFSET,
+               .end    = XIICEND,
+               .flags  = IORESOURCE_MEM,
+       },
+       {
+               .start  = IRQ_TIMBERDALE_I2C,
+               .end    = IRQ_TIMBERDALE_I2C,
+               .flags  = IORESOURCE_IRQ,
+       },
+};
+
+static const __devinitconst struct resource timberdale_ocores_resources[] = {
        {
                .start  = OCORESOFFSET,
                .end    = OCORESEND,
@@ -126,7 +148,7 @@ static __devinitdata struct xspi_platform_data timberdale_xspi_platform_data = {
         */
 };
 
-const static __devinitconst struct resource timberdale_spi_resources[] = {
+static const __devinitconst struct resource timberdale_spi_resources[] = {
        {
                .start  = SPIOFFSET,
                .end    = SPIEND,
@@ -139,7 +161,7 @@ const static __devinitconst struct resource timberdale_spi_resources[] = {
        },
 };
 
-const static __devinitconst struct resource timberdale_eth_resources[] = {
+static const __devinitconst struct resource timberdale_eth_resources[] = {
        {
                .start  = ETHOFFSET,
                .end    = ETHEND,
@@ -159,7 +181,7 @@ static __devinitdata struct timbgpio_platform_data
        .irq_base = 200,
 };
 
-const static __devinitconst struct resource timberdale_gpio_resources[] = {
+static const __devinitconst struct resource timberdale_gpio_resources[] = {
        {
                .start  = GPIOOFFSET,
                .end    = GPIOEND,
@@ -172,7 +194,7 @@ const static __devinitconst struct resource timberdale_gpio_resources[] = {
        },
 };
 
-const static __devinitconst struct resource timberdale_mlogicore_resources[] = {
+static const __devinitconst struct resource timberdale_mlogicore_resources[] = {
        {
                .start  = MLCOREOFFSET,
                .end    = MLCOREEND,
@@ -190,7 +212,7 @@ const static __devinitconst struct resource timberdale_mlogicore_resources[] = {
        },
 };
 
-const static __devinitconst struct resource timberdale_uart_resources[] = {
+static const __devinitconst struct resource timberdale_uart_resources[] = {
        {
                .start  = UARTOFFSET,
                .end    = UARTEND,
@@ -203,7 +225,7 @@ const static __devinitconst struct resource timberdale_uart_resources[] = {
        },
 };
 
-const static __devinitconst struct resource timberdale_uartlite_resources[] = {
+static const __devinitconst struct resource timberdale_uartlite_resources[] = {
        {
                .start  = UARTLITEOFFSET,
                .end    = UARTLITEEND,
@@ -216,7 +238,7 @@ const static __devinitconst struct resource timberdale_uartlite_resources[] = {
        },
 };
 
-const static __devinitconst struct resource timberdale_radio_resources[] = {
+static const __devinitconst struct resource timberdale_radio_resources[] = {
        {
                .start  = RDSOFFSET,
                .end    = RDSEND,
@@ -250,7 +272,66 @@ static __devinitdata struct timb_radio_platform_data
        }
 };
 
-const static __devinitconst struct resource timberdale_dma_resources[] = {
+static __devinitdata struct timb_dma_platform_data timb_dma_platform_data = {
+       .nr_channels = 10,
+       .channels = {
+               {
+                       /* UART RX */
+                       .rx = true,
+                       .descriptors = 2,
+                       .descriptor_elements = 1
+               },
+               {
+                       /* UART TX */
+                       .rx = false,
+                       .descriptors = 2,
+                       .descriptor_elements = 1
+               },
+               {
+                       /* MLB RX */
+                       .rx = true,
+                       .descriptors = 2,
+                       .descriptor_elements = 1
+               },
+               {
+                       /* MLB TX */
+                       .rx = false,
+                       .descriptors = 2,
+                       .descriptor_elements = 1
+               },
+               {
+                       /* Video RX */
+                       .rx = true,
+                       .bytes_per_line = 1440,
+                       .descriptors = 2,
+                       .descriptor_elements = 16
+               },
+               {
+                       /* Video framedrop */
+               },
+               {
+                       /* SDHCI RX */
+                       .rx = true,
+               },
+               {
+                       /* SDHCI TX */
+               },
+               {
+                       /* ETH RX */
+                       .rx = true,
+                       .descriptors = 2,
+                       .descriptor_elements = 1
+               },
+               {
+                       /* ETH TX */
+                       .rx = false,
+                       .descriptors = 2,
+                       .descriptor_elements = 1
+               },
+       }
+};
+
+static const __devinitconst struct resource timberdale_dma_resources[] = {
        {
                .start  = DMAOFFSET,
                .end    = DMAEND,
@@ -265,11 +346,25 @@ const static __devinitconst struct resource timberdale_dma_resources[] = {
 
 static __devinitdata struct mfd_cell timberdale_cells_bar0_cfg0[] = {
        {
+               .name = "timb-dma",
+               .num_resources = ARRAY_SIZE(timberdale_dma_resources),
+               .resources = timberdale_dma_resources,
+               .platform_data = &timb_dma_platform_data,
+               .data_size = sizeof(timb_dma_platform_data),
+       },
+       {
                .name = "timb-uart",
                .num_resources = ARRAY_SIZE(timberdale_uart_resources),
                .resources = timberdale_uart_resources,
        },
        {
+               .name = "xiic-i2c",
+               .num_resources = ARRAY_SIZE(timberdale_xiic_resources),
+               .resources = timberdale_xiic_resources,
+               .platform_data = &timberdale_xiic_platform_data,
+               .data_size = sizeof(timberdale_xiic_platform_data),
+       },
+       {
                .name = "timb-gpio",
                .num_resources = ARRAY_SIZE(timberdale_gpio_resources),
                .resources = timberdale_gpio_resources,
@@ -295,14 +390,16 @@ static __devinitdata struct mfd_cell timberdale_cells_bar0_cfg0[] = {
                .num_resources = ARRAY_SIZE(timberdale_eth_resources),
                .resources = timberdale_eth_resources,
        },
+};
+
+static __devinitdata struct mfd_cell timberdale_cells_bar0_cfg1[] = {
        {
                .name = "timb-dma",
                .num_resources = ARRAY_SIZE(timberdale_dma_resources),
                .resources = timberdale_dma_resources,
+               .platform_data = &timb_dma_platform_data,
+               .data_size = sizeof(timb_dma_platform_data),
        },
-};
-
-static __devinitdata struct mfd_cell timberdale_cells_bar0_cfg1[] = {
        {
                .name = "timb-uart",
                .num_resources = ARRAY_SIZE(timberdale_uart_resources),
@@ -314,6 +411,13 @@ static __devinitdata struct mfd_cell timberdale_cells_bar0_cfg1[] = {
                .resources = timberdale_uartlite_resources,
        },
        {
+               .name = "xiic-i2c",
+               .num_resources = ARRAY_SIZE(timberdale_xiic_resources),
+               .resources = timberdale_xiic_resources,
+               .platform_data = &timberdale_xiic_platform_data,
+               .data_size = sizeof(timberdale_xiic_platform_data),
+       },
+       {
                .name = "timb-gpio",
                .num_resources = ARRAY_SIZE(timberdale_gpio_resources),
                .resources = timberdale_gpio_resources,
@@ -344,20 +448,29 @@ static __devinitdata struct mfd_cell timberdale_cells_bar0_cfg1[] = {
                .num_resources = ARRAY_SIZE(timberdale_eth_resources),
                .resources = timberdale_eth_resources,
        },
+};
+
+static __devinitdata struct mfd_cell timberdale_cells_bar0_cfg2[] = {
        {
                .name = "timb-dma",
                .num_resources = ARRAY_SIZE(timberdale_dma_resources),
                .resources = timberdale_dma_resources,
+               .platform_data = &timb_dma_platform_data,
+               .data_size = sizeof(timb_dma_platform_data),
        },
-};
-
-static __devinitdata struct mfd_cell timberdale_cells_bar0_cfg2[] = {
        {
                .name = "timb-uart",
                .num_resources = ARRAY_SIZE(timberdale_uart_resources),
                .resources = timberdale_uart_resources,
        },
        {
+               .name = "xiic-i2c",
+               .num_resources = ARRAY_SIZE(timberdale_xiic_resources),
+               .resources = timberdale_xiic_resources,
+               .platform_data = &timberdale_xiic_platform_data,
+               .data_size = sizeof(timberdale_xiic_platform_data),
+       },
+       {
                .name = "timb-gpio",
                .num_resources = ARRAY_SIZE(timberdale_gpio_resources),
                .resources = timberdale_gpio_resources,
@@ -378,14 +491,16 @@ static __devinitdata struct mfd_cell timberdale_cells_bar0_cfg2[] = {
                .platform_data = &timberdale_xspi_platform_data,
                .data_size = sizeof(timberdale_xspi_platform_data),
        },
+};
+
+static __devinitdata struct mfd_cell timberdale_cells_bar0_cfg3[] = {
        {
                .name = "timb-dma",
                .num_resources = ARRAY_SIZE(timberdale_dma_resources),
                .resources = timberdale_dma_resources,
+               .platform_data = &timb_dma_platform_data,
+               .data_size = sizeof(timb_dma_platform_data),
        },
-};
-
-static __devinitdata struct mfd_cell timberdale_cells_bar0_cfg3[] = {
        {
                .name = "timb-uart",
                .num_resources = ARRAY_SIZE(timberdale_uart_resources),
@@ -424,11 +539,6 @@ static __devinitdata struct mfd_cell timberdale_cells_bar0_cfg3[] = {
                .num_resources = ARRAY_SIZE(timberdale_eth_resources),
                .resources = timberdale_eth_resources,
        },
-       {
-               .name = "timb-dma",
-               .num_resources = ARRAY_SIZE(timberdale_dma_resources),
-               .resources = timberdale_dma_resources,
-       },
 };
 
 static const __devinitconst struct resource timberdale_sdhc_resources[] = {
index 8d27ffa..c11bf6e 100644 (file)
@@ -23,7 +23,7 @@
 #ifndef MFD_TIMBERDALE_H
 #define MFD_TIMBERDALE_H
 
-#define DRV_VERSION            "0.1"
+#define DRV_VERSION            "0.2"
 
 /* This driver only support versions >= 3.8 and < 4.0  */
 #define TIMB_SUPPORTED_MAJOR   3
@@ -66,7 +66,7 @@
 
 #define CHIPCTLOFFSET  0x800
 #define CHIPCTLEND     0x8ff
-#define CHIPCTLSIZE    (CHIPCTLEND - CHIPCTLOFFSET)
+#define CHIPCTLSIZE    (CHIPCTLEND - CHIPCTLOFFSET + 1)
 
 #define INTCOFFSET     0xc00
 #define INTCEND                0xfff
 #define GPIO_PIN_BT_RST                15
 #define GPIO_NR_PINS           16
 
+/* DMA Channels */
+#define DMA_UART_RX         0
+#define DMA_UART_TX         1
+#define DMA_MLB_RX          2
+#define DMA_MLB_TX          3
+#define DMA_VIDEO_RX        4
+#define DMA_VIDEO_DROP      5
+#define DMA_SDHCI_RX        6
+#define DMA_SDHCI_TX        7
+#define DMA_ETH_RX          8
+#define DMA_ETH_TX          9
+
 #endif
index e595530..9b22a77 100644 (file)
@@ -530,8 +530,8 @@ static int __exit tps65010_remove(struct i2c_client *client)
        cancel_delayed_work(&tps->work);
        flush_scheduled_work();
        debugfs_remove(tps->file);
-       kfree(tps);
        i2c_set_clientdata(client, NULL);
+       kfree(tps);
        the_tps = NULL;
        return 0;
 }
diff --git a/drivers/mfd/tps6507x.c b/drivers/mfd/tps6507x.c
new file mode 100644 (file)
index 0000000..d859dff
--- /dev/null
@@ -0,0 +1,159 @@
+/*
+ * tps6507x.c  --  TPS6507x chip family multi-function driver
+ *
+ *  Copyright (c) 2010 RidgeRun (todd.fischer@ridgerun.com)
+ *
+ * Author: Todd Fischer
+ *         todd.fischer@ridgerun.com
+ *
+ * Credits:
+ *
+ *    Using code from wm831x-*.c, wm8400-core, Wolfson Microelectronics PLC.
+ *
+ * For licencing details see kernel-base/COPYING
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/tps6507x.h>
+
+static struct mfd_cell tps6507x_devs[] = {
+       {
+               .name = "tps6507x-pmic",
+       },
+       {
+               .name = "tps6507x-ts",
+       },
+};
+
+
+static int tps6507x_i2c_read_device(struct tps6507x_dev *tps6507x, char reg,
+                                 int bytes, void *dest)
+{
+       struct i2c_client *i2c = tps6507x->i2c_client;
+       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 = bytes;
+       xfer[1].buf = dest;
+
+       ret = i2c_transfer(i2c->adapter, xfer, 2);
+       if (ret == 2)
+               ret = 0;
+       else if (ret >= 0)
+               ret = -EIO;
+
+       return ret;
+}
+
+static int tps6507x_i2c_write_device(struct tps6507x_dev *tps6507x, char reg,
+                                  int bytes, void *src)
+{
+       struct i2c_client *i2c = tps6507x->i2c_client;
+       /* we add 1 byte for device register */
+       u8 msg[TPS6507X_MAX_REGISTER + 1];
+       int ret;
+
+       if (bytes > (TPS6507X_MAX_REGISTER + 1))
+               return -EINVAL;
+
+       msg[0] = reg;
+       memcpy(&msg[1], src, bytes);
+
+       ret = i2c_master_send(i2c, msg, bytes + 1);
+       if (ret < 0)
+               return ret;
+       if (ret != bytes + 1)
+               return -EIO;
+       return 0;
+}
+
+static int tps6507x_i2c_probe(struct i2c_client *i2c,
+                           const struct i2c_device_id *id)
+{
+       struct tps6507x_dev *tps6507x;
+       int ret = 0;
+
+       tps6507x = kzalloc(sizeof(struct tps6507x_dev), GFP_KERNEL);
+       if (tps6507x == NULL) {
+               kfree(i2c);
+               return -ENOMEM;
+       }
+
+       i2c_set_clientdata(i2c, tps6507x);
+       tps6507x->dev = &i2c->dev;
+       tps6507x->i2c_client = i2c;
+       tps6507x->read_dev = tps6507x_i2c_read_device;
+       tps6507x->write_dev = tps6507x_i2c_write_device;
+
+       ret = mfd_add_devices(tps6507x->dev, -1,
+                             tps6507x_devs, ARRAY_SIZE(tps6507x_devs),
+                             NULL, 0);
+
+       if (ret < 0)
+               goto err;
+
+       return ret;
+
+err:
+       mfd_remove_devices(tps6507x->dev);
+       kfree(tps6507x);
+       return ret;
+}
+
+static int tps6507x_i2c_remove(struct i2c_client *i2c)
+{
+       struct tps6507x_dev *tps6507x = i2c_get_clientdata(i2c);
+
+       mfd_remove_devices(tps6507x->dev);
+       kfree(tps6507x);
+
+       return 0;
+}
+
+static const struct i2c_device_id tps6507x_i2c_id[] = {
+       { "tps6507x", 0 },
+       { }
+};
+MODULE_DEVICE_TABLE(i2c, tps6507x_i2c_id);
+
+
+static struct i2c_driver tps6507x_i2c_driver = {
+       .driver = {
+                  .name = "tps6507x",
+                  .owner = THIS_MODULE,
+       },
+       .probe = tps6507x_i2c_probe,
+       .remove = tps6507x_i2c_remove,
+       .id_table = tps6507x_i2c_id,
+};
+
+static int __init tps6507x_i2c_init(void)
+{
+       return i2c_add_driver(&tps6507x_i2c_driver);
+}
+/* init early so consumer devices can complete system boot */
+subsys_initcall(tps6507x_i2c_init);
+
+static void __exit tps6507x_i2c_exit(void)
+{
+       i2c_del_driver(&tps6507x_i2c_driver);
+}
+module_exit(tps6507x_i2c_exit);
+
+MODULE_DESCRIPTION("TPS6507x chip family multi-function driver");
+MODULE_LICENSE("GPL");
index 202bdd5..097f24d 100644 (file)
@@ -232,10 +232,11 @@ static const struct sih sih_modules_twl5031[8] = {
        },
        [6] = {
                /*
-                * ACI doesn't use the same SIH organization.
-                * For example, it supports only one interrupt line
+                * ECI/DBI doesn't use the same SIH organization.
+                * For example, it supports only one interrupt output line.
+                * That is, the interrupts are seen on both INT1 and INT2 lines.
                 */
-               .name           = "aci",
+               .name           = "eci_dbi",
                .module         = TWL5031_MODULE_ACCESSORY,
                .bits           = 9,
                .bytes_ixr      = 2,
@@ -247,8 +248,8 @@ static const struct sih sih_modules_twl5031[8] = {
 
        },
        [7] = {
-               /* Accessory */
-               .name           = "acc",
+               /* Audio accessory */
+               .name           = "audio",
                .module         = TWL5031_MODULE_ACCESSORY,
                .control_offset = TWL5031_ACCSIHCTRL,
                .bits           = 2,
index f2ab025..1a968f3 100644 (file)
@@ -322,7 +322,11 @@ EXPORT_SYMBOL_GPL(wm831x_set_bits);
  */
 int wm831x_auxadc_read(struct wm831x *wm831x, enum wm831x_auxadc input)
 {
-       int ret, src;
+       int ret, src, irq_masked, timeout;
+
+       /* Are we using the interrupt? */
+       irq_masked = wm831x_reg_read(wm831x, WM831X_INTERRUPT_STATUS_1_MASK);
+       irq_masked &= WM831X_AUXADC_DATA_EINT;
 
        mutex_lock(&wm831x->auxadc_lock);
 
@@ -342,6 +346,9 @@ int wm831x_auxadc_read(struct wm831x *wm831x, enum wm831x_auxadc input)
                goto out;
        }
 
+       /* Clear any notification from a very late arriving interrupt */
+       try_wait_for_completion(&wm831x->auxadc_done);
+
        ret = wm831x_set_bits(wm831x, WM831X_AUXADC_CONTROL,
                              WM831X_AUX_CVT_ENA, WM831X_AUX_CVT_ENA);
        if (ret < 0) {
@@ -349,22 +356,46 @@ int wm831x_auxadc_read(struct wm831x *wm831x, enum wm831x_auxadc input)
                goto disable;
        }
 
-       /* If an interrupt arrived late clean up after it */
-       try_wait_for_completion(&wm831x->auxadc_done);
-
-       /* Ignore the result to allow us to soldier on without IRQ hookup */
-       wait_for_completion_timeout(&wm831x->auxadc_done, msecs_to_jiffies(5));
-
-       ret = wm831x_reg_read(wm831x, WM831X_AUXADC_CONTROL);
-       if (ret < 0) {
-               dev_err(wm831x->dev, "AUXADC status read failed: %d\n", ret);
-               goto disable;
-       }
-
-       if (ret & WM831X_AUX_CVT_ENA) {
-               dev_err(wm831x->dev, "Timed out reading AUXADC\n");
-               ret = -EBUSY;
-               goto disable;
+       if (irq_masked) {
+               /* If we're not using interrupts then poll the
+                * interrupt status register */
+               timeout = 5;
+               while (timeout) {
+                       msleep(1);
+
+                       ret = wm831x_reg_read(wm831x,
+                                             WM831X_INTERRUPT_STATUS_1);
+                       if (ret < 0) {
+                               dev_err(wm831x->dev,
+                                       "ISR 1 read failed: %d\n", ret);
+                               goto disable;
+                       }
+
+                       /* Did it complete? */
+                       if (ret & WM831X_AUXADC_DATA_EINT) {
+                               wm831x_reg_write(wm831x,
+                                                WM831X_INTERRUPT_STATUS_1,
+                                                WM831X_AUXADC_DATA_EINT);
+                               break;
+                       } else {
+                               dev_err(wm831x->dev,
+                                       "AUXADC conversion timeout\n");
+                               ret = -EBUSY;
+                               goto disable;
+                       }
+               }
+       } else {
+               /* If we are using interrupts then wait for the
+                * interrupt to complete.  Use an extremely long
+                * timeout to handle situations with heavy load where
+                * the notification of the interrupt may be delayed by
+                * threaded IRQ handling. */
+               if (!wait_for_completion_timeout(&wm831x->auxadc_done,
+                                                msecs_to_jiffies(500))) {
+                       dev_err(wm831x->dev, "Timed out waiting for AUXADC\n");
+                       ret = -EBUSY;
+                       goto disable;
+               }
        }
 
        ret = wm831x_reg_read(wm831x, WM831X_AUXADC_DATA);
@@ -1463,6 +1494,7 @@ static int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq)
        case WM8310:
                parent = WM8310;
                wm831x->num_gpio = 16;
+               wm831x->charger_irq_wake = 1;
                if (rev > 0) {
                        wm831x->has_gpio_ena = 1;
                        wm831x->has_cs_sts = 1;
@@ -1474,6 +1506,7 @@ static int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq)
        case WM8311:
                parent = WM8311;
                wm831x->num_gpio = 16;
+               wm831x->charger_irq_wake = 1;
                if (rev > 0) {
                        wm831x->has_gpio_ena = 1;
                        wm831x->has_cs_sts = 1;
@@ -1485,6 +1518,7 @@ static int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq)
        case WM8312:
                parent = WM8312;
                wm831x->num_gpio = 16;
+               wm831x->charger_irq_wake = 1;
                if (rev > 0) {
                        wm831x->has_gpio_ena = 1;
                        wm831x->has_cs_sts = 1;
@@ -1623,6 +1657,42 @@ static void wm831x_device_exit(struct wm831x *wm831x)
        kfree(wm831x);
 }
 
+static int wm831x_device_suspend(struct wm831x *wm831x)
+{
+       int reg, mask;
+
+       /* If the charger IRQs are a wake source then make sure we ack
+        * them even if they're not actively being used (eg, no power
+        * driver or no IRQ line wired up) then acknowledge the
+        * interrupts otherwise suspend won't last very long.
+        */
+       if (wm831x->charger_irq_wake) {
+               reg = wm831x_reg_read(wm831x, WM831X_INTERRUPT_STATUS_2_MASK);
+
+               mask = WM831X_CHG_BATT_HOT_EINT |
+                       WM831X_CHG_BATT_COLD_EINT |
+                       WM831X_CHG_BATT_FAIL_EINT |
+                       WM831X_CHG_OV_EINT | WM831X_CHG_END_EINT |
+                       WM831X_CHG_TO_EINT | WM831X_CHG_MODE_EINT |
+                       WM831X_CHG_START_EINT;
+
+               /* If any of the interrupts are masked read the statuses */
+               if (reg & mask)
+                       reg = wm831x_reg_read(wm831x,
+                                             WM831X_INTERRUPT_STATUS_2);
+
+               if (reg & mask) {
+                       dev_info(wm831x->dev,
+                                "Acknowledging masked charger IRQs: %x\n",
+                                reg & mask);
+                       wm831x_reg_write(wm831x, WM831X_INTERRUPT_STATUS_2,
+                                        reg & mask);
+               }
+       }
+
+       return 0;
+}
+
 static int wm831x_i2c_read_device(struct wm831x *wm831x, unsigned short reg,
                                  int bytes, void *dest)
 {
@@ -1697,6 +1767,13 @@ static int wm831x_i2c_remove(struct i2c_client *i2c)
        return 0;
 }
 
+static int wm831x_i2c_suspend(struct i2c_client *i2c, pm_message_t mesg)
+{
+       struct wm831x *wm831x = i2c_get_clientdata(i2c);
+
+       return wm831x_device_suspend(wm831x);
+}
+
 static const struct i2c_device_id wm831x_i2c_id[] = {
        { "wm8310", WM8310 },
        { "wm8311", WM8311 },
@@ -1714,6 +1791,7 @@ static struct i2c_driver wm831x_i2c_driver = {
        },
        .probe = wm831x_i2c_probe,
        .remove = wm831x_i2c_remove,
+       .suspend = wm831x_i2c_suspend,
        .id_table = wm831x_i2c_id,
 };
 
index 4c1122c..7dabe4d 100644 (file)
@@ -39,8 +39,6 @@ struct wm831x_irq_data {
        int primary;
        int reg;
        int mask;
-       irq_handler_t handler;
-       void *handler_data;
 };
 
 static struct wm831x_irq_data wm831x_irqs[] = {
@@ -492,6 +490,14 @@ int wm831x_irq_init(struct wm831x *wm831x, int irq)
 
        mutex_init(&wm831x->irq_lock);
 
+       /* Mask the individual interrupt sources */
+       for (i = 0; i < ARRAY_SIZE(wm831x->irq_masks_cur); i++) {
+               wm831x->irq_masks_cur[i] = 0xffff;
+               wm831x->irq_masks_cache[i] = 0xffff;
+               wm831x_reg_write(wm831x, WM831X_INTERRUPT_STATUS_1_MASK + i,
+                                0xffff);
+       }
+
        if (!irq) {
                dev_warn(wm831x->dev,
                         "No interrupt specified - functionality limited\n");
@@ -507,14 +513,6 @@ int wm831x_irq_init(struct wm831x *wm831x, int irq)
        wm831x->irq = irq;
        wm831x->irq_base = pdata->irq_base;
 
-       /* Mask the individual interrupt sources */
-       for (i = 0; i < ARRAY_SIZE(wm831x->irq_masks_cur); i++) {
-               wm831x->irq_masks_cur[i] = 0xffff;
-               wm831x->irq_masks_cache[i] = 0xffff;
-               wm831x_reg_write(wm831x, WM831X_INTERRUPT_STATUS_1_MASK + i,
-                                0xffff);
-       }
-
        /* Register them with genirq */
        for (cur_irq = wm831x->irq_base;
             cur_irq < ARRAY_SIZE(wm831x_irqs) + wm831x->irq_base;
index 65830f5..7795af4 100644 (file)
@@ -64,10 +64,8 @@ static int wm8350_i2c_probe(struct i2c_client *i2c,
        int ret = 0;
 
        wm8350 = kzalloc(sizeof(struct wm8350), GFP_KERNEL);
-       if (wm8350 == NULL) {
-               kfree(i2c);
+       if (wm8350 == NULL)
                return -ENOMEM;
-       }
 
        i2c_set_clientdata(i2c, wm8350);
        wm8350->dev = &i2c->dev;
@@ -82,6 +80,7 @@ static int wm8350_i2c_probe(struct i2c_client *i2c,
        return ret;
 
 err:
+       i2c_set_clientdata(i2c, NULL);
        kfree(wm8350);
        return ret;
 }
@@ -91,6 +90,7 @@ static int wm8350_i2c_remove(struct i2c_client *i2c)
        struct wm8350 *wm8350 = i2c_get_clientdata(i2c);
 
        wm8350_device_exit(wm8350);
+       i2c_set_clientdata(i2c, NULL);
        kfree(wm8350);
 
        return 0;
index 865ce01..e08aafa 100644 (file)
@@ -118,7 +118,7 @@ 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));
+       BUG_ON(reg + num_regs > 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++)
@@ -144,7 +144,7 @@ static int wm8400_write(struct wm8400 *wm8400, u8 reg, int num_regs,
 {
        int ret, i;
 
-       BUG_ON(reg + num_regs - 1 > ARRAY_SIZE(wm8400->reg_cache));
+       BUG_ON(reg + num_regs > ARRAY_SIZE(wm8400->reg_cache));
 
        for (i = 0; i < num_regs; i++) {
                BUG_ON(!reg_data[reg + i].writable);
index 72ebb3f..4dfa6b9 100644 (file)
@@ -189,8 +189,7 @@ static loff_t vol_cdev_llseek(struct file *file, loff_t offset, int origin)
        return new_offset;
 }
 
-static int vol_cdev_fsync(struct file *file, struct dentry *dentry,
-                         int datasync)
+static int vol_cdev_fsync(struct file *file, int datasync)
 {
        struct ubi_volume_desc *desc = file->private_data;
        struct ubi_device *ubi = desc->vol->ubi;
index 82eaf65..ea9b7a0 100644 (file)
@@ -551,8 +551,7 @@ static irqreturn_t el16_interrupt(int irq, void *dev_id)
        void __iomem *shmem;
 
        if (dev == NULL) {
-               pr_err("%s: net_interrupt(): irq %d for unknown device.\n",
-                       dev->name, irq);
+               pr_err("net_interrupt(): irq %d for unknown device.\n", irq);
                return IRQ_NONE;
        }
 
index c911bfb..9d11dbf 100644 (file)
@@ -294,7 +294,7 @@ int be_cmd_POST(struct be_adapter *adapter)
                } else {
                        return 0;
                }
-       } while (timeout < 20);
+       } while (timeout < 40);
 
        dev_err(&adapter->pdev->dev, "POST timeout; stage=0x%x\n", stage);
        return -1;
index aa065c7..54b1427 100644 (file)
@@ -1861,7 +1861,7 @@ static int be_setup(struct be_adapter *adapter)
                                goto if_destroy;
                        }
                        vf++;
-               } while (vf < num_vfs);
+               }
        } else if (!be_physfn(adapter)) {
                status = be_cmd_mac_addr_query(adapter, mac,
                        MAC_ADDRESS_TYPE_NETWORK, false, adapter->if_handle);
index 05b7517..2c5227c 100644 (file)
@@ -63,6 +63,16 @@ config CAN_BFIN
          To compile this driver as a module, choose M here: the
          module will be called bfin_can.
 
+config CAN_JANZ_ICAN3
+       tristate "Janz VMOD-ICAN3 Intelligent CAN controller"
+       depends on CAN_DEV && MFD_JANZ_CMODIO
+       ---help---
+         Driver for Janz VMOD-ICAN3 Intelligent CAN controller module, which
+         connects to a MODULbus carrier board.
+
+         This driver can also be built as a module. If so, the module will be
+         called janz-ican3.ko.
+
 source "drivers/net/can/mscan/Kconfig"
 
 source "drivers/net/can/sja1000/Kconfig"
index 7a702f2..9047cd0 100644 (file)
@@ -15,5 +15,6 @@ obj-$(CONFIG_CAN_AT91)                += at91_can.o
 obj-$(CONFIG_CAN_TI_HECC)      += ti_hecc.o
 obj-$(CONFIG_CAN_MCP251X)      += mcp251x.o
 obj-$(CONFIG_CAN_BFIN)         += bfin_can.o
+obj-$(CONFIG_CAN_JANZ_ICAN3)   += janz-ican3.o
 
 ccflags-$(CONFIG_CAN_DEBUG_DEVICES) := -DDEBUG
diff --git a/drivers/net/can/janz-ican3.c b/drivers/net/can/janz-ican3.c
new file mode 100644 (file)
index 0000000..6e533dc
--- /dev/null
@@ -0,0 +1,1830 @@
+/*
+ * Janz MODULbus VMOD-ICAN3 CAN Interface Driver
+ *
+ * Copyright (c) 2010 Ira W. Snyder <iws@ovro.caltech.edu>
+ *
+ * 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/interrupt.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+
+#include <linux/netdevice.h>
+#include <linux/can.h>
+#include <linux/can/dev.h>
+#include <linux/can/error.h>
+
+#include <linux/mfd/janz.h>
+
+/* the DPM has 64k of memory, organized into 256x 256 byte pages */
+#define DPM_NUM_PAGES          256
+#define DPM_PAGE_SIZE          256
+#define DPM_PAGE_ADDR(p)       ((p) * DPM_PAGE_SIZE)
+
+/* JANZ ICAN3 "old-style" host interface queue page numbers */
+#define QUEUE_OLD_CONTROL      0
+#define QUEUE_OLD_RB0          1
+#define QUEUE_OLD_RB1          2
+#define QUEUE_OLD_WB0          3
+#define QUEUE_OLD_WB1          4
+
+/* Janz ICAN3 "old-style" host interface control registers */
+#define MSYNC_PEER             0x00            /* ICAN only */
+#define MSYNC_LOCL             0x01            /* host only */
+#define TARGET_RUNNING         0x02
+
+#define MSYNC_RB0              0x01
+#define MSYNC_RB1              0x02
+#define MSYNC_RBLW             0x04
+#define MSYNC_RB_MASK          (MSYNC_RB0 | MSYNC_RB1)
+
+#define MSYNC_WB0              0x10
+#define MSYNC_WB1              0x20
+#define MSYNC_WBLW             0x40
+#define MSYNC_WB_MASK          (MSYNC_WB0 | MSYNC_WB1)
+
+/* Janz ICAN3 "new-style" host interface queue page numbers */
+#define QUEUE_TOHOST           5
+#define QUEUE_FROMHOST_MID     6
+#define QUEUE_FROMHOST_HIGH    7
+#define QUEUE_FROMHOST_LOW     8
+
+/* The first free page in the DPM is #9 */
+#define DPM_FREE_START         9
+
+/* Janz ICAN3 "new-style" and "fast" host interface descriptor flags */
+#define DESC_VALID             0x80
+#define DESC_WRAP              0x40
+#define DESC_INTERRUPT         0x20
+#define DESC_IVALID            0x10
+#define DESC_LEN(len)          (len)
+
+/* Janz ICAN3 Firmware Messages */
+#define MSG_CONNECTI           0x02
+#define MSG_DISCONNECT         0x03
+#define MSG_IDVERS             0x04
+#define MSG_MSGLOST            0x05
+#define MSG_NEWHOSTIF          0x08
+#define MSG_INQUIRY            0x0a
+#define MSG_SETAFILMASK                0x10
+#define MSG_INITFDPMQUEUE      0x11
+#define MSG_HWCONF             0x12
+#define MSG_FMSGLOST           0x15
+#define MSG_CEVTIND            0x37
+#define MSG_CBTRREQ            0x41
+#define MSG_COFFREQ            0x42
+#define MSG_CONREQ             0x43
+#define MSG_CCONFREQ           0x47
+
+/*
+ * Janz ICAN3 CAN Inquiry Message Types
+ *
+ * NOTE: there appears to be a firmware bug here. You must send
+ * NOTE: INQUIRY_STATUS and expect to receive an INQUIRY_EXTENDED
+ * NOTE: response. The controller never responds to a message with
+ * NOTE: the INQUIRY_EXTENDED subspec :(
+ */
+#define INQUIRY_STATUS         0x00
+#define INQUIRY_TERMINATION    0x01
+#define INQUIRY_EXTENDED       0x04
+
+/* Janz ICAN3 CAN Set Acceptance Filter Mask Message Types */
+#define SETAFILMASK_REJECT     0x00
+#define SETAFILMASK_FASTIF     0x02
+
+/* Janz ICAN3 CAN Hardware Configuration Message Types */
+#define HWCONF_TERMINATE_ON    0x01
+#define HWCONF_TERMINATE_OFF   0x00
+
+/* Janz ICAN3 CAN Event Indication Message Types */
+#define CEVTIND_EI             0x01
+#define CEVTIND_DOI            0x02
+#define CEVTIND_LOST           0x04
+#define CEVTIND_FULL           0x08
+#define CEVTIND_BEI            0x10
+
+#define CEVTIND_CHIP_SJA1000   0x02
+
+#define ICAN3_BUSERR_QUOTA_MAX 255
+
+/* Janz ICAN3 CAN Frame Conversion */
+#define ICAN3_ECHO     0x10
+#define ICAN3_EFF_RTR  0x40
+#define ICAN3_SFF_RTR  0x10
+#define ICAN3_EFF      0x80
+
+#define ICAN3_CAN_TYPE_MASK    0x0f
+#define ICAN3_CAN_TYPE_SFF     0x00
+#define ICAN3_CAN_TYPE_EFF     0x01
+
+#define ICAN3_CAN_DLC_MASK     0x0f
+
+/*
+ * SJA1000 Status and Error Register Definitions
+ *
+ * Copied from drivers/net/can/sja1000/sja1000.h
+ */
+
+/* status register content */
+#define SR_BS          0x80
+#define SR_ES          0x40
+#define SR_TS          0x20
+#define SR_RS          0x10
+#define SR_TCS         0x08
+#define SR_TBS         0x04
+#define SR_DOS         0x02
+#define SR_RBS         0x01
+
+#define SR_CRIT (SR_BS|SR_ES)
+
+/* ECC register */
+#define ECC_SEG                0x1F
+#define ECC_DIR                0x20
+#define ECC_ERR                6
+#define ECC_BIT                0x00
+#define ECC_FORM       0x40
+#define ECC_STUFF      0x80
+#define ECC_MASK       0xc0
+
+/* Number of buffers for use in the "new-style" host interface */
+#define ICAN3_NEW_BUFFERS      16
+
+/* Number of buffers for use in the "fast" host interface */
+#define ICAN3_TX_BUFFERS       512
+#define ICAN3_RX_BUFFERS       1024
+
+/* SJA1000 Clock Input */
+#define ICAN3_CAN_CLOCK                8000000
+
+/* Driver Name */
+#define DRV_NAME "janz-ican3"
+
+/* DPM Control Registers -- starts at offset 0x100 in the MODULbus registers */
+struct ican3_dpm_control {
+       /* window address register */
+       u8 window_address;
+       u8 unused1;
+
+       /*
+        * Read access: clear interrupt from microcontroller
+        * Write access: send interrupt to microcontroller
+        */
+       u8 interrupt;
+       u8 unused2;
+
+       /* write-only: reset all hardware on the module */
+       u8 hwreset;
+       u8 unused3;
+
+       /* write-only: generate an interrupt to the TPU */
+       u8 tpuinterrupt;
+};
+
+struct ican3_dev {
+
+       /* must be the first member */
+       struct can_priv can;
+
+       /* CAN network device */
+       struct net_device *ndev;
+       struct napi_struct napi;
+
+       /* Device for printing */
+       struct device *dev;
+
+       /* module number */
+       unsigned int num;
+
+       /* base address of registers and IRQ */
+       struct janz_cmodio_onboard_regs __iomem *ctrl;
+       struct ican3_dpm_control __iomem *dpmctrl;
+       void __iomem *dpm;
+       int irq;
+
+       /* CAN bus termination status */
+       struct completion termination_comp;
+       bool termination_enabled;
+
+       /* CAN bus error status registers */
+       struct completion buserror_comp;
+       struct can_berr_counter bec;
+
+       /* old and new style host interface */
+       unsigned int iftype;
+
+       /*
+        * Any function which changes the current DPM page must hold this
+        * lock while it is performing data accesses. This ensures that the
+        * function will not be preempted and end up reading data from a
+        * different DPM page than it expects.
+        */
+       spinlock_t lock;
+
+       /* new host interface */
+       unsigned int rx_int;
+       unsigned int rx_num;
+       unsigned int tx_num;
+
+       /* fast host interface */
+       unsigned int fastrx_start;
+       unsigned int fastrx_int;
+       unsigned int fastrx_num;
+       unsigned int fasttx_start;
+       unsigned int fasttx_num;
+
+       /* first free DPM page */
+       unsigned int free_page;
+};
+
+struct ican3_msg {
+       u8 control;
+       u8 spec;
+       __le16 len;
+       u8 data[252];
+};
+
+struct ican3_new_desc {
+       u8 control;
+       u8 pointer;
+};
+
+struct ican3_fast_desc {
+       u8 control;
+       u8 command;
+       u8 data[14];
+};
+
+/* write to the window basic address register */
+static inline void ican3_set_page(struct ican3_dev *mod, unsigned int page)
+{
+       BUG_ON(page >= DPM_NUM_PAGES);
+       iowrite8(page, &mod->dpmctrl->window_address);
+}
+
+/*
+ * ICAN3 "old-style" host interface
+ */
+
+/*
+ * Recieve a message from the ICAN3 "old-style" firmware interface
+ *
+ * LOCKING: must hold mod->lock
+ *
+ * returns 0 on success, -ENOMEM when no message exists
+ */
+static int ican3_old_recv_msg(struct ican3_dev *mod, struct ican3_msg *msg)
+{
+       unsigned int mbox, mbox_page;
+       u8 locl, peer, xord;
+
+       /* get the MSYNC registers */
+       ican3_set_page(mod, QUEUE_OLD_CONTROL);
+       peer = ioread8(mod->dpm + MSYNC_PEER);
+       locl = ioread8(mod->dpm + MSYNC_LOCL);
+       xord = locl ^ peer;
+
+       if ((xord & MSYNC_RB_MASK) == 0x00) {
+               dev_dbg(mod->dev, "no mbox for reading\n");
+               return -ENOMEM;
+       }
+
+       /* find the first free mbox to read */
+       if ((xord & MSYNC_RB_MASK) == MSYNC_RB_MASK)
+               mbox = (xord & MSYNC_RBLW) ? MSYNC_RB0 : MSYNC_RB1;
+       else
+               mbox = (xord & MSYNC_RB0) ? MSYNC_RB0 : MSYNC_RB1;
+
+       /* copy the message */
+       mbox_page = (mbox == MSYNC_RB0) ? QUEUE_OLD_RB0 : QUEUE_OLD_RB1;
+       ican3_set_page(mod, mbox_page);
+       memcpy_fromio(msg, mod->dpm, sizeof(*msg));
+
+       /*
+        * notify the firmware that the read buffer is available
+        * for it to fill again
+        */
+       locl ^= mbox;
+
+       ican3_set_page(mod, QUEUE_OLD_CONTROL);
+       iowrite8(locl, mod->dpm + MSYNC_LOCL);
+       return 0;
+}
+
+/*
+ * Send a message through the "old-style" firmware interface
+ *
+ * LOCKING: must hold mod->lock
+ *
+ * returns 0 on success, -ENOMEM when no free space exists
+ */
+static int ican3_old_send_msg(struct ican3_dev *mod, struct ican3_msg *msg)
+{
+       unsigned int mbox, mbox_page;
+       u8 locl, peer, xord;
+
+       /* get the MSYNC registers */
+       ican3_set_page(mod, QUEUE_OLD_CONTROL);
+       peer = ioread8(mod->dpm + MSYNC_PEER);
+       locl = ioread8(mod->dpm + MSYNC_LOCL);
+       xord = locl ^ peer;
+
+       if ((xord & MSYNC_WB_MASK) == MSYNC_WB_MASK) {
+               dev_err(mod->dev, "no mbox for writing\n");
+               return -ENOMEM;
+       }
+
+       /* calculate a free mbox to use */
+       mbox = (xord & MSYNC_WB0) ? MSYNC_WB1 : MSYNC_WB0;
+
+       /* copy the message to the DPM */
+       mbox_page = (mbox == MSYNC_WB0) ? QUEUE_OLD_WB0 : QUEUE_OLD_WB1;
+       ican3_set_page(mod, mbox_page);
+       memcpy_toio(mod->dpm, msg, sizeof(*msg));
+
+       locl ^= mbox;
+       if (mbox == MSYNC_WB1)
+               locl |= MSYNC_WBLW;
+
+       ican3_set_page(mod, QUEUE_OLD_CONTROL);
+       iowrite8(locl, mod->dpm + MSYNC_LOCL);
+       return 0;
+}
+
+/*
+ * ICAN3 "new-style" Host Interface Setup
+ */
+
+static void __devinit ican3_init_new_host_interface(struct ican3_dev *mod)
+{
+       struct ican3_new_desc desc;
+       unsigned long flags;
+       void __iomem *dst;
+       int i;
+
+       spin_lock_irqsave(&mod->lock, flags);
+
+       /* setup the internal datastructures for RX */
+       mod->rx_num = 0;
+       mod->rx_int = 0;
+
+       /* tohost queue descriptors are in page 5 */
+       ican3_set_page(mod, QUEUE_TOHOST);
+       dst = mod->dpm;
+
+       /* initialize the tohost (rx) queue descriptors: pages 9-24 */
+       for (i = 0; i < ICAN3_NEW_BUFFERS; i++) {
+               desc.control = DESC_INTERRUPT | DESC_LEN(1); /* I L=1 */
+               desc.pointer = mod->free_page;
+
+               /* set wrap flag on last buffer */
+               if (i == ICAN3_NEW_BUFFERS - 1)
+                       desc.control |= DESC_WRAP;
+
+               memcpy_toio(dst, &desc, sizeof(desc));
+               dst += sizeof(desc);
+               mod->free_page++;
+       }
+
+       /* fromhost (tx) mid queue descriptors are in page 6 */
+       ican3_set_page(mod, QUEUE_FROMHOST_MID);
+       dst = mod->dpm;
+
+       /* setup the internal datastructures for TX */
+       mod->tx_num = 0;
+
+       /* initialize the fromhost mid queue descriptors: pages 25-40 */
+       for (i = 0; i < ICAN3_NEW_BUFFERS; i++) {
+               desc.control = DESC_VALID | DESC_LEN(1); /* V L=1 */
+               desc.pointer = mod->free_page;
+
+               /* set wrap flag on last buffer */
+               if (i == ICAN3_NEW_BUFFERS - 1)
+                       desc.control |= DESC_WRAP;
+
+               memcpy_toio(dst, &desc, sizeof(desc));
+               dst += sizeof(desc);
+               mod->free_page++;
+       }
+
+       /* fromhost hi queue descriptors are in page 7 */
+       ican3_set_page(mod, QUEUE_FROMHOST_HIGH);
+       dst = mod->dpm;
+
+       /* initialize only a single buffer in the fromhost hi queue (unused) */
+       desc.control = DESC_VALID | DESC_WRAP | DESC_LEN(1); /* VW L=1 */
+       desc.pointer = mod->free_page;
+       memcpy_toio(dst, &desc, sizeof(desc));
+       mod->free_page++;
+
+       /* fromhost low queue descriptors are in page 8 */
+       ican3_set_page(mod, QUEUE_FROMHOST_LOW);
+       dst = mod->dpm;
+
+       /* initialize only a single buffer in the fromhost low queue (unused) */
+       desc.control = DESC_VALID | DESC_WRAP | DESC_LEN(1); /* VW L=1 */
+       desc.pointer = mod->free_page;
+       memcpy_toio(dst, &desc, sizeof(desc));
+       mod->free_page++;
+
+       spin_unlock_irqrestore(&mod->lock, flags);
+}
+
+/*
+ * ICAN3 Fast Host Interface Setup
+ */
+
+static void __devinit ican3_init_fast_host_interface(struct ican3_dev *mod)
+{
+       struct ican3_fast_desc desc;
+       unsigned long flags;
+       unsigned int addr;
+       void __iomem *dst;
+       int i;
+
+       spin_lock_irqsave(&mod->lock, flags);
+
+       /* save the start recv page */
+       mod->fastrx_start = mod->free_page;
+       mod->fastrx_num = 0;
+       mod->fastrx_int = 0;
+
+       /* build a single fast tohost queue descriptor */
+       memset(&desc, 0, sizeof(desc));
+       desc.control = 0x00;
+       desc.command = 1;
+
+       /* build the tohost queue descriptor ring in memory */
+       addr = 0;
+       for (i = 0; i < ICAN3_RX_BUFFERS; i++) {
+
+               /* set the wrap bit on the last buffer */
+               if (i == ICAN3_RX_BUFFERS - 1)
+                       desc.control |= DESC_WRAP;
+
+               /* switch to the correct page */
+               ican3_set_page(mod, mod->free_page);
+
+               /* copy the descriptor to the DPM */
+               dst = mod->dpm + addr;
+               memcpy_toio(dst, &desc, sizeof(desc));
+               addr += sizeof(desc);
+
+               /* move to the next page if necessary */
+               if (addr >= DPM_PAGE_SIZE) {
+                       addr = 0;
+                       mod->free_page++;
+               }
+       }
+
+       /* make sure we page-align the next queue */
+       if (addr != 0)
+               mod->free_page++;
+
+       /* save the start xmit page */
+       mod->fasttx_start = mod->free_page;
+       mod->fasttx_num = 0;
+
+       /* build a single fast fromhost queue descriptor */
+       memset(&desc, 0, sizeof(desc));
+       desc.control = DESC_VALID;
+       desc.command = 1;
+
+       /* build the fromhost queue descriptor ring in memory */
+       addr = 0;
+       for (i = 0; i < ICAN3_TX_BUFFERS; i++) {
+
+               /* set the wrap bit on the last buffer */
+               if (i == ICAN3_TX_BUFFERS - 1)
+                       desc.control |= DESC_WRAP;
+
+               /* switch to the correct page */
+               ican3_set_page(mod, mod->free_page);
+
+               /* copy the descriptor to the DPM */
+               dst = mod->dpm + addr;
+               memcpy_toio(dst, &desc, sizeof(desc));
+               addr += sizeof(desc);
+
+               /* move to the next page if necessary */
+               if (addr >= DPM_PAGE_SIZE) {
+                       addr = 0;
+                       mod->free_page++;
+               }
+       }
+
+       spin_unlock_irqrestore(&mod->lock, flags);
+}
+
+/*
+ * ICAN3 "new-style" Host Interface Message Helpers
+ */
+
+/*
+ * LOCKING: must hold mod->lock
+ */
+static int ican3_new_send_msg(struct ican3_dev *mod, struct ican3_msg *msg)
+{
+       struct ican3_new_desc desc;
+       void __iomem *desc_addr = mod->dpm + (mod->tx_num * sizeof(desc));
+
+       /* switch to the fromhost mid queue, and read the buffer descriptor */
+       ican3_set_page(mod, QUEUE_FROMHOST_MID);
+       memcpy_fromio(&desc, desc_addr, sizeof(desc));
+
+       if (!(desc.control & DESC_VALID)) {
+               dev_dbg(mod->dev, "%s: no free buffers\n", __func__);
+               return -ENOMEM;
+       }
+
+       /* switch to the data page, copy the data */
+       ican3_set_page(mod, desc.pointer);
+       memcpy_toio(mod->dpm, msg, sizeof(*msg));
+
+       /* switch back to the descriptor, set the valid bit, write it back */
+       ican3_set_page(mod, QUEUE_FROMHOST_MID);
+       desc.control ^= DESC_VALID;
+       memcpy_toio(desc_addr, &desc, sizeof(desc));
+
+       /* update the tx number */
+       mod->tx_num = (desc.control & DESC_WRAP) ? 0 : (mod->tx_num + 1);
+       return 0;
+}
+
+/*
+ * LOCKING: must hold mod->lock
+ */
+static int ican3_new_recv_msg(struct ican3_dev *mod, struct ican3_msg *msg)
+{
+       struct ican3_new_desc desc;
+       void __iomem *desc_addr = mod->dpm + (mod->rx_num * sizeof(desc));
+
+       /* switch to the tohost queue, and read the buffer descriptor */
+       ican3_set_page(mod, QUEUE_TOHOST);
+       memcpy_fromio(&desc, desc_addr, sizeof(desc));
+
+       if (!(desc.control & DESC_VALID)) {
+               dev_dbg(mod->dev, "%s: no buffers to recv\n", __func__);
+               return -ENOMEM;
+       }
+
+       /* switch to the data page, copy the data */
+       ican3_set_page(mod, desc.pointer);
+       memcpy_fromio(msg, mod->dpm, sizeof(*msg));
+
+       /* switch back to the descriptor, toggle the valid bit, write it back */
+       ican3_set_page(mod, QUEUE_TOHOST);
+       desc.control ^= DESC_VALID;
+       memcpy_toio(desc_addr, &desc, sizeof(desc));
+
+       /* update the rx number */
+       mod->rx_num = (desc.control & DESC_WRAP) ? 0 : (mod->rx_num + 1);
+       return 0;
+}
+
+/*
+ * Message Send / Recv Helpers
+ */
+
+static int ican3_send_msg(struct ican3_dev *mod, struct ican3_msg *msg)
+{
+       unsigned long flags;
+       int ret;
+
+       spin_lock_irqsave(&mod->lock, flags);
+
+       if (mod->iftype == 0)
+               ret = ican3_old_send_msg(mod, msg);
+       else
+               ret = ican3_new_send_msg(mod, msg);
+
+       spin_unlock_irqrestore(&mod->lock, flags);
+       return ret;
+}
+
+static int ican3_recv_msg(struct ican3_dev *mod, struct ican3_msg *msg)
+{
+       unsigned long flags;
+       int ret;
+
+       spin_lock_irqsave(&mod->lock, flags);
+
+       if (mod->iftype == 0)
+               ret = ican3_old_recv_msg(mod, msg);
+       else
+               ret = ican3_new_recv_msg(mod, msg);
+
+       spin_unlock_irqrestore(&mod->lock, flags);
+       return ret;
+}
+
+/*
+ * Quick Pre-constructed Messages
+ */
+
+static int __devinit ican3_msg_connect(struct ican3_dev *mod)
+{
+       struct ican3_msg msg;
+
+       memset(&msg, 0, sizeof(msg));
+       msg.spec = MSG_CONNECTI;
+       msg.len = cpu_to_le16(0);
+
+       return ican3_send_msg(mod, &msg);
+}
+
+static int __devexit ican3_msg_disconnect(struct ican3_dev *mod)
+{
+       struct ican3_msg msg;
+
+       memset(&msg, 0, sizeof(msg));
+       msg.spec = MSG_DISCONNECT;
+       msg.len = cpu_to_le16(0);
+
+       return ican3_send_msg(mod, &msg);
+}
+
+static int __devinit ican3_msg_newhostif(struct ican3_dev *mod)
+{
+       struct ican3_msg msg;
+       int ret;
+
+       memset(&msg, 0, sizeof(msg));
+       msg.spec = MSG_NEWHOSTIF;
+       msg.len = cpu_to_le16(0);
+
+       /* If we're not using the old interface, switching seems bogus */
+       WARN_ON(mod->iftype != 0);
+
+       ret = ican3_send_msg(mod, &msg);
+       if (ret)
+               return ret;
+
+       /* mark the module as using the new host interface */
+       mod->iftype = 1;
+       return 0;
+}
+
+static int __devinit ican3_msg_fasthostif(struct ican3_dev *mod)
+{
+       struct ican3_msg msg;
+       unsigned int addr;
+
+       memset(&msg, 0, sizeof(msg));
+       msg.spec = MSG_INITFDPMQUEUE;
+       msg.len = cpu_to_le16(8);
+
+       /* write the tohost queue start address */
+       addr = DPM_PAGE_ADDR(mod->fastrx_start);
+       msg.data[0] = addr & 0xff;
+       msg.data[1] = (addr >> 8) & 0xff;
+       msg.data[2] = (addr >> 16) & 0xff;
+       msg.data[3] = (addr >> 24) & 0xff;
+
+       /* write the fromhost queue start address */
+       addr = DPM_PAGE_ADDR(mod->fasttx_start);
+       msg.data[4] = addr & 0xff;
+       msg.data[5] = (addr >> 8) & 0xff;
+       msg.data[6] = (addr >> 16) & 0xff;
+       msg.data[7] = (addr >> 24) & 0xff;
+
+       /* If we're not using the new interface yet, we cannot do this */
+       WARN_ON(mod->iftype != 1);
+
+       return ican3_send_msg(mod, &msg);
+}
+
+/*
+ * Setup the CAN filter to either accept or reject all
+ * messages from the CAN bus.
+ */
+static int __devinit ican3_set_id_filter(struct ican3_dev *mod, bool accept)
+{
+       struct ican3_msg msg;
+       int ret;
+
+       /* Standard Frame Format */
+       memset(&msg, 0, sizeof(msg));
+       msg.spec = MSG_SETAFILMASK;
+       msg.len = cpu_to_le16(5);
+       msg.data[0] = 0x00; /* IDLo LSB */
+       msg.data[1] = 0x00; /* IDLo MSB */
+       msg.data[2] = 0xff; /* IDHi LSB */
+       msg.data[3] = 0x07; /* IDHi MSB */
+
+       /* accept all frames for fast host if, or reject all frames */
+       msg.data[4] = accept ? SETAFILMASK_FASTIF : SETAFILMASK_REJECT;
+
+       ret = ican3_send_msg(mod, &msg);
+       if (ret)
+               return ret;
+
+       /* Extended Frame Format */
+       memset(&msg, 0, sizeof(msg));
+       msg.spec = MSG_SETAFILMASK;
+       msg.len = cpu_to_le16(13);
+       msg.data[0] = 0;    /* MUX = 0 */
+       msg.data[1] = 0x00; /* IDLo LSB */
+       msg.data[2] = 0x00;
+       msg.data[3] = 0x00;
+       msg.data[4] = 0x20; /* IDLo MSB */
+       msg.data[5] = 0xff; /* IDHi LSB */
+       msg.data[6] = 0xff;
+       msg.data[7] = 0xff;
+       msg.data[8] = 0x3f; /* IDHi MSB */
+
+       /* accept all frames for fast host if, or reject all frames */
+       msg.data[9] = accept ? SETAFILMASK_FASTIF : SETAFILMASK_REJECT;
+
+       return ican3_send_msg(mod, &msg);
+}
+
+/*
+ * Bring the CAN bus online or offline
+ */
+static int ican3_set_bus_state(struct ican3_dev *mod, bool on)
+{
+       struct ican3_msg msg;
+
+       memset(&msg, 0, sizeof(msg));
+       msg.spec = on ? MSG_CONREQ : MSG_COFFREQ;
+       msg.len = cpu_to_le16(0);
+
+       return ican3_send_msg(mod, &msg);
+}
+
+static int ican3_set_termination(struct ican3_dev *mod, bool on)
+{
+       struct ican3_msg msg;
+
+       memset(&msg, 0, sizeof(msg));
+       msg.spec = MSG_HWCONF;
+       msg.len = cpu_to_le16(2);
+       msg.data[0] = 0x00;
+       msg.data[1] = on ? HWCONF_TERMINATE_ON : HWCONF_TERMINATE_OFF;
+
+       return ican3_send_msg(mod, &msg);
+}
+
+static int ican3_send_inquiry(struct ican3_dev *mod, u8 subspec)
+{
+       struct ican3_msg msg;
+
+       memset(&msg, 0, sizeof(msg));
+       msg.spec = MSG_INQUIRY;
+       msg.len = cpu_to_le16(2);
+       msg.data[0] = subspec;
+       msg.data[1] = 0x00;
+
+       return ican3_send_msg(mod, &msg);
+}
+
+static int ican3_set_buserror(struct ican3_dev *mod, u8 quota)
+{
+       struct ican3_msg msg;
+
+       memset(&msg, 0, sizeof(msg));
+       msg.spec = MSG_CCONFREQ;
+       msg.len = cpu_to_le16(2);
+       msg.data[0] = 0x00;
+       msg.data[1] = quota;
+
+       return ican3_send_msg(mod, &msg);
+}
+
+/*
+ * ICAN3 to Linux CAN Frame Conversion
+ */
+
+static void ican3_to_can_frame(struct ican3_dev *mod,
+                              struct ican3_fast_desc *desc,
+                              struct can_frame *cf)
+{
+       if ((desc->command & ICAN3_CAN_TYPE_MASK) == ICAN3_CAN_TYPE_SFF) {
+               if (desc->data[1] & ICAN3_SFF_RTR)
+                       cf->can_id |= CAN_RTR_FLAG;
+
+               cf->can_id |= desc->data[0] << 3;
+               cf->can_id |= (desc->data[1] & 0xe0) >> 5;
+               cf->can_dlc = desc->data[1] & ICAN3_CAN_DLC_MASK;
+               memcpy(cf->data, &desc->data[2], sizeof(cf->data));
+       } else {
+               cf->can_dlc = desc->data[0] & ICAN3_CAN_DLC_MASK;
+               if (desc->data[0] & ICAN3_EFF_RTR)
+                       cf->can_id |= CAN_RTR_FLAG;
+
+               if (desc->data[0] & ICAN3_EFF) {
+                       cf->can_id |= CAN_EFF_FLAG;
+                       cf->can_id |= desc->data[2] << 21; /* 28-21 */
+                       cf->can_id |= desc->data[3] << 13; /* 20-13 */
+                       cf->can_id |= desc->data[4] << 5;  /* 12-5  */
+                       cf->can_id |= (desc->data[5] & 0xf8) >> 3;
+               } else {
+                       cf->can_id |= desc->data[2] << 3;  /* 10-3  */
+                       cf->can_id |= desc->data[3] >> 5;  /* 2-0   */
+               }
+
+               memcpy(cf->data, &desc->data[6], sizeof(cf->data));
+       }
+}
+
+static void can_frame_to_ican3(struct ican3_dev *mod,
+                              struct can_frame *cf,
+                              struct ican3_fast_desc *desc)
+{
+       /* clear out any stale data in the descriptor */
+       memset(desc->data, 0, sizeof(desc->data));
+
+       /* we always use the extended format, with the ECHO flag set */
+       desc->command = ICAN3_CAN_TYPE_EFF;
+       desc->data[0] |= cf->can_dlc;
+       desc->data[1] |= ICAN3_ECHO;
+
+       if (cf->can_id & CAN_RTR_FLAG)
+               desc->data[0] |= ICAN3_EFF_RTR;
+
+       /* pack the id into the correct places */
+       if (cf->can_id & CAN_EFF_FLAG) {
+               desc->data[0] |= ICAN3_EFF;
+               desc->data[2] = (cf->can_id & 0x1fe00000) >> 21; /* 28-21 */
+               desc->data[3] = (cf->can_id & 0x001fe000) >> 13; /* 20-13 */
+               desc->data[4] = (cf->can_id & 0x00001fe0) >> 5;  /* 12-5  */
+               desc->data[5] = (cf->can_id & 0x0000001f) << 3;  /* 4-0   */
+       } else {
+               desc->data[2] = (cf->can_id & 0x7F8) >> 3; /* bits 10-3 */
+               desc->data[3] = (cf->can_id & 0x007) << 5; /* bits 2-0  */
+       }
+
+       /* copy the data bits into the descriptor */
+       memcpy(&desc->data[6], cf->data, sizeof(cf->data));
+}
+
+/*
+ * Interrupt Handling
+ */
+
+/*
+ * Handle an ID + Version message response from the firmware. We never generate
+ * this message in production code, but it is very useful when debugging to be
+ * able to display this message.
+ */
+static void ican3_handle_idvers(struct ican3_dev *mod, struct ican3_msg *msg)
+{
+       dev_dbg(mod->dev, "IDVERS response: %s\n", msg->data);
+}
+
+static void ican3_handle_msglost(struct ican3_dev *mod, struct ican3_msg *msg)
+{
+       struct net_device *dev = mod->ndev;
+       struct net_device_stats *stats = &dev->stats;
+       struct can_frame *cf;
+       struct sk_buff *skb;
+
+       /*
+        * Report that communication messages with the microcontroller firmware
+        * are being lost. These are never CAN frames, so we do not generate an
+        * error frame for userspace
+        */
+       if (msg->spec == MSG_MSGLOST) {
+               dev_err(mod->dev, "lost %d control messages\n", msg->data[0]);
+               return;
+       }
+
+       /*
+        * Oops, this indicates that we have lost messages in the fast queue,
+        * which are exclusively CAN messages. Our driver isn't reading CAN
+        * frames fast enough.
+        *
+        * We'll pretend that the SJA1000 told us that it ran out of buffer
+        * space, because there is not a better message for this.
+        */
+       skb = alloc_can_err_skb(dev, &cf);
+       if (skb) {
+               cf->can_id |= CAN_ERR_CRTL;
+               cf->data[1] = CAN_ERR_CRTL_RX_OVERFLOW;
+               stats->rx_errors++;
+               stats->rx_bytes += cf->can_dlc;
+               netif_rx(skb);
+       }
+}
+
+/*
+ * Handle CAN Event Indication Messages from the firmware
+ *
+ * The ICAN3 firmware provides the values of some SJA1000 registers when it
+ * generates this message. The code below is largely copied from the
+ * drivers/net/can/sja1000/sja1000.c file, and adapted as necessary
+ */
+static int ican3_handle_cevtind(struct ican3_dev *mod, struct ican3_msg *msg)
+{
+       struct net_device *dev = mod->ndev;
+       struct net_device_stats *stats = &dev->stats;
+       enum can_state state = mod->can.state;
+       u8 status, isrc, rxerr, txerr;
+       struct can_frame *cf;
+       struct sk_buff *skb;
+
+       /* we can only handle the SJA1000 part */
+       if (msg->data[1] != CEVTIND_CHIP_SJA1000) {
+               dev_err(mod->dev, "unable to handle errors on non-SJA1000\n");
+               return -ENODEV;
+       }
+
+       /* check the message length for sanity */
+       if (le16_to_cpu(msg->len) < 6) {
+               dev_err(mod->dev, "error message too short\n");
+               return -EINVAL;
+       }
+
+       skb = alloc_can_err_skb(dev, &cf);
+       if (skb == NULL)
+               return -ENOMEM;
+
+       isrc = msg->data[0];
+       status = msg->data[3];
+       rxerr = msg->data[4];
+       txerr = msg->data[5];
+
+       /* data overrun interrupt */
+       if (isrc == CEVTIND_DOI || isrc == CEVTIND_LOST) {
+               dev_dbg(mod->dev, "data overrun interrupt\n");
+               cf->can_id |= CAN_ERR_CRTL;
+               cf->data[1] = CAN_ERR_CRTL_RX_OVERFLOW;
+               stats->rx_over_errors++;
+               stats->rx_errors++;
+       }
+
+       /* error warning + passive interrupt */
+       if (isrc == CEVTIND_EI) {
+               dev_dbg(mod->dev, "error warning + passive interrupt\n");
+               if (status & SR_BS) {
+                       state = CAN_STATE_BUS_OFF;
+                       cf->can_id |= CAN_ERR_BUSOFF;
+                       can_bus_off(dev);
+               } else if (status & SR_ES) {
+                       if (rxerr >= 128 || txerr >= 128)
+                               state = CAN_STATE_ERROR_PASSIVE;
+                       else
+                               state = CAN_STATE_ERROR_WARNING;
+               } else {
+                       state = CAN_STATE_ERROR_ACTIVE;
+               }
+       }
+
+       /* bus error interrupt */
+       if (isrc == CEVTIND_BEI) {
+               u8 ecc = msg->data[2];
+
+               dev_dbg(mod->dev, "bus error interrupt\n");
+               mod->can.can_stats.bus_error++;
+               stats->rx_errors++;
+               cf->can_id |= CAN_ERR_PROT | CAN_ERR_BUSERROR;
+
+               switch (ecc & ECC_MASK) {
+               case ECC_BIT:
+                       cf->data[2] |= CAN_ERR_PROT_BIT;
+                       break;
+               case ECC_FORM:
+                       cf->data[2] |= CAN_ERR_PROT_FORM;
+                       break;
+               case ECC_STUFF:
+                       cf->data[2] |= CAN_ERR_PROT_STUFF;
+                       break;
+               default:
+                       cf->data[2] |= CAN_ERR_PROT_UNSPEC;
+                       cf->data[3] = ecc & ECC_SEG;
+                       break;
+               }
+
+               if ((ecc & ECC_DIR) == 0)
+                       cf->data[2] |= CAN_ERR_PROT_TX;
+
+               cf->data[6] = txerr;
+               cf->data[7] = rxerr;
+       }
+
+       if (state != mod->can.state && (state == CAN_STATE_ERROR_WARNING ||
+                                       state == CAN_STATE_ERROR_PASSIVE)) {
+               cf->can_id |= CAN_ERR_CRTL;
+               if (state == CAN_STATE_ERROR_WARNING) {
+                       mod->can.can_stats.error_warning++;
+                       cf->data[1] = (txerr > rxerr) ?
+                               CAN_ERR_CRTL_TX_WARNING :
+                               CAN_ERR_CRTL_RX_WARNING;
+               } else {
+                       mod->can.can_stats.error_passive++;
+                       cf->data[1] = (txerr > rxerr) ?
+                               CAN_ERR_CRTL_TX_PASSIVE :
+                               CAN_ERR_CRTL_RX_PASSIVE;
+               }
+
+               cf->data[6] = txerr;
+               cf->data[7] = rxerr;
+       }
+
+       mod->can.state = state;
+       stats->rx_errors++;
+       stats->rx_bytes += cf->can_dlc;
+       netif_rx(skb);
+       return 0;
+}
+
+static void ican3_handle_inquiry(struct ican3_dev *mod, struct ican3_msg *msg)
+{
+       switch (msg->data[0]) {
+       case INQUIRY_STATUS:
+       case INQUIRY_EXTENDED:
+               mod->bec.rxerr = msg->data[5];
+               mod->bec.txerr = msg->data[6];
+               complete(&mod->buserror_comp);
+               break;
+       case INQUIRY_TERMINATION:
+               mod->termination_enabled = msg->data[6] & HWCONF_TERMINATE_ON;
+               complete(&mod->termination_comp);
+               break;
+       default:
+               dev_err(mod->dev, "recieved an unknown inquiry response\n");
+               break;
+       }
+}
+
+static void ican3_handle_unknown_message(struct ican3_dev *mod,
+                                       struct ican3_msg *msg)
+{
+       dev_warn(mod->dev, "recieved unknown message: spec 0x%.2x length %d\n",
+                          msg->spec, le16_to_cpu(msg->len));
+}
+
+/*
+ * Handle a control message from the firmware
+ */
+static void ican3_handle_message(struct ican3_dev *mod, struct ican3_msg *msg)
+{
+       dev_dbg(mod->dev, "%s: modno %d spec 0x%.2x len %d bytes\n", __func__,
+                          mod->num, msg->spec, le16_to_cpu(msg->len));
+
+       switch (msg->spec) {
+       case MSG_IDVERS:
+               ican3_handle_idvers(mod, msg);
+               break;
+       case MSG_MSGLOST:
+       case MSG_FMSGLOST:
+               ican3_handle_msglost(mod, msg);
+               break;
+       case MSG_CEVTIND:
+               ican3_handle_cevtind(mod, msg);
+               break;
+       case MSG_INQUIRY:
+               ican3_handle_inquiry(mod, msg);
+               break;
+       default:
+               ican3_handle_unknown_message(mod, msg);
+               break;
+       }
+}
+
+/*
+ * Check that there is room in the TX ring to transmit another skb
+ *
+ * LOCKING: must hold mod->lock
+ */
+static bool ican3_txok(struct ican3_dev *mod)
+{
+       struct ican3_fast_desc __iomem *desc;
+       u8 control;
+
+       /* copy the control bits of the descriptor */
+       ican3_set_page(mod, mod->fasttx_start + (mod->fasttx_num / 16));
+       desc = mod->dpm + ((mod->fasttx_num % 16) * sizeof(*desc));
+       control = ioread8(&desc->control);
+
+       /* if the control bits are not valid, then we have no more space */
+       if (!(control & DESC_VALID))
+               return false;
+
+       return true;
+}
+
+/*
+ * Recieve one CAN frame from the hardware
+ *
+ * This works like the core of a NAPI function, but is intended to be called
+ * from workqueue context instead. This driver already needs a workqueue to
+ * process control messages, so we use the workqueue instead of using NAPI.
+ * This was done to simplify locking.
+ *
+ * CONTEXT: must be called from user context
+ */
+static int ican3_recv_skb(struct ican3_dev *mod)
+{
+       struct net_device *ndev = mod->ndev;
+       struct net_device_stats *stats = &ndev->stats;
+       struct ican3_fast_desc desc;
+       void __iomem *desc_addr;
+       struct can_frame *cf;
+       struct sk_buff *skb;
+       unsigned long flags;
+
+       spin_lock_irqsave(&mod->lock, flags);
+
+       /* copy the whole descriptor */
+       ican3_set_page(mod, mod->fastrx_start + (mod->fastrx_num / 16));
+       desc_addr = mod->dpm + ((mod->fastrx_num % 16) * sizeof(desc));
+       memcpy_fromio(&desc, desc_addr, sizeof(desc));
+
+       spin_unlock_irqrestore(&mod->lock, flags);
+
+       /* check that we actually have a CAN frame */
+       if (!(desc.control & DESC_VALID))
+               return -ENOBUFS;
+
+       /* allocate an skb */
+       skb = alloc_can_skb(ndev, &cf);
+       if (unlikely(skb == NULL)) {
+               stats->rx_dropped++;
+               goto err_noalloc;
+       }
+
+       /* convert the ICAN3 frame into Linux CAN format */
+       ican3_to_can_frame(mod, &desc, cf);
+
+       /* receive the skb, update statistics */
+       netif_receive_skb(skb);
+       stats->rx_packets++;
+       stats->rx_bytes += cf->can_dlc;
+
+err_noalloc:
+       /* toggle the valid bit and return the descriptor to the ring */
+       desc.control ^= DESC_VALID;
+
+       spin_lock_irqsave(&mod->lock, flags);
+
+       ican3_set_page(mod, mod->fastrx_start + (mod->fastrx_num / 16));
+       memcpy_toio(desc_addr, &desc, 1);
+
+       /* update the next buffer pointer */
+       mod->fastrx_num = (desc.control & DESC_WRAP) ? 0
+                                                    : (mod->fastrx_num + 1);
+
+       /* there are still more buffers to process */
+       spin_unlock_irqrestore(&mod->lock, flags);
+       return 0;
+}
+
+static int ican3_napi(struct napi_struct *napi, int budget)
+{
+       struct ican3_dev *mod = container_of(napi, struct ican3_dev, napi);
+       struct ican3_msg msg;
+       unsigned long flags;
+       int received = 0;
+       int ret;
+
+       /* process all communication messages */
+       while (true) {
+               ret = ican3_recv_msg(mod, &msg);
+               if (ret)
+                       break;
+
+               ican3_handle_message(mod, &msg);
+       }
+
+       /* process all CAN frames from the fast interface */
+       while (received < budget) {
+               ret = ican3_recv_skb(mod);
+               if (ret)
+                       break;
+
+               received++;
+       }
+
+       /* We have processed all packets that the adapter had, but it
+        * was less than our budget, stop polling */
+       if (received < budget)
+               napi_complete(napi);
+
+       spin_lock_irqsave(&mod->lock, flags);
+
+       /* Wake up the transmit queue if necessary */
+       if (netif_queue_stopped(mod->ndev) && ican3_txok(mod))
+               netif_wake_queue(mod->ndev);
+
+       spin_unlock_irqrestore(&mod->lock, flags);
+
+       /* re-enable interrupt generation */
+       iowrite8(1 << mod->num, &mod->ctrl->int_enable);
+       return received;
+}
+
+static irqreturn_t ican3_irq(int irq, void *dev_id)
+{
+       struct ican3_dev *mod = dev_id;
+       u8 stat;
+
+       /*
+        * The interrupt status register on this device reports interrupts
+        * as zeroes instead of using ones like most other devices
+        */
+       stat = ioread8(&mod->ctrl->int_disable) & (1 << mod->num);
+       if (stat == (1 << mod->num))
+               return IRQ_NONE;
+
+       /* clear the MODULbus interrupt from the microcontroller */
+       ioread8(&mod->dpmctrl->interrupt);
+
+       /* disable interrupt generation, schedule the NAPI poller */
+       iowrite8(1 << mod->num, &mod->ctrl->int_disable);
+       napi_schedule(&mod->napi);
+       return IRQ_HANDLED;
+}
+
+/*
+ * Firmware reset, startup, and shutdown
+ */
+
+/*
+ * Reset an ICAN module to its power-on state
+ *
+ * CONTEXT: no network device registered
+ * LOCKING: work function disabled
+ */
+static int ican3_reset_module(struct ican3_dev *mod)
+{
+       u8 val = 1 << mod->num;
+       unsigned long start;
+       u8 runold, runnew;
+
+       /* disable interrupts so no more work is scheduled */
+       iowrite8(1 << mod->num, &mod->ctrl->int_disable);
+
+       /* flush any pending work */
+       flush_scheduled_work();
+
+       /* the first unallocated page in the DPM is #9 */
+       mod->free_page = DPM_FREE_START;
+
+       ican3_set_page(mod, QUEUE_OLD_CONTROL);
+       runold = ioread8(mod->dpm + TARGET_RUNNING);
+
+       /* reset the module */
+       iowrite8(val, &mod->ctrl->reset_assert);
+       iowrite8(val, &mod->ctrl->reset_deassert);
+
+       /* wait until the module has finished resetting and is running */
+       start = jiffies;
+       do {
+               ican3_set_page(mod, QUEUE_OLD_CONTROL);
+               runnew = ioread8(mod->dpm + TARGET_RUNNING);
+               if (runnew == (runold ^ 0xff))
+                       return 0;
+
+               msleep(10);
+       } while (time_before(jiffies, start + HZ / 4));
+
+       dev_err(mod->dev, "failed to reset CAN module\n");
+       return -ETIMEDOUT;
+}
+
+static void __devexit ican3_shutdown_module(struct ican3_dev *mod)
+{
+       ican3_msg_disconnect(mod);
+       ican3_reset_module(mod);
+}
+
+/*
+ * Startup an ICAN module, bringing it into fast mode
+ */
+static int __devinit ican3_startup_module(struct ican3_dev *mod)
+{
+       int ret;
+
+       ret = ican3_reset_module(mod);
+       if (ret) {
+               dev_err(mod->dev, "unable to reset module\n");
+               return ret;
+       }
+
+       /* re-enable interrupts so we can send messages */
+       iowrite8(1 << mod->num, &mod->ctrl->int_enable);
+
+       ret = ican3_msg_connect(mod);
+       if (ret) {
+               dev_err(mod->dev, "unable to connect to module\n");
+               return ret;
+       }
+
+       ican3_init_new_host_interface(mod);
+       ret = ican3_msg_newhostif(mod);
+       if (ret) {
+               dev_err(mod->dev, "unable to switch to new-style interface\n");
+               return ret;
+       }
+
+       /* default to "termination on" */
+       ret = ican3_set_termination(mod, true);
+       if (ret) {
+               dev_err(mod->dev, "unable to enable termination\n");
+               return ret;
+       }
+
+       /* default to "bus errors enabled" */
+       ret = ican3_set_buserror(mod, ICAN3_BUSERR_QUOTA_MAX);
+       if (ret) {
+               dev_err(mod->dev, "unable to set bus-error\n");
+               return ret;
+       }
+
+       ican3_init_fast_host_interface(mod);
+       ret = ican3_msg_fasthostif(mod);
+       if (ret) {
+               dev_err(mod->dev, "unable to switch to fast host interface\n");
+               return ret;
+       }
+
+       ret = ican3_set_id_filter(mod, true);
+       if (ret) {
+               dev_err(mod->dev, "unable to set acceptance filter\n");
+               return ret;
+       }
+
+       return 0;
+}
+
+/*
+ * CAN Network Device
+ */
+
+static int ican3_open(struct net_device *ndev)
+{
+       struct ican3_dev *mod = netdev_priv(ndev);
+       u8 quota;
+       int ret;
+
+       /* open the CAN layer */
+       ret = open_candev(ndev);
+       if (ret) {
+               dev_err(mod->dev, "unable to start CAN layer\n");
+               return ret;
+       }
+
+       /* set the bus error generation state appropriately */
+       if (mod->can.ctrlmode & CAN_CTRLMODE_BERR_REPORTING)
+               quota = ICAN3_BUSERR_QUOTA_MAX;
+       else
+               quota = 0;
+
+       ret = ican3_set_buserror(mod, quota);
+       if (ret) {
+               dev_err(mod->dev, "unable to set bus-error\n");
+               close_candev(ndev);
+               return ret;
+       }
+
+       /* bring the bus online */
+       ret = ican3_set_bus_state(mod, true);
+       if (ret) {
+               dev_err(mod->dev, "unable to set bus-on\n");
+               close_candev(ndev);
+               return ret;
+       }
+
+       /* start up the network device */
+       mod->can.state = CAN_STATE_ERROR_ACTIVE;
+       netif_start_queue(ndev);
+
+       return 0;
+}
+
+static int ican3_stop(struct net_device *ndev)
+{
+       struct ican3_dev *mod = netdev_priv(ndev);
+       int ret;
+
+       /* stop the network device xmit routine */
+       netif_stop_queue(ndev);
+       mod->can.state = CAN_STATE_STOPPED;
+
+       /* bring the bus offline, stop receiving packets */
+       ret = ican3_set_bus_state(mod, false);
+       if (ret) {
+               dev_err(mod->dev, "unable to set bus-off\n");
+               return ret;
+       }
+
+       /* close the CAN layer */
+       close_candev(ndev);
+       return 0;
+}
+
+static int ican3_xmit(struct sk_buff *skb, struct net_device *ndev)
+{
+       struct ican3_dev *mod = netdev_priv(ndev);
+       struct net_device_stats *stats = &ndev->stats;
+       struct can_frame *cf = (struct can_frame *)skb->data;
+       struct ican3_fast_desc desc;
+       void __iomem *desc_addr;
+       unsigned long flags;
+
+       spin_lock_irqsave(&mod->lock, flags);
+
+       /* check that we can actually transmit */
+       if (!ican3_txok(mod)) {
+               dev_err(mod->dev, "no free descriptors, stopping queue\n");
+               netif_stop_queue(ndev);
+               spin_unlock_irqrestore(&mod->lock, flags);
+               return NETDEV_TX_BUSY;
+       }
+
+       /* copy the control bits of the descriptor */
+       ican3_set_page(mod, mod->fasttx_start + (mod->fasttx_num / 16));
+       desc_addr = mod->dpm + ((mod->fasttx_num % 16) * sizeof(desc));
+       memset(&desc, 0, sizeof(desc));
+       memcpy_fromio(&desc, desc_addr, 1);
+
+       /* convert the Linux CAN frame into ICAN3 format */
+       can_frame_to_ican3(mod, cf, &desc);
+
+       /*
+        * the programming manual says that you must set the IVALID bit, then
+        * interrupt, then set the valid bit. Quite weird, but it seems to be
+        * required for this to work
+        */
+       desc.control |= DESC_IVALID;
+       memcpy_toio(desc_addr, &desc, sizeof(desc));
+
+       /* generate a MODULbus interrupt to the microcontroller */
+       iowrite8(0x01, &mod->dpmctrl->interrupt);
+
+       desc.control ^= DESC_VALID;
+       memcpy_toio(desc_addr, &desc, sizeof(desc));
+
+       /* update the next buffer pointer */
+       mod->fasttx_num = (desc.control & DESC_WRAP) ? 0
+                                                    : (mod->fasttx_num + 1);
+
+       /* update statistics */
+       stats->tx_packets++;
+       stats->tx_bytes += cf->can_dlc;
+       kfree_skb(skb);
+
+       /*
+        * This hardware doesn't have TX-done notifications, so we'll try and
+        * emulate it the best we can using ECHO skbs. Get the next TX
+        * descriptor, and see if we have room to send. If not, stop the queue.
+        * It will be woken when the ECHO skb for the current packet is recv'd.
+        */
+
+       /* copy the control bits of the descriptor */
+       if (!ican3_txok(mod))
+               netif_stop_queue(ndev);
+
+       spin_unlock_irqrestore(&mod->lock, flags);
+       return NETDEV_TX_OK;
+}
+
+static const struct net_device_ops ican3_netdev_ops = {
+       .ndo_open       = ican3_open,
+       .ndo_stop       = ican3_stop,
+       .ndo_start_xmit = ican3_xmit,
+};
+
+/*
+ * Low-level CAN Device
+ */
+
+/* This structure was stolen from drivers/net/can/sja1000/sja1000.c */
+static struct can_bittiming_const ican3_bittiming_const = {
+       .name = DRV_NAME,
+       .tseg1_min = 1,
+       .tseg1_max = 16,
+       .tseg2_min = 1,
+       .tseg2_max = 8,
+       .sjw_max = 4,
+       .brp_min = 1,
+       .brp_max = 64,
+       .brp_inc = 1,
+};
+
+/*
+ * This routine was stolen from drivers/net/can/sja1000/sja1000.c
+ *
+ * The bittiming register command for the ICAN3 just sets the bit timing
+ * registers on the SJA1000 chip directly
+ */
+static int ican3_set_bittiming(struct net_device *ndev)
+{
+       struct ican3_dev *mod = netdev_priv(ndev);
+       struct can_bittiming *bt = &mod->can.bittiming;
+       struct ican3_msg msg;
+       u8 btr0, btr1;
+
+       btr0 = ((bt->brp - 1) & 0x3f) | (((bt->sjw - 1) & 0x3) << 6);
+       btr1 = ((bt->prop_seg + bt->phase_seg1 - 1) & 0xf) |
+               (((bt->phase_seg2 - 1) & 0x7) << 4);
+       if (mod->can.ctrlmode & CAN_CTRLMODE_3_SAMPLES)
+               btr1 |= 0x80;
+
+       memset(&msg, 0, sizeof(msg));
+       msg.spec = MSG_CBTRREQ;
+       msg.len = cpu_to_le16(4);
+       msg.data[0] = 0x00;
+       msg.data[1] = 0x00;
+       msg.data[2] = btr0;
+       msg.data[3] = btr1;
+
+       return ican3_send_msg(mod, &msg);
+}
+
+static int ican3_set_mode(struct net_device *ndev, enum can_mode mode)
+{
+       struct ican3_dev *mod = netdev_priv(ndev);
+       int ret;
+
+       if (mode != CAN_MODE_START)
+               return -ENOTSUPP;
+
+       /* bring the bus online */
+       ret = ican3_set_bus_state(mod, true);
+       if (ret) {
+               dev_err(mod->dev, "unable to set bus-on\n");
+               return ret;
+       }
+
+       /* start up the network device */
+       mod->can.state = CAN_STATE_ERROR_ACTIVE;
+
+       if (netif_queue_stopped(ndev))
+               netif_wake_queue(ndev);
+
+       return 0;
+}
+
+static int ican3_get_berr_counter(const struct net_device *ndev,
+                                 struct can_berr_counter *bec)
+{
+       struct ican3_dev *mod = netdev_priv(ndev);
+       int ret;
+
+       ret = ican3_send_inquiry(mod, INQUIRY_STATUS);
+       if (ret)
+               return ret;
+
+       ret = wait_for_completion_timeout(&mod->buserror_comp, HZ);
+       if (ret <= 0) {
+               dev_info(mod->dev, "%s timed out\n", __func__);
+               return -ETIMEDOUT;
+       }
+
+       bec->rxerr = mod->bec.rxerr;
+       bec->txerr = mod->bec.txerr;
+       return 0;
+}
+
+/*
+ * Sysfs Attributes
+ */
+
+static ssize_t ican3_sysfs_show_term(struct device *dev,
+                                    struct device_attribute *attr,
+                                    char *buf)
+{
+       struct ican3_dev *mod = netdev_priv(to_net_dev(dev));
+       int ret;
+
+       ret = ican3_send_inquiry(mod, INQUIRY_TERMINATION);
+       if (ret)
+               return ret;
+
+       ret = wait_for_completion_timeout(&mod->termination_comp, HZ);
+       if (ret <= 0) {
+               dev_info(mod->dev, "%s timed out\n", __func__);
+               return -ETIMEDOUT;
+       }
+
+       return snprintf(buf, PAGE_SIZE, "%u\n", mod->termination_enabled);
+}
+
+static ssize_t ican3_sysfs_set_term(struct device *dev,
+                                   struct device_attribute *attr,
+                                   const char *buf, size_t count)
+{
+       struct ican3_dev *mod = netdev_priv(to_net_dev(dev));
+       unsigned long enable;
+       int ret;
+
+       if (strict_strtoul(buf, 0, &enable))
+               return -EINVAL;
+
+       ret = ican3_set_termination(mod, enable);
+       if (ret)
+               return ret;
+
+       return count;
+}
+
+static DEVICE_ATTR(termination, S_IWUGO | S_IRUGO, ican3_sysfs_show_term,
+                                                  ican3_sysfs_set_term);
+
+static struct attribute *ican3_sysfs_attrs[] = {
+       &dev_attr_termination.attr,
+       NULL,
+};
+
+static struct attribute_group ican3_sysfs_attr_group = {
+       .attrs = ican3_sysfs_attrs,
+};
+
+/*
+ * PCI Subsystem
+ */
+
+static int __devinit ican3_probe(struct platform_device *pdev)
+{
+       struct janz_platform_data *pdata;
+       struct net_device *ndev;
+       struct ican3_dev *mod;
+       struct resource *res;
+       struct device *dev;
+       int ret;
+
+       pdata = pdev->dev.platform_data;
+       if (!pdata)
+               return -ENXIO;
+
+       dev_dbg(&pdev->dev, "probe: module number %d\n", pdata->modno);
+
+       /* save the struct device for printing */
+       dev = &pdev->dev;
+
+       /* allocate the CAN device and private data */
+       ndev = alloc_candev(sizeof(*mod), 0);
+       if (!ndev) {
+               dev_err(dev, "unable to allocate CANdev\n");
+               ret = -ENOMEM;
+               goto out_return;
+       }
+
+       platform_set_drvdata(pdev, ndev);
+       mod = netdev_priv(ndev);
+       mod->ndev = ndev;
+       mod->dev = &pdev->dev;
+       mod->num = pdata->modno;
+       netif_napi_add(ndev, &mod->napi, ican3_napi, ICAN3_RX_BUFFERS);
+       spin_lock_init(&mod->lock);
+       init_completion(&mod->termination_comp);
+       init_completion(&mod->buserror_comp);
+
+       /* setup device-specific sysfs attributes */
+       ndev->sysfs_groups[0] = &ican3_sysfs_attr_group;
+
+       /* the first unallocated page in the DPM is 9 */
+       mod->free_page = DPM_FREE_START;
+
+       ndev->netdev_ops = &ican3_netdev_ops;
+       ndev->flags |= IFF_ECHO;
+       SET_NETDEV_DEV(ndev, &pdev->dev);
+
+       mod->can.clock.freq = ICAN3_CAN_CLOCK;
+       mod->can.bittiming_const = &ican3_bittiming_const;
+       mod->can.do_set_bittiming = ican3_set_bittiming;
+       mod->can.do_set_mode = ican3_set_mode;
+       mod->can.do_get_berr_counter = ican3_get_berr_counter;
+       mod->can.ctrlmode_supported = CAN_CTRLMODE_3_SAMPLES
+                                   | CAN_CTRLMODE_BERR_REPORTING;
+
+       /* find our IRQ number */
+       mod->irq = platform_get_irq(pdev, 0);
+       if (mod->irq < 0) {
+               dev_err(dev, "IRQ line not found\n");
+               ret = -ENODEV;
+               goto out_free_ndev;
+       }
+
+       ndev->irq = mod->irq;
+
+       /* get access to the MODULbus registers for this module */
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (!res) {
+               dev_err(dev, "MODULbus registers not found\n");
+               ret = -ENODEV;
+               goto out_free_ndev;
+       }
+
+       mod->dpm = ioremap(res->start, resource_size(res));
+       if (!mod->dpm) {
+               dev_err(dev, "MODULbus registers not ioremap\n");
+               ret = -ENOMEM;
+               goto out_free_ndev;
+       }
+
+       mod->dpmctrl = mod->dpm + DPM_PAGE_SIZE;
+
+       /* get access to the control registers for this module */
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+       if (!res) {
+               dev_err(dev, "CONTROL registers not found\n");
+               ret = -ENODEV;
+               goto out_iounmap_dpm;
+       }
+
+       mod->ctrl = ioremap(res->start, resource_size(res));
+       if (!mod->ctrl) {
+               dev_err(dev, "CONTROL registers not ioremap\n");
+               ret = -ENOMEM;
+               goto out_iounmap_dpm;
+       }
+
+       /* disable our IRQ, then hookup the IRQ handler */
+       iowrite8(1 << mod->num, &mod->ctrl->int_disable);
+       ret = request_irq(mod->irq, ican3_irq, IRQF_SHARED, DRV_NAME, mod);
+       if (ret) {
+               dev_err(dev, "unable to request IRQ\n");
+               goto out_iounmap_ctrl;
+       }
+
+       /* reset and initialize the CAN controller into fast mode */
+       napi_enable(&mod->napi);
+       ret = ican3_startup_module(mod);
+       if (ret) {
+               dev_err(dev, "%s: unable to start CANdev\n", __func__);
+               goto out_free_irq;
+       }
+
+       /* register with the Linux CAN layer */
+       ret = register_candev(ndev);
+       if (ret) {
+               dev_err(dev, "%s: unable to register CANdev\n", __func__);
+               goto out_free_irq;
+       }
+
+       dev_info(dev, "module %d: registered CAN device\n", pdata->modno);
+       return 0;
+
+out_free_irq:
+       napi_disable(&mod->napi);
+       iowrite8(1 << mod->num, &mod->ctrl->int_disable);
+       free_irq(mod->irq, mod);
+out_iounmap_ctrl:
+       iounmap(mod->ctrl);
+out_iounmap_dpm:
+       iounmap(mod->dpm);
+out_free_ndev:
+       free_candev(ndev);
+out_return:
+       return ret;
+}
+
+static int __devexit ican3_remove(struct platform_device *pdev)
+{
+       struct net_device *ndev = platform_get_drvdata(pdev);
+       struct ican3_dev *mod = netdev_priv(ndev);
+
+       /* unregister the netdevice, stop interrupts */
+       unregister_netdev(ndev);
+       napi_disable(&mod->napi);
+       iowrite8(1 << mod->num, &mod->ctrl->int_disable);
+       free_irq(mod->irq, mod);
+
+       /* put the module into reset */
+       ican3_shutdown_module(mod);
+
+       /* unmap all registers */
+       iounmap(mod->ctrl);
+       iounmap(mod->dpm);
+
+       free_candev(ndev);
+
+       return 0;
+}
+
+static struct platform_driver ican3_driver = {
+       .driver         = {
+               .name   = DRV_NAME,
+               .owner  = THIS_MODULE,
+       },
+       .probe          = ican3_probe,
+       .remove         = __devexit_p(ican3_remove),
+};
+
+static int __init ican3_init(void)
+{
+       return platform_driver_register(&ican3_driver);
+}
+
+static void __exit ican3_exit(void)
+{
+       platform_driver_unregister(&ican3_driver);
+}
+
+MODULE_AUTHOR("Ira W. Snyder <iws@ovro.caltech.edu>");
+MODULE_DESCRIPTION("Janz MODULbus VMOD-ICAN3 Driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:janz-ican3");
+
+module_init(ican3_init);
+module_exit(ican3_exit);
index be90d35..fe92566 100644 (file)
@@ -3367,13 +3367,9 @@ static int cnic_cm_shutdown(struct cnic_dev *dev)
 
 static void cnic_init_context(struct cnic_dev *dev, u32 cid)
 {
-       struct cnic_local *cp = dev->cnic_priv;
        u32 cid_addr;
        int i;
 
-       if (CHIP_NUM(cp) == CHIP_NUM_5709)
-               return;
-
        cid_addr = GET_CID_ADDR(cid);
 
        for (i = 0; i < CTX_SIZE; i += 4)
@@ -3530,14 +3526,11 @@ static void cnic_init_bnx2_tx_ring(struct cnic_dev *dev)
 
        sb_id = cp->status_blk_num;
        tx_cid = 20;
-       cnic_init_context(dev, tx_cid);
-       cnic_init_context(dev, tx_cid + 1);
        cp->tx_cons_ptr = &s_blk->status_tx_quick_consumer_index2;
        if (ethdev->drv_state & CNIC_DRV_STATE_USING_MSIX) {
                struct status_block_msix *sblk = cp->status_blk.bnx2;
 
                tx_cid = TX_TSS_CID + sb_id - 1;
-               cnic_init_context(dev, tx_cid);
                CNIC_WR(dev, BNX2_TSCH_TSS_CFG, (sb_id << 24) |
                        (TX_TSS_CID << 7));
                cp->tx_cons_ptr = &sblk->status_tx_quick_consumer_index;
@@ -3556,6 +3549,9 @@ static void cnic_init_bnx2_tx_ring(struct cnic_dev *dev)
                offset2 = BNX2_L2CTX_TBDR_BHADDR_HI_XI;
                offset3 = BNX2_L2CTX_TBDR_BHADDR_LO_XI;
        } else {
+               cnic_init_context(dev, tx_cid);
+               cnic_init_context(dev, tx_cid + 1);
+
                offset0 = BNX2_L2CTX_TYPE;
                offset1 = BNX2_L2CTX_CMD_TYPE;
                offset2 = BNX2_L2CTX_TBDR_BHADDR_HI;
index 110c620..0c55177 100644 (file)
@@ -12,8 +12,8 @@
 #ifndef CNIC_IF_H
 #define CNIC_IF_H
 
-#define CNIC_MODULE_VERSION    "2.1.1"
-#define CNIC_MODULE_RELDATE    "Feb 22, 2010"
+#define CNIC_MODULE_VERSION    "2.1.2"
+#define CNIC_MODULE_RELDATE    "May 26, 2010"
 
 #define CNIC_ULP_RDMA          0
 #define CNIC_ULP_ISCSI         1
index 326465f..ddf7a86 100644 (file)
@@ -681,6 +681,8 @@ static int fec_enet_mii_probe(struct net_device *dev)
        struct phy_device *phy_dev = NULL;
        int phy_addr;
 
+       fep->phy_dev = NULL;
+
        /* find the first phy */
        for (phy_addr = 0; phy_addr < PHY_MAX_ADDR; phy_addr++) {
                if (fep->mii_bus->phy_map[phy_addr]) {
@@ -711,6 +713,11 @@ static int fec_enet_mii_probe(struct net_device *dev)
        fep->link = 0;
        fep->full_duplex = 0;
 
+       printk(KERN_INFO "%s: Freescale FEC PHY driver [%s] "
+               "(mii_bus:phy_addr=%s, irq=%d)\n", dev->name,
+               fep->phy_dev->drv->name, dev_name(&fep->phy_dev->dev),
+               fep->phy_dev->irq);
+
        return 0;
 }
 
@@ -756,13 +763,8 @@ static int fec_enet_mii_init(struct platform_device *pdev)
        if (mdiobus_register(fep->mii_bus))
                goto err_out_free_mdio_irq;
 
-       if (fec_enet_mii_probe(dev) != 0)
-               goto err_out_unregister_bus;
-
        return 0;
 
-err_out_unregister_bus:
-       mdiobus_unregister(fep->mii_bus);
 err_out_free_mdio_irq:
        kfree(fep->mii_bus->irq);
 err_out_free_mdiobus:
@@ -915,7 +917,12 @@ fec_enet_open(struct net_device *dev)
        if (ret)
                return ret;
 
-       /* schedule a link state check */
+       /* Probe and connect to PHY when open the interface */
+       ret = fec_enet_mii_probe(dev);
+       if (ret) {
+               fec_enet_free_buffers(dev);
+               return ret;
+       }
        phy_start(fep->phy_dev);
        netif_start_queue(dev);
        fep->opened = 1;
@@ -929,10 +936,12 @@ fec_enet_close(struct net_device *dev)
 
        /* Don't know what to do yet. */
        fep->opened = 0;
-       phy_stop(fep->phy_dev);
        netif_stop_queue(dev);
        fec_stop(dev);
 
+       if (fep->phy_dev)
+               phy_disconnect(fep->phy_dev);
+
         fec_enet_free_buffers(dev);
 
        return 0;
@@ -1316,11 +1325,6 @@ fec_probe(struct platform_device *pdev)
        if (ret)
                goto failed_register;
 
-       printk(KERN_INFO "%s: Freescale FEC PHY driver [%s] "
-               "(mii_bus:phy_addr=%s, irq=%d)\n", ndev->name,
-               fep->phy_dev->drv->name, dev_name(&fep->phy_dev->dev),
-               fep->phy_dev->irq);
-
        return 0;
 
 failed_register:
index 694132e..4e7d1d0 100644 (file)
@@ -1151,8 +1151,7 @@ static int __init yam_init_driver(void)
                dev = alloc_netdev(sizeof(struct yam_port), name,
                                   yam_setup);
                if (!dev) {
-                       printk(KERN_ERR "yam: cannot allocate net device %s\n",
-                              dev->name);
+                       pr_err("yam: cannot allocate net device\n");
                        err = -ENOMEM;
                        goto error;
                }
index c033584..522abe2 100644 (file)
@@ -295,6 +295,10 @@ This option defaults to enabled (set) */
 
 #define MULTICAST_CAM_TABLE_NUM 4
 
+/* TEMAC Synthesis features */
+#define TEMAC_FEATURE_RX_CSUM  (1 << 0)
+#define TEMAC_FEATURE_TX_CSUM  (1 << 1)
+
 /* TX/RX CURDESC_PTR points to first descriptor */
 /* TX/RX TAILDESC_PTR points to last descriptor in linked list */
 
@@ -353,6 +357,7 @@ struct temac_local {
        struct mutex indirect_mutex;
        u32 options;                    /* Current options word */
        int last_link;
+       unsigned int temac_features;
 
        /* Buffer descriptors */
        struct cdmac_bd *tx_bd_v;
index fa7620e..52dcc84 100644 (file)
@@ -245,7 +245,7 @@ static int temac_dma_bd_init(struct net_device *ndev)
                                          CHNL_CTRL_IRQ_COAL_EN);
        /* 0x10220483 */
        /* 0x00100483 */
-       lp->dma_out(lp, RX_CHNL_CTRL, 0xff010000 |
+       lp->dma_out(lp, RX_CHNL_CTRL, 0xff070000 |
                                          CHNL_CTRL_IRQ_EN |
                                          CHNL_CTRL_IRQ_DLY_EN |
                                          CHNL_CTRL_IRQ_COAL_EN |
@@ -574,6 +574,10 @@ static void temac_start_xmit_done(struct net_device *ndev)
                if (cur_p->app4)
                        dev_kfree_skb_irq((struct sk_buff *)cur_p->app4);
                cur_p->app0 = 0;
+               cur_p->app1 = 0;
+               cur_p->app2 = 0;
+               cur_p->app3 = 0;
+               cur_p->app4 = 0;
 
                ndev->stats.tx_packets++;
                ndev->stats.tx_bytes += cur_p->len;
@@ -589,6 +593,29 @@ static void temac_start_xmit_done(struct net_device *ndev)
        netif_wake_queue(ndev);
 }
 
+static inline int temac_check_tx_bd_space(struct temac_local *lp, int num_frag)
+{
+       struct cdmac_bd *cur_p;
+       int tail;
+
+       tail = lp->tx_bd_tail;
+       cur_p = &lp->tx_bd_v[tail];
+
+       do {
+               if (cur_p->app0)
+                       return NETDEV_TX_BUSY;
+
+               tail++;
+               if (tail >= TX_BD_NUM)
+                       tail = 0;
+
+               cur_p = &lp->tx_bd_v[tail];
+               num_frag--;
+       } while (num_frag >= 0);
+
+       return 0;
+}
+
 static int temac_start_xmit(struct sk_buff *skb, struct net_device *ndev)
 {
        struct temac_local *lp = netdev_priv(ndev);
@@ -603,7 +630,7 @@ static int temac_start_xmit(struct sk_buff *skb, struct net_device *ndev)
        start_p = lp->tx_bd_p + sizeof(*lp->tx_bd_v) * lp->tx_bd_tail;
        cur_p = &lp->tx_bd_v[lp->tx_bd_tail];
 
-       if (cur_p->app0 & STS_CTRL_APP0_CMPLT) {
+       if (temac_check_tx_bd_space(lp, num_frag)) {
                if (!netif_queue_stopped(ndev)) {
                        netif_stop_queue(ndev);
                        return NETDEV_TX_BUSY;
@@ -613,29 +640,14 @@ static int temac_start_xmit(struct sk_buff *skb, struct net_device *ndev)
 
        cur_p->app0 = 0;
        if (skb->ip_summed == CHECKSUM_PARTIAL) {
-               const struct iphdr *ip = ip_hdr(skb);
-               int length = 0, start = 0, insert = 0;
-
-               switch (ip->protocol) {
-               case IPPROTO_TCP:
-                       start = sizeof(struct iphdr) + ETH_HLEN;
-                       insert = sizeof(struct iphdr) + ETH_HLEN + 16;
-                       length = ip->tot_len - sizeof(struct iphdr);
-                       break;
-               case IPPROTO_UDP:
-                       start = sizeof(struct iphdr) + ETH_HLEN;
-                       insert = sizeof(struct iphdr) + ETH_HLEN + 6;
-                       length = ip->tot_len - sizeof(struct iphdr);
-                       break;
-               default:
-                       break;
-               }
-               cur_p->app1 = ((start << 16) | insert);
-               cur_p->app2 = csum_tcpudp_magic(ip->saddr, ip->daddr,
-                                               length, ip->protocol, 0);
-               skb->data[insert] = 0;
-               skb->data[insert + 1] = 0;
+               unsigned int csum_start_off = skb_transport_offset(skb);
+               unsigned int csum_index_off = csum_start_off + skb->csum_offset;
+
+               cur_p->app0 |= 1; /* TX Checksum Enabled */
+               cur_p->app1 = (csum_start_off << 16) | csum_index_off;
+               cur_p->app2 = 0;  /* initial checksum seed */
        }
+
        cur_p->app0 |= STS_CTRL_APP0_SOP;
        cur_p->len = skb_headlen(skb);
        cur_p->phys = dma_map_single(ndev->dev.parent, skb->data, skb->len,
@@ -699,6 +711,15 @@ static void ll_temac_recv(struct net_device *ndev)
                skb->protocol = eth_type_trans(skb, ndev);
                skb->ip_summed = CHECKSUM_NONE;
 
+               /* if we're doing rx csum offload, set it up */
+               if (((lp->temac_features & TEMAC_FEATURE_RX_CSUM) != 0) &&
+                       (skb->protocol == __constant_htons(ETH_P_IP)) &&
+                       (skb->len > 64)) {
+
+                       skb->csum = cur_p->app3 & 0xFFFF;
+                       skb->ip_summed = CHECKSUM_COMPLETE;
+               }
+
                netif_rx(skb);
 
                ndev->stats.rx_packets++;
@@ -883,6 +904,7 @@ temac_of_probe(struct of_device *op, const struct of_device_id *match)
        struct temac_local *lp;
        struct net_device *ndev;
        const void *addr;
+       __be32 *p;
        int size, rc = 0;
 
        /* Init network device structure */
@@ -926,6 +948,18 @@ temac_of_probe(struct of_device *op, const struct of_device_id *match)
                goto nodev;
        }
 
+       /* Setup checksum offload, but default to off if not specified */
+       lp->temac_features = 0;
+       p = (__be32 *)of_get_property(op->dev.of_node, "xlnx,txcsum", NULL);
+       if (p && be32_to_cpu(*p)) {
+               lp->temac_features |= TEMAC_FEATURE_TX_CSUM;
+               /* Can checksum TCP/UDP over IPv4. */
+               ndev->features |= NETIF_F_IP_CSUM;
+       }
+       p = (__be32 *)of_get_property(op->dev.of_node, "xlnx,rxcsum", NULL);
+       if (p && be32_to_cpu(*p))
+               lp->temac_features |= TEMAC_FEATURE_RX_CSUM;
+
        /* Find the DMA node, map the DMA registers, and decode the DMA IRQs */
        np = of_parse_phandle(op->dev.of_node, "llink-connected", 0);
        if (!np) {
@@ -950,7 +984,7 @@ temac_of_probe(struct of_device *op, const struct of_device_id *match)
 
        lp->rx_irq = irq_of_parse_and_map(np, 0);
        lp->tx_irq = irq_of_parse_and_map(np, 1);
-       if (!lp->rx_irq || !lp->tx_irq) {
+       if ((lp->rx_irq == NO_IRQ) || (lp->tx_irq == NO_IRQ)) {
                dev_err(&op->dev, "could not determine irqs\n");
                rc = -ENOMEM;
                goto nodev;
index 7aaae2d..80c11d1 100644 (file)
@@ -130,4 +130,21 @@ static inline int aer_osc_setup(struct pcie_device *pciedev)
 }
 #endif
 
+#ifdef CONFIG_ACPI_APEI
+extern int pcie_aer_get_firmware_first(struct pci_dev *pci_dev);
+#else
+static inline int pcie_aer_get_firmware_first(struct pci_dev *pci_dev)
+{
+       if (pci_dev->__aer_firmware_first_valid)
+               return pci_dev->__aer_firmware_first;
+       return 0;
+}
+#endif
+
+static inline void pcie_aer_force_firmware_first(struct pci_dev *pci_dev,
+                                                int enable)
+{
+       pci_dev->__aer_firmware_first = !!enable;
+       pci_dev->__aer_firmware_first_valid = 1;
+}
 #endif /* _AERDRV_H_ */
index 0481408..f278d7b 100644 (file)
@@ -16,6 +16,7 @@
 #include <linux/acpi.h>
 #include <linux/pci-acpi.h>
 #include <linux/delay.h>
+#include <acpi/apei.h>
 #include "aerdrv.h"
 
 /**
@@ -53,3 +54,79 @@ int aer_osc_setup(struct pcie_device *pciedev)
 
        return 0;
 }
+
+#ifdef CONFIG_ACPI_APEI
+static inline int hest_match_pci(struct acpi_hest_aer_common *p,
+                                struct pci_dev *pci)
+{
+       return  (0           == pci_domain_nr(pci->bus) &&
+                p->bus      == pci->bus->number &&
+                p->device   == PCI_SLOT(pci->devfn) &&
+                p->function == PCI_FUNC(pci->devfn));
+}
+
+struct aer_hest_parse_info {
+       struct pci_dev *pci_dev;
+       int firmware_first;
+};
+
+static int aer_hest_parse(struct acpi_hest_header *hest_hdr, void *data)
+{
+       struct aer_hest_parse_info *info = data;
+       struct acpi_hest_aer_common *p;
+       u8 pcie_type = 0;
+       u8 bridge = 0;
+       int ff = 0;
+
+       switch (hest_hdr->type) {
+       case ACPI_HEST_TYPE_AER_ROOT_PORT:
+               pcie_type = PCI_EXP_TYPE_ROOT_PORT;
+               break;
+       case ACPI_HEST_TYPE_AER_ENDPOINT:
+               pcie_type = PCI_EXP_TYPE_ENDPOINT;
+               break;
+       case ACPI_HEST_TYPE_AER_BRIDGE:
+               if ((info->pci_dev->class >> 16) == PCI_BASE_CLASS_BRIDGE)
+                       bridge = 1;
+               break;
+       default:
+               return 0;
+       }
+
+       p = (struct acpi_hest_aer_common *)(hest_hdr + 1);
+       if (p->flags & ACPI_HEST_GLOBAL) {
+               if ((info->pci_dev->is_pcie &&
+                    info->pci_dev->pcie_type == pcie_type) || bridge)
+                       ff = !!(p->flags & ACPI_HEST_FIRMWARE_FIRST);
+       } else
+               if (hest_match_pci(p, info->pci_dev))
+                       ff = !!(p->flags & ACPI_HEST_FIRMWARE_FIRST);
+       info->firmware_first = ff;
+
+       return 0;
+}
+
+static void aer_set_firmware_first(struct pci_dev *pci_dev)
+{
+       int rc;
+       struct aer_hest_parse_info info = {
+               .pci_dev        = pci_dev,
+               .firmware_first = 0,
+       };
+
+       rc = apei_hest_parse(aer_hest_parse, &info);
+
+       if (rc)
+               pci_dev->__aer_firmware_first = 0;
+       else
+               pci_dev->__aer_firmware_first = info.firmware_first;
+       pci_dev->__aer_firmware_first_valid = 1;
+}
+
+int pcie_aer_get_firmware_first(struct pci_dev *dev)
+{
+       if (!dev->__aer_firmware_first_valid)
+               aer_set_firmware_first(dev);
+       return dev->__aer_firmware_first;
+}
+#endif
index df2d686..8af4f61 100644 (file)
@@ -36,7 +36,7 @@ int pci_enable_pcie_error_reporting(struct pci_dev *dev)
        u16 reg16 = 0;
        int pos;
 
-       if (dev->aer_firmware_first)
+       if (pcie_aer_get_firmware_first(dev))
                return -EIO;
 
        pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR);
@@ -63,7 +63,7 @@ int pci_disable_pcie_error_reporting(struct pci_dev *dev)
        u16 reg16 = 0;
        int pos;
 
-       if (dev->aer_firmware_first)
+       if (pcie_aer_get_firmware_first(dev))
                return -EIO;
 
        pos = pci_pcie_cap(dev);
@@ -771,7 +771,7 @@ void aer_isr(struct work_struct *work)
  */
 int aer_init(struct pcie_device *dev)
 {
-       if (dev->port->aer_firmware_first) {
+       if (pcie_aer_get_firmware_first(dev->port)) {
                dev_printk(KERN_DEBUG, &dev->device,
                           "PCIe errors handled by platform firmware.\n");
                goto out;
@@ -785,7 +785,7 @@ out:
        if (forceload) {
                dev_printk(KERN_DEBUG, &dev->device,
                           "aerdrv forceload requested.\n");
-               dev->port->aer_firmware_first = 0;
+               pcie_aer_force_firmware_first(dev->port, 0);
                return 0;
        }
        return -ENXIO;
index c82548a..f4adba2 100644 (file)
@@ -10,7 +10,6 @@
 #include <linux/module.h>
 #include <linux/cpumask.h>
 #include <linux/pci-aspm.h>
-#include <acpi/acpi_hest.h>
 #include "pci.h"
 
 #define CARDBUS_LATENCY_TIMER  176     /* secondary latency timer */
@@ -904,12 +903,6 @@ void set_pcie_hotplug_bridge(struct pci_dev *pdev)
                pdev->is_hotplug_bridge = 1;
 }
 
-static void set_pci_aer_firmware_first(struct pci_dev *pdev)
-{
-       if (acpi_hest_firmware_first_pci(pdev))
-               pdev->aer_firmware_first = 1;
-}
-
 #define LEGACY_IO_RESOURCE     (IORESOURCE_IO | IORESOURCE_PCI_FIXED)
 
 /**
@@ -939,7 +932,6 @@ int pci_setup_device(struct pci_dev *dev)
        dev->multifunction = !!(hdr_type & 0x80);
        dev->error_state = pci_channel_io_normal;
        set_pcie_port_type(dev);
-       set_pci_aer_firmware_first(dev);
 
        list_for_each_entry(slot, &dev->bus->slots, list)
                if (PCI_SLOT(dev->devfn) == slot->number)
index 5664321..8070e07 100644 (file)
@@ -1124,7 +1124,7 @@ static void rio_update_route_tables(struct rio_mport *port)
 
 /**
  * rio_init_em - Initializes RIO Error Management (for switches)
- * @port: Master port associated with the RIO network
+ * @rdev: RIO device
  *
  * For each enumerated switch, call device-specific error management
  * initialization routine (if supplied by the switch driver).
index 777e099..08fa453 100644 (file)
@@ -338,7 +338,7 @@ int rio_release_outb_dbell(struct rio_dev *rdev, struct resource *res)
 
 /**
  * rio_request_inb_pwrite - request inbound port-write message service
- * @mport: RIO device to which register inbound port-write callback routine
+ * @rdev: RIO device to which register inbound port-write callback routine
  * @pwcback: Callback routine to execute when port-write is received
  *
  * Binds a port-write callback function to the RapidIO device.
@@ -385,7 +385,10 @@ EXPORT_SYMBOL_GPL(rio_release_inb_pwrite);
 /**
  * rio_mport_get_physefb - Helper function that returns register offset
  *                      for Physical Layer Extended Features Block.
- * @rdev: RIO device
+ * @port: Master port to issue transaction
+ * @local: Indicate a local master port or remote device access
+ * @destid: Destination ID of the device
+ * @hopcount: Number of switch hops to the device
  */
 u32
 rio_mport_get_physefb(struct rio_mport *port, int local,
@@ -430,7 +433,7 @@ rio_mport_get_physefb(struct rio_mport *port, int local,
 
 /**
  * rio_get_comptag - Begin or continue searching for a RIO device by component tag
- * @comp_tag: RIO component tad to match
+ * @comp_tag: RIO component tag to match
  * @from: Previous RIO device found in search, or %NULL for new search
  *
  * Iterates through the list of known RIO devices. If a RIO device is
@@ -835,7 +838,6 @@ int rio_std_route_get_entry(struct rio_mport *mport, u16 destid, u8 hopcount,
  * rio_std_route_clr_table - Clear swotch route table using standard registers
  *   defined in RIO specification rev.1.3.
  * @mport: Master port to issue transaction
- * @local: Indicate a local master port or remote device access
  * @destid: Destination ID of the device
  * @hopcount: Number of switch hops to the device
  * @table: routing table ID (global or port-specific)
index 1afd008..7b14a67 100644 (file)
@@ -16,7 +16,7 @@
 #include <linux/delay.h>
 #include <linux/platform_device.h>
 #include <linux/regulator/driver.h>
-#include <linux/mfd/ab3100.h>
+#include <linux/mfd/abx500.h>
 
 /* LDO registers and some handy masking definitions for AB3100 */
 #define AB3100_LDO_A           0x40
@@ -41,7 +41,7 @@
  * struct ab3100_regulator
  * A struct passed around the individual regulator functions
  * @platform_device: platform device holding this regulator
- * @ab3100: handle to the AB3100 parent chip
+ * @dev: handle to the device
  * @plfdata: AB3100 platform data passed in at probe time
  * @regreg: regulator register number in the AB3100
  * @fixed_voltage: a fixed voltage for this regulator, if this
@@ -52,7 +52,7 @@
  */
 struct ab3100_regulator {
        struct regulator_dev *rdev;
-       struct ab3100 *ab3100;
+       struct device *dev;
        struct ab3100_platform_data *plfdata;
        u8 regreg;
        int fixed_voltage;
@@ -183,7 +183,7 @@ static int ab3100_enable_regulator(struct regulator_dev *reg)
        int err;
        u8 regval;
 
-       err = ab3100_get_register_interruptible(abreg->ab3100, abreg->regreg,
+       err = abx500_get_register_interruptible(abreg->dev, 0, abreg->regreg,
                                                &regval);
        if (err) {
                dev_warn(&reg->dev, "failed to get regid %d value\n",
@@ -197,7 +197,7 @@ static int ab3100_enable_regulator(struct regulator_dev *reg)
 
        regval |= AB3100_REG_ON_MASK;
 
-       err = ab3100_set_register_interruptible(abreg->ab3100, abreg->regreg,
+       err = abx500_set_register_interruptible(abreg->dev, 0, abreg->regreg,
                                                regval);
        if (err) {
                dev_warn(&reg->dev, "failed to set regid %d value\n",
@@ -245,14 +245,14 @@ static int ab3100_disable_regulator(struct regulator_dev *reg)
        if (abreg->regreg == AB3100_LDO_D) {
                dev_info(&reg->dev, "disabling LDO D - shut down system\n");
                /* Setting LDO D to 0x00 cuts the power to the SoC */
-               return ab3100_set_register_interruptible(abreg->ab3100,
+               return abx500_set_register_interruptible(abreg->dev, 0,
                                                         AB3100_LDO_D, 0x00U);
        }
 
        /*
         * All other regulators are handled here
         */
-       err = ab3100_get_register_interruptible(abreg->ab3100, abreg->regreg,
+       err = abx500_get_register_interruptible(abreg->dev, 0, abreg->regreg,
                                                &regval);
        if (err) {
                dev_err(&reg->dev, "unable to get register 0x%x\n",
@@ -260,7 +260,7 @@ static int ab3100_disable_regulator(struct regulator_dev *reg)
                return err;
        }
        regval &= ~AB3100_REG_ON_MASK;
-       return ab3100_set_register_interruptible(abreg->ab3100, abreg->regreg,
+       return abx500_set_register_interruptible(abreg->dev, 0, abreg->regreg,
                                                 regval);
 }
 
@@ -270,7 +270,7 @@ static int ab3100_is_enabled_regulator(struct regulator_dev *reg)
        u8 regval;
        int err;
 
-       err = ab3100_get_register_interruptible(abreg->ab3100, abreg->regreg,
+       err = abx500_get_register_interruptible(abreg->dev, 0, abreg->regreg,
                                                &regval);
        if (err) {
                dev_err(&reg->dev, "unable to get register 0x%x\n",
@@ -305,7 +305,7 @@ static int ab3100_get_voltage_regulator(struct regulator_dev *reg)
         * For variable types, read out setting and index into
         * supplied voltage list.
         */
-       err = ab3100_get_register_interruptible(abreg->ab3100,
+       err = abx500_get_register_interruptible(abreg->dev, 0,
                                                abreg->regreg, &regval);
        if (err) {
                dev_warn(&reg->dev,
@@ -373,7 +373,7 @@ static int ab3100_set_voltage_regulator(struct regulator_dev *reg,
        if (bestindex < 0)
                return bestindex;
 
-       err = ab3100_get_register_interruptible(abreg->ab3100,
+       err = abx500_get_register_interruptible(abreg->dev, 0,
                                                abreg->regreg, &regval);
        if (err) {
                dev_warn(&reg->dev,
@@ -386,7 +386,7 @@ static int ab3100_set_voltage_regulator(struct regulator_dev *reg,
        regval &= ~0xE0;
        regval |= (bestindex << 5);
 
-       err = ab3100_set_register_interruptible(abreg->ab3100,
+       err = abx500_set_register_interruptible(abreg->dev, 0,
                                                abreg->regreg, regval);
        if (err)
                dev_warn(&reg->dev, "failed to set regulator register %02x\n",
@@ -414,7 +414,7 @@ static int ab3100_set_suspend_voltage_regulator(struct regulator_dev *reg,
        /* LDO E and BUCK have special suspend voltages you can set */
        bestindex = ab3100_get_best_voltage_index(reg, uV, uV);
 
-       err = ab3100_get_register_interruptible(abreg->ab3100,
+       err = abx500_get_register_interruptible(abreg->dev, 0,
                                                targetreg, &regval);
        if (err) {
                dev_warn(&reg->dev,
@@ -427,7 +427,7 @@ static int ab3100_set_suspend_voltage_regulator(struct regulator_dev *reg,
        regval &= ~0xE0;
        regval |= (bestindex << 5);
 
-       err = ab3100_set_register_interruptible(abreg->ab3100,
+       err = abx500_set_register_interruptible(abreg->dev, 0,
                                                targetreg, regval);
        if (err)
                dev_warn(&reg->dev, "failed to set regulator register %02x\n",
@@ -574,13 +574,12 @@ ab3100_regulator_desc[AB3100_NUM_REGULATORS] = {
 static int __devinit ab3100_regulators_probe(struct platform_device *pdev)
 {
        struct ab3100_platform_data *plfdata = pdev->dev.platform_data;
-       struct ab3100 *ab3100 = platform_get_drvdata(pdev);
        int err = 0;
        u8 data;
        int i;
 
        /* Check chip state */
-       err = ab3100_get_register_interruptible(ab3100,
+       err = abx500_get_register_interruptible(&pdev->dev, 0,
                                                AB3100_LDO_D, &data);
        if (err) {
                dev_err(&pdev->dev, "could not read initial status of LDO_D\n");
@@ -595,7 +594,7 @@ static int __devinit ab3100_regulators_probe(struct platform_device *pdev)
 
        /* Set up regulators */
        for (i = 0; i < ARRAY_SIZE(ab3100_reg_init_order); i++) {
-               err = ab3100_set_register_interruptible(ab3100,
+               err = abx500_set_register_interruptible(&pdev->dev, 0,
                                        ab3100_reg_init_order[i],
                                        plfdata->reg_initvals[i]);
                if (err) {
@@ -617,7 +616,7 @@ static int __devinit ab3100_regulators_probe(struct platform_device *pdev)
                 * see what it looks like for a certain machine, go
                 * into the machine I2C setup.
                 */
-               reg->ab3100 = ab3100;
+               reg->dev = &pdev->dev;
                reg->plfdata = plfdata;
 
                /*
index 74841ab..14b4576 100644 (file)
 #include <linux/platform_device.h>
 #include <linux/regulator/driver.h>
 #include <linux/regulator/machine.h>
-#include <linux/i2c.h>
 #include <linux/delay.h>
 #include <linux/slab.h>
-
-/* Register definitions */
-#define        TPS6507X_REG_PPATH1                             0X01
-#define        TPS6507X_REG_INT                                0X02
-#define        TPS6507X_REG_CHGCONFIG0                         0X03
-#define        TPS6507X_REG_CHGCONFIG1                         0X04
-#define        TPS6507X_REG_CHGCONFIG2                         0X05
-#define        TPS6507X_REG_CHGCONFIG3                         0X06
-#define        TPS6507X_REG_REG_ADCONFIG                       0X07
-#define        TPS6507X_REG_TSCMODE                            0X08
-#define        TPS6507X_REG_ADRESULT_1                         0X09
-#define        TPS6507X_REG_ADRESULT_2                         0X0A
-#define        TPS6507X_REG_PGOOD                              0X0B
-#define        TPS6507X_REG_PGOODMASK                          0X0C
-#define        TPS6507X_REG_CON_CTRL1                          0X0D
-#define        TPS6507X_REG_CON_CTRL2                          0X0E
-#define        TPS6507X_REG_CON_CTRL3                          0X0F
-#define        TPS6507X_REG_DEFDCDC1                           0X10
-#define        TPS6507X_REG_DEFDCDC2_LOW                       0X11
-#define        TPS6507X_REG_DEFDCDC2_HIGH                      0X12
-#define        TPS6507X_REG_DEFDCDC3_LOW                       0X13
-#define        TPS6507X_REG_DEFDCDC3_HIGH                      0X14
-#define        TPS6507X_REG_DEFSLEW                            0X15
-#define        TPS6507X_REG_LDO_CTRL1                          0X16
-#define        TPS6507X_REG_DEFLDO2                            0X17
-#define        TPS6507X_REG_WLED_CTRL1                         0X18
-#define        TPS6507X_REG_WLED_CTRL2                         0X19
-
-/* CON_CTRL1 bitfields */
-#define        TPS6507X_CON_CTRL1_DCDC1_ENABLE         BIT(4)
-#define        TPS6507X_CON_CTRL1_DCDC2_ENABLE         BIT(3)
-#define        TPS6507X_CON_CTRL1_DCDC3_ENABLE         BIT(2)
-#define        TPS6507X_CON_CTRL1_LDO1_ENABLE          BIT(1)
-#define        TPS6507X_CON_CTRL1_LDO2_ENABLE          BIT(0)
-
-/* DEFDCDC1 bitfields */
-#define TPS6507X_DEFDCDC1_DCDC1_EXT_ADJ_EN     BIT(7)
-#define TPS6507X_DEFDCDC1_DCDC1_MASK           0X3F
-
-/* DEFDCDC2_LOW bitfields */
-#define TPS6507X_DEFDCDC2_LOW_DCDC2_MASK       0X3F
-
-/* DEFDCDC2_HIGH bitfields */
-#define TPS6507X_DEFDCDC2_HIGH_DCDC2_MASK      0X3F
-
-/* DEFDCDC3_LOW bitfields */
-#define TPS6507X_DEFDCDC3_LOW_DCDC3_MASK       0X3F
-
-/* DEFDCDC3_HIGH bitfields */
-#define TPS6507X_DEFDCDC3_HIGH_DCDC3_MASK      0X3F
-
-/* TPS6507X_REG_LDO_CTRL1 bitfields */
-#define TPS6507X_REG_LDO_CTRL1_LDO1_MASK       0X0F
-
-/* TPS6507X_REG_DEFLDO2 bitfields */
-#define TPS6507X_REG_DEFLDO2_LDO2_MASK         0X3F
-
-/* VDCDC MASK */
-#define TPS6507X_DEFDCDCX_DCDC_MASK            0X3F
+#include <linux/mfd/tps6507x.h>
 
 /* DCDC's */
 #define TPS6507X_DCDC_1                                0
@@ -162,101 +103,146 @@ struct tps_info {
        const u16 *table;
 };
 
-struct tps_pmic {
+static const struct tps_info tps6507x_pmic_regs[] = {
+       {
+               .name = "VDCDC1",
+               .min_uV = 725000,
+               .max_uV = 3300000,
+               .table_len = ARRAY_SIZE(VDCDCx_VSEL_table),
+               .table = VDCDCx_VSEL_table,
+       },
+       {
+               .name = "VDCDC2",
+               .min_uV = 725000,
+               .max_uV = 3300000,
+               .table_len = ARRAY_SIZE(VDCDCx_VSEL_table),
+               .table = VDCDCx_VSEL_table,
+       },
+       {
+               .name = "VDCDC3",
+               .min_uV = 725000,
+               .max_uV = 3300000,
+               .table_len = ARRAY_SIZE(VDCDCx_VSEL_table),
+               .table = VDCDCx_VSEL_table,
+       },
+       {
+               .name = "LDO1",
+               .min_uV = 1000000,
+               .max_uV = 3300000,
+               .table_len = ARRAY_SIZE(LDO1_VSEL_table),
+               .table = LDO1_VSEL_table,
+       },
+       {
+               .name = "LDO2",
+               .min_uV = 725000,
+               .max_uV = 3300000,
+               .table_len = ARRAY_SIZE(LDO2_VSEL_table),
+               .table = LDO2_VSEL_table,
+       },
+};
+
+struct tps6507x_pmic {
        struct regulator_desc desc[TPS6507X_NUM_REGULATOR];
-       struct i2c_client *client;
+       struct tps6507x_dev *mfd;
        struct regulator_dev *rdev[TPS6507X_NUM_REGULATOR];
        const struct tps_info *info[TPS6507X_NUM_REGULATOR];
        struct mutex io_lock;
 };
-
-static inline int tps_6507x_read(struct tps_pmic *tps, u8 reg)
+static inline int tps6507x_pmic_read(struct tps6507x_pmic *tps, u8 reg)
 {
-       return i2c_smbus_read_byte_data(tps->client, reg);
+       u8 val;
+       int err;
+
+       err = tps->mfd->read_dev(tps->mfd, reg, 1, &val);
+
+       if (err)
+               return err;
+
+       return val;
 }
 
-static inline int tps_6507x_write(struct tps_pmic *tps, u8 reg, u8 val)
+static inline int tps6507x_pmic_write(struct tps6507x_pmic *tps, u8 reg, u8 val)
 {
-       return i2c_smbus_write_byte_data(tps->client, reg, val);
+       return tps->mfd->write_dev(tps->mfd, reg, 1, &val);
 }
 
-static int tps_6507x_set_bits(struct tps_pmic *tps, u8 reg, u8 mask)
+static int tps6507x_pmic_set_bits(struct tps6507x_pmic *tps, u8 reg, u8 mask)
 {
        int err, data;
 
        mutex_lock(&tps->io_lock);
 
-       data = tps_6507x_read(tps, reg);
+       data = tps6507x_pmic_read(tps, reg);
        if (data < 0) {
-               dev_err(&tps->client->dev, "Read from reg 0x%x failed\n", reg);
+               dev_err(tps->mfd->dev, "Read from reg 0x%x failed\n", reg);
                err = data;
                goto out;
        }
 
        data |= mask;
-       err = tps_6507x_write(tps, reg, data);
+       err = tps6507x_pmic_write(tps, reg, data);
        if (err)
-               dev_err(&tps->client->dev, "Write for reg 0x%x failed\n", reg);
+               dev_err(tps->mfd->dev, "Write for reg 0x%x failed\n", reg);
 
 out:
        mutex_unlock(&tps->io_lock);
        return err;
 }
 
-static int tps_6507x_clear_bits(struct tps_pmic *tps, u8 reg, u8 mask)
+static int tps6507x_pmic_clear_bits(struct tps6507x_pmic *tps, u8 reg, u8 mask)
 {
        int err, data;
 
        mutex_lock(&tps->io_lock);
 
-       data = tps_6507x_read(tps, reg);
+       data = tps6507x_pmic_read(tps, reg);
        if (data < 0) {
-               dev_err(&tps->client->dev, "Read from reg 0x%x failed\n", reg);
+               dev_err(tps->mfd->dev, "Read from reg 0x%x failed\n", reg);
                err = data;
                goto out;
        }
 
        data &= ~mask;
-       err = tps_6507x_write(tps, reg, data);
+       err = tps6507x_pmic_write(tps, reg, data);
        if (err)
-               dev_err(&tps->client->dev, "Write for reg 0x%x failed\n", reg);
+               dev_err(tps->mfd->dev, "Write for reg 0x%x failed\n", reg);
 
 out:
        mutex_unlock(&tps->io_lock);
        return err;
 }
 
-static int tps_6507x_reg_read(struct tps_pmic *tps, u8 reg)
+static int tps6507x_pmic_reg_read(struct tps6507x_pmic *tps, u8 reg)
 {
        int data;
 
        mutex_lock(&tps->io_lock);
 
-       data = tps_6507x_read(tps, reg);
+       data = tps6507x_pmic_read(tps, reg);
        if (data < 0)
-               dev_err(&tps->client->dev, "Read from reg 0x%x failed\n", reg);
+               dev_err(tps->mfd->dev, "Read from reg 0x%x failed\n", reg);
 
        mutex_unlock(&tps->io_lock);
        return data;
 }
 
-static int tps_6507x_reg_write(struct tps_pmic *tps, u8 reg, u8 val)
+static int tps6507x_pmic_reg_write(struct tps6507x_pmic *tps, u8 reg, u8 val)
 {
        int err;
 
        mutex_lock(&tps->io_lock);
 
-       err = tps_6507x_write(tps, reg, val);
+       err = tps6507x_pmic_write(tps, reg, val);
        if (err < 0)
-               dev_err(&tps->client->dev, "Write for reg 0x%x failed\n", reg);
+               dev_err(tps->mfd->dev, "Write for reg 0x%x failed\n", reg);
 
        mutex_unlock(&tps->io_lock);
        return err;
 }
 
-static int tps6507x_dcdc_is_enabled(struct regulator_dev *dev)
+static int tps6507x_pmic_dcdc_is_enabled(struct regulator_dev *dev)
 {
-       struct tps_pmic *tps = rdev_get_drvdata(dev);
+       struct tps6507x_pmic *tps = rdev_get_drvdata(dev);
        int data, dcdc = rdev_get_id(dev);
        u8 shift;
 
@@ -264,7 +250,7 @@ static int tps6507x_dcdc_is_enabled(struct regulator_dev *dev)
                return -EINVAL;
 
        shift = TPS6507X_MAX_REG_ID - dcdc;
-       data = tps_6507x_reg_read(tps, TPS6507X_REG_CON_CTRL1);
+       data = tps6507x_pmic_reg_read(tps, TPS6507X_REG_CON_CTRL1);
 
        if (data < 0)
                return data;
@@ -272,9 +258,9 @@ static int tps6507x_dcdc_is_enabled(struct regulator_dev *dev)
                return (data & 1<<shift) ? 1 : 0;
 }
 
-static int tps6507x_ldo_is_enabled(struct regulator_dev *dev)
+static int tps6507x_pmic_ldo_is_enabled(struct regulator_dev *dev)
 {
-       struct tps_pmic *tps = rdev_get_drvdata(dev);
+       struct tps6507x_pmic *tps = rdev_get_drvdata(dev);
        int data, ldo = rdev_get_id(dev);
        u8 shift;
 
@@ -282,7 +268,7 @@ static int tps6507x_ldo_is_enabled(struct regulator_dev *dev)
                return -EINVAL;
 
        shift = TPS6507X_MAX_REG_ID - ldo;
-       data = tps_6507x_reg_read(tps, TPS6507X_REG_CON_CTRL1);
+       data = tps6507x_pmic_reg_read(tps, TPS6507X_REG_CON_CTRL1);
 
        if (data < 0)
                return data;
@@ -290,9 +276,9 @@ static int tps6507x_ldo_is_enabled(struct regulator_dev *dev)
                return (data & 1<<shift) ? 1 : 0;
 }
 
-static int tps6507x_dcdc_enable(struct regulator_dev *dev)
+static int tps6507x_pmic_dcdc_enable(struct regulator_dev *dev)
 {
-       struct tps_pmic *tps = rdev_get_drvdata(dev);
+       struct tps6507x_pmic *tps = rdev_get_drvdata(dev);
        int dcdc = rdev_get_id(dev);
        u8 shift;
 
@@ -300,12 +286,12 @@ static int tps6507x_dcdc_enable(struct regulator_dev *dev)
                return -EINVAL;
 
        shift = TPS6507X_MAX_REG_ID - dcdc;
-       return tps_6507x_set_bits(tps, TPS6507X_REG_CON_CTRL1, 1 << shift);
+       return tps6507x_pmic_set_bits(tps, TPS6507X_REG_CON_CTRL1, 1 << shift);
 }
 
-static int tps6507x_dcdc_disable(struct regulator_dev *dev)
+static int tps6507x_pmic_dcdc_disable(struct regulator_dev *dev)
 {
-       struct tps_pmic *tps = rdev_get_drvdata(dev);
+       struct tps6507x_pmic *tps = rdev_get_drvdata(dev);
        int dcdc = rdev_get_id(dev);
        u8 shift;
 
@@ -313,12 +299,13 @@ static int tps6507x_dcdc_disable(struct regulator_dev *dev)
                return -EINVAL;
 
        shift = TPS6507X_MAX_REG_ID - dcdc;
-       return tps_6507x_clear_bits(tps, TPS6507X_REG_CON_CTRL1, 1 << shift);
+       return tps6507x_pmic_clear_bits(tps, TPS6507X_REG_CON_CTRL1,
+                                       1 << shift);
 }
 
-static int tps6507x_ldo_enable(struct regulator_dev *dev)
+static int tps6507x_pmic_ldo_enable(struct regulator_dev *dev)
 {
-       struct tps_pmic *tps = rdev_get_drvdata(dev);
+       struct tps6507x_pmic *tps = rdev_get_drvdata(dev);
        int ldo = rdev_get_id(dev);
        u8 shift;
 
@@ -326,12 +313,12 @@ static int tps6507x_ldo_enable(struct regulator_dev *dev)
                return -EINVAL;
 
        shift = TPS6507X_MAX_REG_ID - ldo;
-       return tps_6507x_set_bits(tps, TPS6507X_REG_CON_CTRL1, 1 << shift);
+       return tps6507x_pmic_set_bits(tps, TPS6507X_REG_CON_CTRL1, 1 << shift);
 }
 
-static int tps6507x_ldo_disable(struct regulator_dev *dev)
+static int tps6507x_pmic_ldo_disable(struct regulator_dev *dev)
 {
-       struct tps_pmic *tps = rdev_get_drvdata(dev);
+       struct tps6507x_pmic *tps = rdev_get_drvdata(dev);
        int ldo = rdev_get_id(dev);
        u8 shift;
 
@@ -339,12 +326,13 @@ static int tps6507x_ldo_disable(struct regulator_dev *dev)
                return -EINVAL;
 
        shift = TPS6507X_MAX_REG_ID - ldo;
-       return tps_6507x_clear_bits(tps, TPS6507X_REG_CON_CTRL1, 1 << shift);
+       return tps6507x_pmic_clear_bits(tps, TPS6507X_REG_CON_CTRL1,
+                                       1 << shift);
 }
 
-static int tps6507x_dcdc_get_voltage(struct regulator_dev *dev)
+static int tps6507x_pmic_dcdc_get_voltage(struct regulator_dev *dev)
 {
-       struct tps_pmic *tps = rdev_get_drvdata(dev);
+       struct tps6507x_pmic *tps = rdev_get_drvdata(dev);
        int data, dcdc = rdev_get_id(dev);
        u8 reg;
 
@@ -362,7 +350,7 @@ static int tps6507x_dcdc_get_voltage(struct regulator_dev *dev)
                return -EINVAL;
        }
 
-       data = tps_6507x_reg_read(tps, reg);
+       data = tps6507x_pmic_reg_read(tps, reg);
        if (data < 0)
                return data;
 
@@ -370,10 +358,10 @@ static int tps6507x_dcdc_get_voltage(struct regulator_dev *dev)
        return tps->info[dcdc]->table[data] * 1000;
 }
 
-static int tps6507x_dcdc_set_voltage(struct regulator_dev *dev,
+static int tps6507x_pmic_dcdc_set_voltage(struct regulator_dev *dev,
                                int min_uV, int max_uV)
 {
-       struct tps_pmic *tps = rdev_get_drvdata(dev);
+       struct tps6507x_pmic *tps = rdev_get_drvdata(dev);
        int data, vsel, dcdc = rdev_get_id(dev);
        u8 reg;
 
@@ -411,19 +399,19 @@ static int tps6507x_dcdc_set_voltage(struct regulator_dev *dev,
        if (vsel == tps->info[dcdc]->table_len)
                return -EINVAL;
 
-       data = tps_6507x_reg_read(tps, reg);
+       data = tps6507x_pmic_reg_read(tps, reg);
        if (data < 0)
                return data;
 
        data &= ~TPS6507X_DEFDCDCX_DCDC_MASK;
        data |= vsel;
 
-       return tps_6507x_reg_write(tps, reg, data);
+       return tps6507x_pmic_reg_write(tps, reg, data);
 }
 
-static int tps6507x_ldo_get_voltage(struct regulator_dev *dev)
+static int tps6507x_pmic_ldo_get_voltage(struct regulator_dev *dev)
 {
-       struct tps_pmic *tps = rdev_get_drvdata(dev);
+       struct tps6507x_pmic *tps = rdev_get_drvdata(dev);
        int data, ldo = rdev_get_id(dev);
        u8 reg, mask;
 
@@ -437,7 +425,7 @@ static int tps6507x_ldo_get_voltage(struct regulator_dev *dev)
                                TPS6507X_REG_DEFLDO2_LDO2_MASK);
        }
 
-       data = tps_6507x_reg_read(tps, reg);
+       data = tps6507x_pmic_reg_read(tps, reg);
        if (data < 0)
                return data;
 
@@ -445,10 +433,10 @@ static int tps6507x_ldo_get_voltage(struct regulator_dev *dev)
        return tps->info[ldo]->table[data] * 1000;
 }
 
-static int tps6507x_ldo_set_voltage(struct regulator_dev *dev,
+static int tps6507x_pmic_ldo_set_voltage(struct regulator_dev *dev,
                                int min_uV, int max_uV)
 {
-       struct tps_pmic *tps = rdev_get_drvdata(dev);
+       struct tps6507x_pmic *tps = rdev_get_drvdata(dev);
        int data, vsel, ldo = rdev_get_id(dev);
        u8 reg, mask;
 
@@ -479,20 +467,20 @@ static int tps6507x_ldo_set_voltage(struct regulator_dev *dev,
        if (vsel == tps->info[ldo]->table_len)
                return -EINVAL;
 
-       data = tps_6507x_reg_read(tps, reg);
+       data = tps6507x_pmic_reg_read(tps, reg);
        if (data < 0)
                return data;
 
        data &= ~mask;
        data |= vsel;
 
-       return tps_6507x_reg_write(tps, reg, data);
+       return tps6507x_pmic_reg_write(tps, reg, data);
 }
 
-static int tps6507x_dcdc_list_voltage(struct regulator_dev *dev,
+static int tps6507x_pmic_dcdc_list_voltage(struct regulator_dev *dev,
                                        unsigned selector)
 {
-       struct tps_pmic *tps = rdev_get_drvdata(dev);
+       struct tps6507x_pmic *tps = rdev_get_drvdata(dev);
        int dcdc = rdev_get_id(dev);
 
        if (dcdc < TPS6507X_DCDC_1 || dcdc > TPS6507X_DCDC_3)
@@ -504,10 +492,10 @@ static int tps6507x_dcdc_list_voltage(struct regulator_dev *dev,
                return tps->info[dcdc]->table[selector] * 1000;
 }
 
-static int tps6507x_ldo_list_voltage(struct regulator_dev *dev,
+static int tps6507x_pmic_ldo_list_voltage(struct regulator_dev *dev,
                                        unsigned selector)
 {
-       struct tps_pmic *tps = rdev_get_drvdata(dev);
+       struct tps6507x_pmic *tps = rdev_get_drvdata(dev);
        int ldo = rdev_get_id(dev);
 
        if (ldo < TPS6507X_LDO_1 || ldo > TPS6507X_LDO_2)
@@ -520,47 +508,54 @@ static int tps6507x_ldo_list_voltage(struct regulator_dev *dev,
 }
 
 /* Operations permitted on VDCDCx */
-static struct regulator_ops tps6507x_dcdc_ops = {
-       .is_enabled = tps6507x_dcdc_is_enabled,
-       .enable = tps6507x_dcdc_enable,
-       .disable = tps6507x_dcdc_disable,
-       .get_voltage = tps6507x_dcdc_get_voltage,
-       .set_voltage = tps6507x_dcdc_set_voltage,
-       .list_voltage = tps6507x_dcdc_list_voltage,
+static struct regulator_ops tps6507x_pmic_dcdc_ops = {
+       .is_enabled = tps6507x_pmic_dcdc_is_enabled,
+       .enable = tps6507x_pmic_dcdc_enable,
+       .disable = tps6507x_pmic_dcdc_disable,
+       .get_voltage = tps6507x_pmic_dcdc_get_voltage,
+       .set_voltage = tps6507x_pmic_dcdc_set_voltage,
+       .list_voltage = tps6507x_pmic_dcdc_list_voltage,
 };
 
 /* Operations permitted on LDOx */
-static struct regulator_ops tps6507x_ldo_ops = {
-       .is_enabled = tps6507x_ldo_is_enabled,
-       .enable = tps6507x_ldo_enable,
-       .disable = tps6507x_ldo_disable,
-       .get_voltage = tps6507x_ldo_get_voltage,
-       .set_voltage = tps6507x_ldo_set_voltage,
-       .list_voltage = tps6507x_ldo_list_voltage,
+static struct regulator_ops tps6507x_pmic_ldo_ops = {
+       .is_enabled = tps6507x_pmic_ldo_is_enabled,
+       .enable = tps6507x_pmic_ldo_enable,
+       .disable = tps6507x_pmic_ldo_disable,
+       .get_voltage = tps6507x_pmic_ldo_get_voltage,
+       .set_voltage = tps6507x_pmic_ldo_set_voltage,
+       .list_voltage = tps6507x_pmic_ldo_list_voltage,
 };
 
-static int __devinit tps_6507x_probe(struct i2c_client *client,
-                                    const struct i2c_device_id *id)
+static __devinit
+int tps6507x_pmic_probe(struct platform_device *pdev)
 {
+       struct tps6507x_dev *tps6507x_dev = dev_get_drvdata(pdev->dev.parent);
        static int desc_id;
-       const struct tps_info *info = (void *)id->driver_data;
+       const struct tps_info *info = &tps6507x_pmic_regs[0];
        struct regulator_init_data *init_data;
        struct regulator_dev *rdev;
-       struct tps_pmic *tps;
+       struct tps6507x_pmic *tps;
+       struct tps6507x_board *tps_board;
        int i;
        int error;
 
-       if (!i2c_check_functionality(client->adapter,
-                               I2C_FUNC_SMBUS_BYTE_DATA))
-               return -EIO;
+       /**
+        * tps_board points to pmic related constants
+        * coming from the board-evm file.
+        */
+
+       tps_board = dev_get_platdata(tps6507x_dev->dev);
+       if (!tps_board)
+               return -EINVAL;
 
        /**
         * init_data points to array of regulator_init structures
         * coming from the board-evm file.
         */
-       init_data = client->dev.platform_data;
+       init_data = tps_board->tps6507x_pmic_init_data;
        if (!init_data)
-               return -EIO;
+               return -EINVAL;
 
        tps = kzalloc(sizeof(*tps), GFP_KERNEL);
        if (!tps)
@@ -569,7 +564,7 @@ static int __devinit tps_6507x_probe(struct i2c_client *client,
        mutex_init(&tps->io_lock);
 
        /* common for all regulators */
-       tps->client = client;
+       tps->mfd = tps6507x_dev;
 
        for (i = 0; i < TPS6507X_NUM_REGULATOR; i++, info++, init_data++) {
                /* Register the regulators */
@@ -578,15 +573,16 @@ static int __devinit tps_6507x_probe(struct i2c_client *client,
                tps->desc[i].id = desc_id++;
                tps->desc[i].n_voltages = num_voltages[i];
                tps->desc[i].ops = (i > TPS6507X_DCDC_3 ?
-                               &tps6507x_ldo_ops : &tps6507x_dcdc_ops);
+               &tps6507x_pmic_ldo_ops : &tps6507x_pmic_dcdc_ops);
                tps->desc[i].type = REGULATOR_VOLTAGE;
                tps->desc[i].owner = THIS_MODULE;
 
                rdev = regulator_register(&tps->desc[i],
-                                       &client->dev, init_data, tps);
+                                       tps6507x_dev->dev, init_data, tps);
                if (IS_ERR(rdev)) {
-                       dev_err(&client->dev, "failed to register %s\n",
-                               id->name);
+                       dev_err(tps6507x_dev->dev,
+                               "failed to register %s regulator\n",
+                               pdev->name);
                        error = PTR_ERR(rdev);
                        goto fail;
                }
@@ -595,7 +591,7 @@ static int __devinit tps_6507x_probe(struct i2c_client *client,
                tps->rdev[i] = rdev;
        }
 
-       i2c_set_clientdata(client, tps);
+       tps6507x_dev->pmic = tps;
 
        return 0;
 
@@ -608,19 +604,17 @@ fail:
 }
 
 /**
- * tps_6507x_remove - TPS6507x driver i2c remove handler
+ * tps6507x_remove - TPS6507x driver i2c remove handler
  * @client: i2c driver client device structure
  *
  * Unregister TPS driver as an i2c client device driver
  */
-static int __devexit tps_6507x_remove(struct i2c_client *client)
+static int __devexit tps6507x_pmic_remove(struct platform_device *pdev)
 {
-       struct tps_pmic *tps = i2c_get_clientdata(client);
+       struct tps6507x_dev *tps6507x_dev = platform_get_drvdata(pdev);
+       struct tps6507x_pmic *tps = tps6507x_dev->pmic;
        int i;
 
-       /* clear the client data in i2c */
-       i2c_set_clientdata(client, NULL);
-
        for (i = 0; i < TPS6507X_NUM_REGULATOR; i++)
                regulator_unregister(tps->rdev[i]);
 
@@ -629,83 +623,38 @@ static int __devexit tps_6507x_remove(struct i2c_client *client)
        return 0;
 }
 
-static const struct tps_info tps6507x_regs[] = {
-       {
-               .name = "VDCDC1",
-               .min_uV = 725000,
-               .max_uV = 3300000,
-               .table_len = ARRAY_SIZE(VDCDCx_VSEL_table),
-               .table = VDCDCx_VSEL_table,
-       },
-       {
-               .name = "VDCDC2",
-               .min_uV = 725000,
-               .max_uV = 3300000,
-               .table_len = ARRAY_SIZE(VDCDCx_VSEL_table),
-               .table = VDCDCx_VSEL_table,
-       },
-       {
-               .name = "VDCDC3",
-               .min_uV = 725000,
-               .max_uV = 3300000,
-               .table_len = ARRAY_SIZE(VDCDCx_VSEL_table),
-               .table = VDCDCx_VSEL_table,
-       },
-       {
-               .name = "LDO1",
-               .min_uV = 1000000,
-               .max_uV = 3300000,
-               .table_len = ARRAY_SIZE(LDO1_VSEL_table),
-               .table = LDO1_VSEL_table,
-       },
-       {
-               .name = "LDO2",
-               .min_uV = 725000,
-               .max_uV = 3300000,
-               .table_len = ARRAY_SIZE(LDO2_VSEL_table),
-               .table = LDO2_VSEL_table,
-       },
-};
-
-static const struct i2c_device_id tps_6507x_id[] = {
-       {.name = "tps6507x",
-       .driver_data = (unsigned long) tps6507x_regs,},
-       { },
-};
-MODULE_DEVICE_TABLE(i2c, tps_6507x_id);
-
-static struct i2c_driver tps_6507x_i2c_driver = {
+static struct platform_driver tps6507x_pmic_driver = {
        .driver = {
-               .name = "tps6507x",
+               .name = "tps6507x-pmic",
                .owner = THIS_MODULE,
        },
-       .probe = tps_6507x_probe,
-       .remove = __devexit_p(tps_6507x_remove),
-       .id_table = tps_6507x_id,
+       .probe = tps6507x_pmic_probe,
+       .remove = __devexit_p(tps6507x_pmic_remove),
 };
 
 /**
- * tps_6507x_init
+ * tps6507x_pmic_init
  *
  * Module init function
  */
-static int __init tps_6507x_init(void)
+static int __init tps6507x_pmic_init(void)
 {
-       return i2c_add_driver(&tps_6507x_i2c_driver);
+       return platform_driver_register(&tps6507x_pmic_driver);
 }
-subsys_initcall(tps_6507x_init);
+subsys_initcall(tps6507x_pmic_init);
 
 /**
- * tps_6507x_cleanup
+ * tps6507x_pmic_cleanup
  *
  * Module exit function
  */
-static void __exit tps_6507x_cleanup(void)
+static void __exit tps6507x_pmic_cleanup(void)
 {
-       i2c_del_driver(&tps_6507x_i2c_driver);
+       platform_driver_unregister(&tps6507x_pmic_driver);
 }
-module_exit(tps_6507x_cleanup);
+module_exit(tps6507x_pmic_cleanup);
 
 MODULE_AUTHOR("Texas Instruments");
 MODULE_DESCRIPTION("TPS6507x voltage regulator driver");
 MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:tps6507x-pmic");
index 4704aac..d26780e 100644 (file)
@@ -9,7 +9,7 @@
 #include <linux/init.h>
 #include <linux/platform_device.h>
 #include <linux/rtc.h>
-#include <linux/mfd/ab3100.h>
+#include <linux/mfd/abx500.h>
 
 /* Clock rate in Hz */
 #define AB3100_RTC_CLOCK_RATE  32768
@@ -45,7 +45,6 @@
  */
 static int ab3100_rtc_set_mmss(struct device *dev, unsigned long secs)
 {
-       struct ab3100 *ab3100_data = dev_get_drvdata(dev);
        u8 regs[] = {AB3100_TI0, AB3100_TI1, AB3100_TI2,
                     AB3100_TI3, AB3100_TI4, AB3100_TI5};
        unsigned char buf[6];
@@ -61,27 +60,26 @@ static int ab3100_rtc_set_mmss(struct device *dev, unsigned long secs)
        buf[5] = (fat_time >> 40) & 0xFF;
 
        for (i = 0; i < 6; i++) {
-               err = ab3100_set_register_interruptible(ab3100_data,
+               err = abx500_set_register_interruptible(dev, 0,
                                                        regs[i], buf[i]);
                if (err)
                        return err;
        }
 
        /* Set the flag to mark that the clock is now set */
-       return ab3100_mask_and_set_register_interruptible(ab3100_data,
+       return abx500_mask_and_set_register_interruptible(dev, 0,
                                                          AB3100_RTC,
-                                                         0xFE, 0x01);
+                                                         0x01, 0x01);
 
 }
 
 static int ab3100_rtc_read_time(struct device *dev, struct rtc_time *tm)
 {
-       struct ab3100 *ab3100_data = dev_get_drvdata(dev);
        unsigned long time;
        u8 rtcval;
        int err;
 
-       err = ab3100_get_register_interruptible(ab3100_data,
+       err = abx500_get_register_interruptible(dev, 0,
                                                AB3100_RTC, &rtcval);
        if (err)
                return err;
@@ -94,7 +92,7 @@ static int ab3100_rtc_read_time(struct device *dev, struct rtc_time *tm)
                u8 buf[6];
 
                /* Read out time registers */
-               err = ab3100_get_register_page_interruptible(ab3100_data,
+               err = abx500_get_register_page_interruptible(dev, 0,
                                                             AB3100_TI0,
                                                             buf, 6);
                if (err != 0)
@@ -114,7 +112,6 @@ static int ab3100_rtc_read_time(struct device *dev, struct rtc_time *tm)
 
 static int ab3100_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm)
 {
-       struct ab3100 *ab3100_data = dev_get_drvdata(dev);
        unsigned long time;
        u64 fat_time;
        u8 buf[6];
@@ -122,7 +119,7 @@ static int ab3100_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm)
        int err;
 
        /* Figure out if alarm is enabled or not */
-       err = ab3100_get_register_interruptible(ab3100_data,
+       err = abx500_get_register_interruptible(dev, 0,
                                                AB3100_RTC, &rtcval);
        if (err)
                return err;
@@ -133,7 +130,7 @@ static int ab3100_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm)
        /* No idea how this could be represented */
        alarm->pending = 0;
        /* Read out alarm registers, only 4 bytes */
-       err = ab3100_get_register_page_interruptible(ab3100_data,
+       err = abx500_get_register_page_interruptible(dev, 0,
                                                     AB3100_AL0, buf, 4);
        if (err)
                return err;
@@ -148,7 +145,6 @@ static int ab3100_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm)
 
 static int ab3100_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm)
 {
-       struct ab3100 *ab3100_data = dev_get_drvdata(dev);
        u8 regs[] = {AB3100_AL0, AB3100_AL1, AB3100_AL2, AB3100_AL3};
        unsigned char buf[4];
        unsigned long secs;
@@ -165,21 +161,19 @@ static int ab3100_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm)
 
        /* Set the alarm */
        for (i = 0; i < 4; i++) {
-               err = ab3100_set_register_interruptible(ab3100_data,
+               err = abx500_set_register_interruptible(dev, 0,
                                                        regs[i], buf[i]);
                if (err)
                        return err;
        }
        /* Then enable the alarm */
-       return ab3100_mask_and_set_register_interruptible(ab3100_data,
-                                                         AB3100_RTC, ~(1 << 2),
+       return abx500_mask_and_set_register_interruptible(dev, 0,
+                                                         AB3100_RTC, (1 << 2),
                                                          alarm->enabled << 2);
 }
 
 static int ab3100_rtc_irq_enable(struct device *dev, unsigned int enabled)
 {
-       struct ab3100 *ab3100_data = dev_get_drvdata(dev);
-
        /*
         * It's not possible to enable/disable the alarm IRQ for this RTC.
         * It does not actually trigger any IRQ: instead its only function is
@@ -188,12 +182,12 @@ static int ab3100_rtc_irq_enable(struct device *dev, unsigned int enabled)
         * and need to be handled there instead.
         */
        if (enabled)
-               return ab3100_mask_and_set_register_interruptible(ab3100_data,
-                                                   AB3100_RTC, ~(1 << 2),
+               return abx500_mask_and_set_register_interruptible(dev, 0,
+                                                   AB3100_RTC, (1 << 2),
                                                    1 << 2);
        else
-               return ab3100_mask_and_set_register_interruptible(ab3100_data,
-                                                   AB3100_RTC, ~(1 << 2),
+               return abx500_mask_and_set_register_interruptible(dev, 0,
+                                                   AB3100_RTC, (1 << 2),
                                                    0);
 }
 
@@ -210,10 +204,9 @@ static int __init ab3100_rtc_probe(struct platform_device *pdev)
        int err;
        u8 regval;
        struct rtc_device *rtc;
-       struct ab3100 *ab3100_data = platform_get_drvdata(pdev);
 
        /* The first RTC register needs special treatment */
-       err = ab3100_get_register_interruptible(ab3100_data,
+       err = abx500_get_register_interruptible(&pdev->dev, 0,
                                                AB3100_RTC, &regval);
        if (err) {
                dev_err(&pdev->dev, "unable to read RTC register\n");
@@ -231,7 +224,7 @@ static int __init ab3100_rtc_probe(struct platform_device *pdev)
                 * This bit remains until RTC power is lost.
                 */
                regval = 1 | RTC_SETTING;
-               err = ab3100_set_register_interruptible(ab3100_data,
+               err = abx500_set_register_interruptible(&pdev->dev, 0,
                                                        AB3100_RTC, regval);
                /* Ignore any error on this write */
        }
index 8dc0383..4a789e5 100644 (file)
@@ -119,7 +119,7 @@ static int s5p_serial_probe(struct platform_device *pdev)
        return s3c24xx_serial_probe(pdev, s5p_uart_inf[pdev->id]);
 }
 
-static struct platform_driver s5p_serial_drv = {
+static struct platform_driver s5p_serial_driver = {
        .probe          = s5p_serial_probe,
        .remove         = __devexit_p(s3c24xx_serial_remove),
        .driver         = {
@@ -130,19 +130,19 @@ static struct platform_driver s5p_serial_drv = {
 
 static int __init s5pv210_serial_console_init(void)
 {
-       return s3c24xx_serial_initconsole(&s5p_serial_drv, s5p_uart_inf);
+       return s3c24xx_serial_initconsole(&s5p_serial_driver, s5p_uart_inf);
 }
 
 console_initcall(s5pv210_serial_console_init);
 
 static int __init s5p_serial_init(void)
 {
-       return s3c24xx_serial_init(&s5p_serial_drv, *s5p_uart_inf);
+       return s3c24xx_serial_init(&s5p_serial_driver, *s5p_uart_inf);
 }
 
 static void __exit s5p_serial_exit(void)
 {
-       platform_driver_unregister(&s5p_serial_drv);
+       platform_driver_unregister(&s5p_serial_driver);
 }
 
 module_init(s5p_serial_init);
index 9286e86..643b413 100644 (file)
@@ -29,7 +29,6 @@
 #include <linux/slab.h>
 #include <linux/statfs.h>
 #include <linux/writeback.h>
-#include <linux/quotaops.h>
 
 #include "netfs.h"
 
@@ -880,7 +879,7 @@ static struct inode *pohmelfs_alloc_inode(struct super_block *sb)
 /*
  * We want fsync() to work on POHMELFS.
  */
-static int pohmelfs_fsync(struct file *file, struct dentry *dentry, int datasync)
+static int pohmelfs_fsync(struct file *file, int datasync)
 {
        struct inode *inode = file->f_mapping->host;
        struct writeback_control wbc = {
@@ -969,13 +968,6 @@ int pohmelfs_setattr_raw(struct inode *inode, struct iattr *attr)
                goto err_out_exit;
        }
 
-       if ((attr->ia_valid & ATTR_UID && attr->ia_uid != inode->i_uid) ||
-           (attr->ia_valid & ATTR_GID && attr->ia_gid != inode->i_gid)) {
-               err = dquot_transfer(inode, attr);
-               if (err)
-                       goto err_out_exit;
-       }
-
        err = inode_setattr(inode, attr);
        if (err) {
                dprintk("%s: ino: %llu, failed to set the attributes.\n", __func__, POHMELFS_I(inode)->ino);
index 6b8bf8c..43abf55 100644 (file)
@@ -794,7 +794,7 @@ printer_write(struct file *fd, const char __user *buf, size_t len, loff_t *ptr)
 }
 
 static int
-printer_fsync(struct file *fd, struct dentry *dentry, int datasync)
+printer_fsync(struct file *fd, int datasync)
 {
        struct printer_dev      *dev = fd->private_data;
        unsigned long           flags;
index aa88911..0f41c91 100644 (file)
@@ -593,17 +593,17 @@ static long vhost_net_ioctl(struct file *f, unsigned int ioctl,
        int r;
        switch (ioctl) {
        case VHOST_NET_SET_BACKEND:
-               r = copy_from_user(&backend, argp, sizeof backend);
-               if (r < 0)
-                       return r;
+               if (copy_from_user(&backend, argp, sizeof backend))
+                       return -EFAULT;
                return vhost_net_set_backend(n, backend.index, backend.fd);
        case VHOST_GET_FEATURES:
                features = VHOST_FEATURES;
-               return copy_to_user(featurep, &features, sizeof features);
+               if (copy_to_user(featurep, &features, sizeof features))
+                       return -EFAULT;
+               return 0;
        case VHOST_SET_FEATURES:
-               r = copy_from_user(&features, featurep, sizeof features);
-               if (r < 0)
-                       return r;
+               if (copy_from_user(&features, featurep, sizeof features))
+                       return -EFAULT;
                if (features & ~VHOST_FEATURES)
                        return -EOPNOTSUPP;
                return vhost_net_set_features(n, features);
index c6fb8e9..3b83382 100644 (file)
@@ -320,10 +320,8 @@ static long vhost_set_memory(struct vhost_dev *d, struct vhost_memory __user *m)
 {
        struct vhost_memory mem, *newmem, *oldmem;
        unsigned long size = offsetof(struct vhost_memory, regions);
-       long r;
-       r = copy_from_user(&mem, m, size);
-       if (r)
-               return r;
+       if (copy_from_user(&mem, m, size))
+               return -EFAULT;
        if (mem.padding)
                return -EOPNOTSUPP;
        if (mem.nregions > VHOST_MEMORY_MAX_NREGIONS)
@@ -333,15 +331,16 @@ static long vhost_set_memory(struct vhost_dev *d, struct vhost_memory __user *m)
                return -ENOMEM;
 
        memcpy(newmem, &mem, size);
-       r = copy_from_user(newmem->regions, m->regions,
-                          mem.nregions * sizeof *m->regions);
-       if (r) {
+       if (copy_from_user(newmem->regions, m->regions,
+                          mem.nregions * sizeof *m->regions)) {
                kfree(newmem);
-               return r;
+               return -EFAULT;
        }
 
-       if (!memory_access_ok(d, newmem, vhost_has_feature(d, VHOST_F_LOG_ALL)))
+       if (!memory_access_ok(d, newmem, vhost_has_feature(d, VHOST_F_LOG_ALL))) {
+               kfree(newmem);
                return -EFAULT;
+       }
        oldmem = d->memory;
        rcu_assign_pointer(d->memory, newmem);
        synchronize_rcu();
@@ -374,7 +373,7 @@ static long vhost_set_vring(struct vhost_dev *d, int ioctl, void __user *argp)
        r = get_user(idx, idxp);
        if (r < 0)
                return r;
-       if (idx > d->nvqs)
+       if (idx >= d->nvqs)
                return -ENOBUFS;
 
        vq = d->vqs + idx;
@@ -389,9 +388,10 @@ static long vhost_set_vring(struct vhost_dev *d, int ioctl, void __user *argp)
                        r = -EBUSY;
                        break;
                }
-               r = copy_from_user(&s, argp, sizeof s);
-               if (r < 0)
+               if (copy_from_user(&s, argp, sizeof s)) {
+                       r = -EFAULT;
                        break;
+               }
                if (!s.num || s.num > 0xffff || (s.num & (s.num - 1))) {
                        r = -EINVAL;
                        break;
@@ -405,9 +405,10 @@ static long vhost_set_vring(struct vhost_dev *d, int ioctl, void __user *argp)
                        r = -EBUSY;
                        break;
                }
-               r = copy_from_user(&s, argp, sizeof s);
-               if (r < 0)
+               if (copy_from_user(&s, argp, sizeof s)) {
+                       r = -EFAULT;
                        break;
+               }
                if (s.num > 0xffff) {
                        r = -EINVAL;
                        break;
@@ -419,12 +420,14 @@ static long vhost_set_vring(struct vhost_dev *d, int ioctl, void __user *argp)
        case VHOST_GET_VRING_BASE:
                s.index = idx;
                s.num = vq->last_avail_idx;
-               r = copy_to_user(argp, &s, sizeof s);
+               if (copy_to_user(argp, &s, sizeof s))
+                       r = -EFAULT;
                break;
        case VHOST_SET_VRING_ADDR:
-               r = copy_from_user(&a, argp, sizeof a);
-               if (r < 0)
+               if (copy_from_user(&a, argp, sizeof a)) {
+                       r = -EFAULT;
                        break;
+               }
                if (a.flags & ~(0x1 << VHOST_VRING_F_LOG)) {
                        r = -EOPNOTSUPP;
                        break;
@@ -477,9 +480,10 @@ static long vhost_set_vring(struct vhost_dev *d, int ioctl, void __user *argp)
                vq->used = (void __user *)(unsigned long)a.used_user_addr;
                break;
        case VHOST_SET_VRING_KICK:
-               r = copy_from_user(&f, argp, sizeof f);
-               if (r < 0)
+               if (copy_from_user(&f, argp, sizeof f)) {
+                       r = -EFAULT;
                        break;
+               }
                eventfp = f.fd == -1 ? NULL : eventfd_fget(f.fd);
                if (IS_ERR(eventfp)) {
                        r = PTR_ERR(eventfp);
@@ -492,9 +496,10 @@ static long vhost_set_vring(struct vhost_dev *d, int ioctl, void __user *argp)
                        filep = eventfp;
                break;
        case VHOST_SET_VRING_CALL:
-               r = copy_from_user(&f, argp, sizeof f);
-               if (r < 0)
+               if (copy_from_user(&f, argp, sizeof f)) {
+                       r = -EFAULT;
                        break;
+               }
                eventfp = f.fd == -1 ? NULL : eventfd_fget(f.fd);
                if (IS_ERR(eventfp)) {
                        r = PTR_ERR(eventfp);
@@ -510,9 +515,10 @@ static long vhost_set_vring(struct vhost_dev *d, int ioctl, void __user *argp)
                        filep = eventfp;
                break;
        case VHOST_SET_VRING_ERR:
-               r = copy_from_user(&f, argp, sizeof f);
-               if (r < 0)
+               if (copy_from_user(&f, argp, sizeof f)) {
+                       r = -EFAULT;
                        break;
+               }
                eventfp = f.fd == -1 ? NULL : eventfd_fget(f.fd);
                if (IS_ERR(eventfp)) {
                        r = PTR_ERR(eventfp);
@@ -575,9 +581,10 @@ long vhost_dev_ioctl(struct vhost_dev *d, unsigned int ioctl, unsigned long arg)
                r = vhost_set_memory(d, argp);
                break;
        case VHOST_SET_LOG_BASE:
-               r = copy_from_user(&p, argp, sizeof p);
-               if (r < 0)
+               if (copy_from_user(&p, argp, sizeof p)) {
+                       r = -EFAULT;
                        break;
+               }
                if ((u64)(unsigned long)p != p) {
                        r = -EFAULT;
                        break;
index 68d2518..38ffc3f 100644 (file)
@@ -222,6 +222,7 @@ static int pm860x_backlight_probe(struct platform_device *pdev)
        data->port = __check_device(pdata, name);
        if (data->port < 0) {
                dev_err(&pdev->dev, "wrong platform data is assigned");
+               kfree(data);
                return -EINVAL;
        }
 
@@ -266,6 +267,7 @@ static int pm860x_backlight_probe(struct platform_device *pdev)
        backlight_update_status(bl);
        return 0;
 out:
+       backlight_device_unregister(bl);
        kfree(data);
        return ret;
 }
index c025c84..e54a337 100644 (file)
@@ -8,12 +8,13 @@ menuconfig BACKLIGHT_LCD_SUPPORT
          Enable this to be able to choose the drivers for controlling the
          backlight and the LCD panel on some platforms, for example on PDAs.
 
+if BACKLIGHT_LCD_SUPPORT
+
 #
 # LCD
 #
 config LCD_CLASS_DEVICE
         tristate "Lowlevel LCD controls"
-       depends on BACKLIGHT_LCD_SUPPORT
        default m
        help
          This framework adds support for low-level control of LCD.
@@ -24,31 +25,32 @@ config LCD_CLASS_DEVICE
          To have support for your specific LCD panel you will have to
          select the proper drivers which depend on this option.
 
+if LCD_CLASS_DEVICE
+
 config LCD_CORGI
        tristate "LCD Panel support for SHARP corgi/spitz model"
-       depends on LCD_CLASS_DEVICE && SPI_MASTER && PXA_SHARPSL
+       depends on SPI_MASTER && PXA_SHARPSL
        help
          Say y here to support the LCD panels usually found on SHARP
          corgi (C7x0) and spitz (Cxx00) models.
 
 config LCD_L4F00242T03
        tristate "Epson L4F00242T03 LCD"
-       depends on LCD_CLASS_DEVICE && SPI_MASTER && GENERIC_GPIO
+       depends on SPI_MASTER && GENERIC_GPIO
        help
          SPI driver for Epson L4F00242T03. This provides basic support
          for init and powering the LCD up/down through a sysfs interface.
 
 config LCD_LMS283GF05
        tristate "Samsung LMS283GF05 LCD"
-       depends on LCD_CLASS_DEVICE && SPI_MASTER && GENERIC_GPIO
+       depends on SPI_MASTER && GENERIC_GPIO
        help
          SPI driver for Samsung LMS283GF05. This provides basic support
          for powering the LCD up/down through a sysfs interface.
 
 config LCD_LTV350QV
        tristate "Samsung LTV350QV LCD Panel"
-       depends on LCD_CLASS_DEVICE && SPI_MASTER
-       default n
+       depends on SPI_MASTER
        help
          If you have a Samsung LTV350QV LCD panel, say y to include a
          power control driver for it.  The panel starts up in power
@@ -59,60 +61,61 @@ config LCD_LTV350QV
 
 config LCD_ILI9320
        tristate
-       depends on LCD_CLASS_DEVICE && BACKLIGHT_LCD_SUPPORT
-       default n
        help
          If you have a panel based on the ILI9320 controller chip
          then say y to include a power driver for it.
 
 config LCD_TDO24M
        tristate "Toppoly TDO24M  and TDO35S LCD Panels support"
-       depends on LCD_CLASS_DEVICE && SPI_MASTER
-       default n
+       depends on SPI_MASTER
        help
          If you have a Toppoly TDO24M/TDO35S series LCD panel, say y here to
          include the support for it.
 
 config LCD_VGG2432A4
        tristate "VGG2432A4 LCM device support"
-       depends on BACKLIGHT_LCD_SUPPORT && LCD_CLASS_DEVICE && SPI_MASTER
+       depends on SPI_MASTER
        select LCD_ILI9320
-       default n
        help
          If you have a VGG2432A4 panel based on the ILI9320 controller chip
          then say y to include a power driver for it.
 
 config LCD_PLATFORM
        tristate "Platform LCD controls"
-       depends on LCD_CLASS_DEVICE
        help
          This driver provides a platform-device registered LCD power
          control interface.
 
 config LCD_TOSA
        tristate "Sharp SL-6000 LCD Driver"
-       depends on LCD_CLASS_DEVICE && SPI
-       depends on MACH_TOSA
-       default n
+       depends on SPI && MACH_TOSA
        help
          If you have an Sharp SL-6000 Zaurus say Y to enable a driver
          for its LCD.
 
 config LCD_HP700
        tristate "HP Jornada 700 series LCD Driver"
-       depends on LCD_CLASS_DEVICE
        depends on SA1100_JORNADA720_SSP && !PREEMPT
        default y
        help
          If you have an HP Jornada 700 series handheld (710/720/728)
          say Y to enable LCD control driver.
 
+config LCD_S6E63M0
+       tristate "S6E63M0 AMOLED LCD Driver"
+       depends on SPI && BACKLIGHT_CLASS_DEVICE
+       default n
+       help
+         If you have an S6E63M0 LCD Panel, say Y to enable its
+         LCD control driver.
+
+endif # LCD_CLASS_DEVICE
+
 #
 # Backlight
 #
 config BACKLIGHT_CLASS_DEVICE
         tristate "Lowlevel Backlight controls"
-       depends on BACKLIGHT_LCD_SUPPORT
        default m
        help
          This framework adds support for low-level control of the LCD
@@ -121,9 +124,11 @@ config BACKLIGHT_CLASS_DEVICE
          To have support for your specific LCD panel you will have to
          select the proper drivers which depend on this option.
 
+if BACKLIGHT_CLASS_DEVICE
+
 config BACKLIGHT_ATMEL_LCDC
        bool "Atmel LCDC Contrast-as-Backlight control"
-       depends on BACKLIGHT_CLASS_DEVICE && FB_ATMEL
+       depends on FB_ATMEL
        default y if MACH_SAM9261EK || MACH_SAM9G10EK || MACH_SAM9263EK
        help
          This provides a backlight control internal to the Atmel LCDC
@@ -136,8 +141,7 @@ config BACKLIGHT_ATMEL_LCDC
 
 config BACKLIGHT_ATMEL_PWM
        tristate "Atmel PWM backlight control"
-       depends on BACKLIGHT_CLASS_DEVICE && ATMEL_PWM
-       default n
+       depends on ATMEL_PWM
        help
          Say Y here if you want to use the PWM peripheral in Atmel AT91 and
          AVR32 devices. This driver will need additional platform data to know
@@ -146,9 +150,18 @@ config BACKLIGHT_ATMEL_PWM
          To compile this driver as a module, choose M here: the module will be
          called atmel-pwm-bl.
 
+config BACKLIGHT_EP93XX
+       tristate "Cirrus EP93xx Backlight Driver"
+       depends on FB_EP93XX
+       help
+         If you have a LCD backlight connected to the BRIGHT output of
+         the EP93xx, say Y here to enable this driver.
+
+         To compile this driver as a module, choose M here: the module will
+         be called ep93xx_bl.
+
 config BACKLIGHT_GENERIC
        tristate "Generic (aka Sharp Corgi) Backlight Driver"
-       depends on BACKLIGHT_CLASS_DEVICE
        default y
        help
          Say y to enable the generic platform backlight driver previously
@@ -157,7 +170,7 @@ config BACKLIGHT_GENERIC
 
 config BACKLIGHT_LOCOMO
        tristate "Sharp LOCOMO LCD/Backlight Driver"
-       depends on BACKLIGHT_CLASS_DEVICE && SHARP_LOCOMO
+       depends on SHARP_LOCOMO
        default y
        help
          If you have a Sharp Zaurus SL-5500 (Collie) or SL-5600 (Poodle) say y to
@@ -165,7 +178,7 @@ config BACKLIGHT_LOCOMO
 
 config BACKLIGHT_OMAP1
        tristate "OMAP1 PWL-based LCD Backlight"
-       depends on BACKLIGHT_CLASS_DEVICE && ARCH_OMAP1
+       depends on ARCH_OMAP1
        default y
        help
          This driver controls the LCD backlight level and power for
@@ -174,7 +187,7 @@ config BACKLIGHT_OMAP1
 
 config BACKLIGHT_HP680
        tristate "HP Jornada 680 Backlight Driver"
-       depends on BACKLIGHT_CLASS_DEVICE && SH_HP6XX
+       depends on SH_HP6XX
        default y
        help
          If you have a HP Jornada 680, say y to enable the
@@ -182,7 +195,6 @@ config BACKLIGHT_HP680
 
 config BACKLIGHT_HP700
        tristate "HP Jornada 700 series Backlight Driver"
-       depends on BACKLIGHT_CLASS_DEVICE
        depends on SA1100_JORNADA720_SSP && !PREEMPT
        default y
        help
@@ -191,76 +203,70 @@ config BACKLIGHT_HP700
 
 config BACKLIGHT_PROGEAR
        tristate "Frontpath ProGear Backlight Driver"
-       depends on BACKLIGHT_CLASS_DEVICE && PCI && X86
-       default n
+       depends on PCI && X86
        help
          If you have a Frontpath ProGear say Y to enable the
          backlight driver.
 
 config BACKLIGHT_CARILLO_RANCH
        tristate "Intel Carillo Ranch Backlight Driver"
-       depends on BACKLIGHT_CLASS_DEVICE && LCD_CLASS_DEVICE && PCI && X86 && FB_LE80578
-       default n
+       depends on LCD_CLASS_DEVICE && PCI && X86 && FB_LE80578
        help
          If you have a Intel LE80578 (Carillo Ranch) say Y to enable the
          backlight driver.
 
 config BACKLIGHT_PWM
        tristate "Generic PWM based Backlight Driver"
-       depends on BACKLIGHT_CLASS_DEVICE && HAVE_PWM
+       depends on HAVE_PWM
        help
          If you have a LCD backlight adjustable by PWM, say Y to enable
          this driver.
 
 config BACKLIGHT_DA903X
        tristate "Backlight Driver for DA9030/DA9034 using WLED"
-       depends on BACKLIGHT_CLASS_DEVICE && PMIC_DA903X
+       depends on PMIC_DA903X
        help
          If you have a LCD backlight connected to the WLED output of DA9030
          or DA9034 WLED output, say Y here to enable this driver.
 
 config BACKLIGHT_MAX8925
        tristate "Backlight driver for MAX8925"
-       depends on BACKLIGHT_CLASS_DEVICE && MFD_MAX8925
+       depends on MFD_MAX8925
        help
          If you have a LCD backlight connected to the WLED output of MAX8925
          WLED output, say Y here to enable this driver.
 
 config BACKLIGHT_MBP_NVIDIA
        tristate "MacBook Pro Nvidia Backlight Driver"
-       depends on BACKLIGHT_CLASS_DEVICE && X86
-       default n
+       depends on X86
        help
          If you have an Apple Macbook Pro with Nvidia graphics hardware say Y
         to enable a driver for its backlight
 
 config BACKLIGHT_TOSA
        tristate "Sharp SL-6000 Backlight Driver"
-       depends on BACKLIGHT_CLASS_DEVICE && I2C
-       depends on MACH_TOSA && LCD_TOSA
-       default n
+       depends on I2C && MACH_TOSA && LCD_TOSA
        help
          If you have an Sharp SL-6000 Zaurus say Y to enable a driver
          for its backlight
 
 config BACKLIGHT_SAHARA
        tristate "Tabletkiosk Sahara Touch-iT Backlight Driver"
-       depends on BACKLIGHT_CLASS_DEVICE && X86
-       default n
+       depends on X86
        help
          If you have a Tabletkiosk Sahara Touch-iT, say y to enable the
          backlight driver.
 
 config BACKLIGHT_WM831X
        tristate "WM831x PMIC Backlight Driver"
-       depends on BACKLIGHT_CLASS_DEVICE && MFD_WM831X
+       depends on MFD_WM831X
        help
          If you have a backlight driven by the ISINK and DCDC of a
          WM831x PMIC say y to enable the backlight driver for it.
 
 config BACKLIGHT_ADX
        tristate "Avionic Design Xanthos Backlight Driver"
-       depends on BACKLIGHT_CLASS_DEVICE && ARCH_PXA_ADX
+       depends on ARCH_PXA_ADX
        default y
        help
          Say Y to enable the backlight driver on Avionic Design Xanthos-based
@@ -268,7 +274,7 @@ config BACKLIGHT_ADX
 
 config BACKLIGHT_ADP5520
        tristate "Backlight Driver for ADP5520/ADP5501 using WLED"
-       depends on BACKLIGHT_CLASS_DEVICE && PMIC_ADP5520
+       depends on PMIC_ADP5520
        help
          If you have a LCD backlight connected to the BST/BL_SNK output of
          ADP5520 or ADP5501, say Y here to enable this driver.
@@ -276,9 +282,31 @@ config BACKLIGHT_ADP5520
          To compile this driver as a module, choose M here: the module will
          be called adp5520_bl.
 
+config BACKLIGHT_ADP8860
+       tristate "Backlight Driver for ADP8860/ADP8861/ADP8863 using WLED"
+       depends on BACKLIGHT_CLASS_DEVICE && I2C
+       select NEW_LEDS
+       select LEDS_CLASS
+       help
+         If you have a LCD backlight connected to the ADP8860, ADP8861 or
+         ADP8863 say Y here to enable this driver.
+
+         To compile this driver as a module, choose M here: the module will
+         be called adp8860_bl.
+
 config BACKLIGHT_88PM860X
        tristate "Backlight Driver for 88PM8606 using WLED"
-       depends on BACKLIGHT_CLASS_DEVICE && MFD_88PM860X
+       depends on MFD_88PM860X
        help
          Say Y to enable the backlight driver for Marvell 88PM8606.
 
+config BACKLIGHT_PCF50633
+       tristate "Backlight driver for NXP PCF50633 MFD"
+       depends on BACKLIGHT_CLASS_DEVICE && MFD_PCF50633
+       help
+         If you have a backlight driven by a NXP PCF50633 MFD, say Y here to
+         enable its driver.
+
+endif # BACKLIGHT_CLASS_DEVICE
+
+endif # BACKLIGHT_LCD_SUPPORT
index 09d1f14..44c0f81 100644 (file)
@@ -11,9 +11,11 @@ obj-$(CONFIG_LCD_PLATFORM)      += platform_lcd.o
 obj-$(CONFIG_LCD_VGG2432A4)       += vgg2432a4.o
 obj-$(CONFIG_LCD_TDO24M)          += tdo24m.o
 obj-$(CONFIG_LCD_TOSA)            += tosa_lcd.o
+obj-$(CONFIG_LCD_S6E63M0)      += s6e63m0.o
 
 obj-$(CONFIG_BACKLIGHT_CLASS_DEVICE) += backlight.o
 obj-$(CONFIG_BACKLIGHT_ATMEL_PWM)    += atmel-pwm-bl.o
+obj-$(CONFIG_BACKLIGHT_EP93XX) += ep93xx_bl.o
 obj-$(CONFIG_BACKLIGHT_GENERIC)        += generic_bl.o
 obj-$(CONFIG_BACKLIGHT_HP700)  += jornada720_bl.o
 obj-$(CONFIG_BACKLIGHT_HP680)  += hp680_bl.o
@@ -30,5 +32,7 @@ obj-$(CONFIG_BACKLIGHT_SAHARA)        += kb3886_bl.o
 obj-$(CONFIG_BACKLIGHT_WM831X) += wm831x_bl.o
 obj-$(CONFIG_BACKLIGHT_ADX)    += adx_bl.o
 obj-$(CONFIG_BACKLIGHT_ADP5520)        += adp5520_bl.o
+obj-$(CONFIG_BACKLIGHT_ADP8860)        += adp8860_bl.o
 obj-$(CONFIG_BACKLIGHT_88PM860X) += 88pm860x_bl.o
+obj-$(CONFIG_BACKLIGHT_PCF50633)       += pcf50633-backlight.o
 
diff --git a/drivers/video/backlight/adp8860_bl.c b/drivers/video/backlight/adp8860_bl.c
new file mode 100644 (file)
index 0000000..921ca37
--- /dev/null
@@ -0,0 +1,838 @@
+/*
+ * Backlight driver for Analog Devices ADP8860 Backlight Devices
+ *
+ * Copyright 2009-2010 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/pm.h>
+#include <linux/platform_device.h>
+#include <linux/i2c.h>
+#include <linux/fb.h>
+#include <linux/backlight.h>
+#include <linux/leds.h>
+#include <linux/slab.h>
+#include <linux/workqueue.h>
+
+#include <linux/i2c/adp8860.h>
+#define ADP8860_EXT_FEATURES
+#define ADP8860_USE_LEDS
+
+#define ADP8860_MFDVID 0x00 /* Manufacturer and device ID */
+#define ADP8860_MDCR 0x01 /* Device mode and status */
+#define ADP8860_MDCR2 0x02 /* Device mode and Status Register 2 */
+#define ADP8860_INTR_EN 0x03 /* Interrupts enable */
+#define ADP8860_CFGR 0x04 /* Configuration register */
+#define ADP8860_BLSEN 0x05 /* Sink enable backlight or independent */
+#define ADP8860_BLOFF 0x06 /* Backlight off timeout */
+#define ADP8860_BLDIM 0x07 /* Backlight dim timeout */
+#define ADP8860_BLFR 0x08 /* Backlight fade in and out rates */
+#define ADP8860_BLMX1 0x09 /* Backlight (Brightness Level 1-daylight) maximum current */
+#define ADP8860_BLDM1 0x0A /* Backlight (Brightness Level 1-daylight) dim current */
+#define ADP8860_BLMX2 0x0B /* Backlight (Brightness Level 2-office) maximum current */
+#define ADP8860_BLDM2 0x0C /* Backlight (Brightness Level 2-office) dim current */
+#define ADP8860_BLMX3 0x0D /* Backlight (Brightness Level 3-dark) maximum current */
+#define ADP8860_BLDM3 0x0E /* Backlight (Brightness Level 3-dark) dim current */
+#define ADP8860_ISCFR 0x0F /* Independent sink current fade control register */
+#define ADP8860_ISCC 0x10 /* Independent sink current control register */
+#define ADP8860_ISCT1 0x11 /* Independent Sink Current Timer Register LED[7:5] */
+#define ADP8860_ISCT2 0x12 /* Independent Sink Current Timer Register LED[4:1] */
+#define ADP8860_ISCF 0x13 /* Independent sink current fade register */
+#define ADP8860_ISC7 0x14 /* Independent Sink Current LED7 */
+#define ADP8860_ISC6 0x15 /* Independent Sink Current LED6 */
+#define ADP8860_ISC5 0x16 /* Independent Sink Current LED5 */
+#define ADP8860_ISC4 0x17 /* Independent Sink Current LED4 */
+#define ADP8860_ISC3 0x18 /* Independent Sink Current LED3 */
+#define ADP8860_ISC2 0x19 /* Independent Sink Current LED2 */
+#define ADP8860_ISC1 0x1A /* Independent Sink Current LED1 */
+#define ADP8860_CCFG 0x1B /* Comparator configuration */
+#define ADP8860_CCFG2 0x1C /* Second comparator configuration */
+#define ADP8860_L2_TRP 0x1D /* L2 comparator reference */
+#define ADP8860_L2_HYS 0x1E /* L2 hysteresis */
+#define ADP8860_L3_TRP 0x1F /* L3 comparator reference */
+#define ADP8860_L3_HYS 0x20 /* L3 hysteresis */
+#define ADP8860_PH1LEVL 0x21 /* First phototransistor ambient light level-low byte register */
+#define ADP8860_PH1LEVH 0x22 /* First phototransistor ambient light level-high byte register */
+#define ADP8860_PH2LEVL 0x23 /* Second phototransistor ambient light level-low byte register */
+#define ADP8860_PH2LEVH 0x24 /* Second phototransistor ambient light level-high byte register */
+
+#define ADP8860_MANUFID                0x0  /* Analog Devices ADP8860 Manufacturer ID */
+#define ADP8861_MANUFID                0x4  /* Analog Devices ADP8861 Manufacturer ID */
+#define ADP8863_MANUFID                0x2  /* Analog Devices ADP8863 Manufacturer ID */
+
+#define ADP8860_DEVID(x)       ((x) & 0xF)
+#define ADP8860_MANID(x)       ((x) >> 4)
+
+/* MDCR Device mode and status */
+#define INT_CFG                        (1 << 6)
+#define NSTBY                  (1 << 5)
+#define DIM_EN                 (1 << 4)
+#define GDWN_DIS               (1 << 3)
+#define SIS_EN                 (1 << 2)
+#define CMP_AUTOEN             (1 << 1)
+#define BLEN                   (1 << 0)
+
+/* ADP8860_CCFG Main ALS comparator level enable */
+#define L3_EN                  (1 << 1)
+#define L2_EN                  (1 << 0)
+
+#define CFGR_BLV_SHIFT         3
+#define CFGR_BLV_MASK          0x3
+#define ADP8860_FLAG_LED_MASK  0xFF
+
+#define FADE_VAL(in, out)      ((0xF & (in)) | ((0xF & (out)) << 4))
+#define BL_CFGR_VAL(law, blv)  ((((blv) & CFGR_BLV_MASK) << CFGR_BLV_SHIFT) | ((0x3 & (law)) << 1))
+#define ALS_CCFG_VAL(filt)     ((0x7 & filt) << 5)
+
+enum {
+       adp8860,
+       adp8861,
+       adp8863
+};
+
+struct adp8860_led {
+       struct led_classdev     cdev;
+       struct work_struct      work;
+       struct i2c_client       *client;
+       enum led_brightness     new_brightness;
+       int                     id;
+       int                     flags;
+};
+
+struct adp8860_bl {
+       struct i2c_client *client;
+       struct backlight_device *bl;
+       struct adp8860_led *led;
+       struct adp8860_backlight_platform_data *pdata;
+       struct mutex lock;
+       unsigned long cached_daylight_max;
+       int id;
+       int revid;
+       int current_brightness;
+       unsigned en_ambl_sens:1;
+       unsigned gdwn_dis:1;
+};
+
+static int adp8860_read(struct i2c_client *client, int reg, uint8_t *val)
+{
+       int ret;
+
+       ret = i2c_smbus_read_byte_data(client, reg);
+       if (ret < 0) {
+               dev_err(&client->dev, "failed reading at 0x%02x\n", reg);
+               return ret;
+       }
+
+       *val = (uint8_t)ret;
+       return 0;
+}
+
+static int adp8860_write(struct i2c_client *client, u8 reg, u8 val)
+{
+       return i2c_smbus_write_byte_data(client, reg, val);
+}
+
+static int adp8860_set_bits(struct i2c_client *client, int reg, uint8_t bit_mask)
+{
+       struct adp8860_bl *data = i2c_get_clientdata(client);
+       uint8_t reg_val;
+       int ret;
+
+       mutex_lock(&data->lock);
+
+       ret = adp8860_read(client, reg, &reg_val);
+
+       if (!ret && ((reg_val & bit_mask) == 0)) {
+               reg_val |= bit_mask;
+               ret = adp8860_write(client, reg, reg_val);
+       }
+
+       mutex_unlock(&data->lock);
+       return ret;
+}
+
+static int adp8860_clr_bits(struct i2c_client *client, int reg, uint8_t bit_mask)
+{
+       struct adp8860_bl *data = i2c_get_clientdata(client);
+       uint8_t reg_val;
+       int ret;
+
+       mutex_lock(&data->lock);
+
+       ret = adp8860_read(client, reg, &reg_val);
+
+       if (!ret && (reg_val & bit_mask)) {
+               reg_val &= ~bit_mask;
+               ret = adp8860_write(client, reg, reg_val);
+       }
+
+       mutex_unlock(&data->lock);
+       return ret;
+}
+
+/*
+ * Independent sink / LED
+ */
+#if defined(ADP8860_USE_LEDS)
+static void adp8860_led_work(struct work_struct *work)
+{
+       struct adp8860_led *led = container_of(work, struct adp8860_led, work);
+       adp8860_write(led->client, ADP8860_ISC1 - led->id + 1,
+                        led->new_brightness >> 1);
+}
+
+static void adp8860_led_set(struct led_classdev *led_cdev,
+                          enum led_brightness value)
+{
+       struct adp8860_led *led;
+
+       led = container_of(led_cdev, struct adp8860_led, cdev);
+       led->new_brightness = value;
+       schedule_work(&led->work);
+}
+
+static int adp8860_led_setup(struct adp8860_led *led)
+{
+       struct i2c_client *client = led->client;
+       int ret = 0;
+
+       ret = adp8860_write(client, ADP8860_ISC1 - led->id + 1, 0);
+       ret |= adp8860_set_bits(client, ADP8860_ISCC, 1 << (led->id - 1));
+
+       if (led->id > 4)
+               ret |= adp8860_set_bits(client, ADP8860_ISCT1,
+                               (led->flags & 0x3) << ((led->id - 5) * 2));
+       else
+               ret |= adp8860_set_bits(client, ADP8860_ISCT2,
+                               (led->flags & 0x3) << ((led->id - 1) * 2));
+
+       return ret;
+}
+
+static int __devinit adp8860_led_probe(struct i2c_client *client)
+{
+       struct adp8860_backlight_platform_data *pdata =
+               client->dev.platform_data;
+       struct adp8860_bl *data = i2c_get_clientdata(client);
+       struct adp8860_led *led, *led_dat;
+       struct led_info *cur_led;
+       int ret, i;
+
+       led = kzalloc(sizeof(*led) * pdata->num_leds, GFP_KERNEL);
+       if (led == NULL) {
+               dev_err(&client->dev, "failed to alloc memory\n");
+               return -ENOMEM;
+       }
+
+       ret = adp8860_write(client, ADP8860_ISCFR, pdata->led_fade_law);
+       ret = adp8860_write(client, ADP8860_ISCT1,
+                       (pdata->led_on_time & 0x3) << 6);
+       ret |= adp8860_write(client, ADP8860_ISCF,
+                       FADE_VAL(pdata->led_fade_in, pdata->led_fade_out));
+
+       if (ret) {
+               dev_err(&client->dev, "failed to write\n");
+               goto err_free;
+       }
+
+       for (i = 0; i < pdata->num_leds; ++i) {
+               cur_led = &pdata->leds[i];
+               led_dat = &led[i];
+
+               led_dat->id = cur_led->flags & ADP8860_FLAG_LED_MASK;
+
+               if (led_dat->id > 7 || led_dat->id < 1) {
+                       dev_err(&client->dev, "Invalid LED ID %d\n",
+                               led_dat->id);
+                       goto err;
+               }
+
+               if (pdata->bl_led_assign & (1 << (led_dat->id - 1))) {
+                       dev_err(&client->dev, "LED %d used by Backlight\n",
+                               led_dat->id);
+                       goto err;
+               }
+
+               led_dat->cdev.name = cur_led->name;
+               led_dat->cdev.default_trigger = cur_led->default_trigger;
+               led_dat->cdev.brightness_set = adp8860_led_set;
+               led_dat->cdev.brightness = LED_OFF;
+               led_dat->flags = cur_led->flags >> FLAG_OFFT_SHIFT;
+               led_dat->client = client;
+               led_dat->new_brightness = LED_OFF;
+               INIT_WORK(&led_dat->work, adp8860_led_work);
+
+               ret = led_classdev_register(&client->dev, &led_dat->cdev);
+               if (ret) {
+                       dev_err(&client->dev, "failed to register LED %d\n",
+                               led_dat->id);
+                       goto err;
+               }
+
+               ret = adp8860_led_setup(led_dat);
+               if (ret) {
+                       dev_err(&client->dev, "failed to write\n");
+                       i++;
+                       goto err;
+               }
+       }
+
+       data->led = led;
+
+       return 0;
+
+ err:
+       for (i = i - 1; i >= 0; --i) {
+               led_classdev_unregister(&led[i].cdev);
+               cancel_work_sync(&led[i].work);
+       }
+
+ err_free:
+       kfree(led);
+
+       return ret;
+}
+
+static int __devexit adp8860_led_remove(struct i2c_client *client)
+{
+       struct adp8860_backlight_platform_data *pdata =
+               client->dev.platform_data;
+       struct adp8860_bl *data = i2c_get_clientdata(client);
+       int i;
+
+       for (i = 0; i < pdata->num_leds; i++) {
+               led_classdev_unregister(&data->led[i].cdev);
+               cancel_work_sync(&data->led[i].work);
+       }
+
+       kfree(data->led);
+       return 0;
+}
+#else
+static int __devinit adp8860_led_probe(struct i2c_client *client)
+{
+       return 0;
+}
+
+static int __devexit adp8860_led_remove(struct i2c_client *client)
+{
+       return 0;
+}
+#endif
+
+static int adp8860_bl_set(struct backlight_device *bl, int brightness)
+{
+       struct adp8860_bl *data = bl_get_data(bl);
+       struct i2c_client *client = data->client;
+       int ret = 0;
+
+       if (data->en_ambl_sens) {
+               if ((brightness > 0) && (brightness < ADP8860_MAX_BRIGHTNESS)) {
+                       /* Disable Ambient Light auto adjust */
+                       ret |= adp8860_clr_bits(client, ADP8860_MDCR,
+                                       CMP_AUTOEN);
+                       ret |= adp8860_write(client, ADP8860_BLMX1, brightness);
+               } else {
+                       /*
+                        * MAX_BRIGHTNESS -> Enable Ambient Light auto adjust
+                        * restore daylight l1 sysfs brightness
+                        */
+                       ret |= adp8860_write(client, ADP8860_BLMX1,
+                                        data->cached_daylight_max);
+                       ret |= adp8860_set_bits(client, ADP8860_MDCR,
+                                        CMP_AUTOEN);
+               }
+       } else
+               ret |= adp8860_write(client, ADP8860_BLMX1, brightness);
+
+       if (data->current_brightness && brightness == 0)
+               ret |= adp8860_set_bits(client,
+                               ADP8860_MDCR, DIM_EN);
+       else if (data->current_brightness == 0 && brightness)
+               ret |= adp8860_clr_bits(client,
+                               ADP8860_MDCR, DIM_EN);
+
+       if (!ret)
+               data->current_brightness = brightness;
+
+       return ret;
+}
+
+static int adp8860_bl_update_status(struct backlight_device *bl)
+{
+       int brightness = bl->props.brightness;
+       if (bl->props.power != FB_BLANK_UNBLANK)
+               brightness = 0;
+
+       if (bl->props.fb_blank != FB_BLANK_UNBLANK)
+               brightness = 0;
+
+       return adp8860_bl_set(bl, brightness);
+}
+
+static int adp8860_bl_get_brightness(struct backlight_device *bl)
+{
+       struct adp8860_bl *data = bl_get_data(bl);
+
+       return data->current_brightness;
+}
+
+static const struct backlight_ops adp8860_bl_ops = {
+       .update_status  = adp8860_bl_update_status,
+       .get_brightness = adp8860_bl_get_brightness,
+};
+
+static int adp8860_bl_setup(struct backlight_device *bl)
+{
+       struct adp8860_bl *data = bl_get_data(bl);
+       struct i2c_client *client = data->client;
+       struct adp8860_backlight_platform_data *pdata = data->pdata;
+       int ret = 0;
+
+       ret |= adp8860_write(client, ADP8860_BLSEN, ~pdata->bl_led_assign);
+       ret |= adp8860_write(client, ADP8860_BLMX1, pdata->l1_daylight_max);
+       ret |= adp8860_write(client, ADP8860_BLDM1, pdata->l1_daylight_dim);
+
+       if (data->en_ambl_sens) {
+               data->cached_daylight_max = pdata->l1_daylight_max;
+               ret |= adp8860_write(client, ADP8860_BLMX2,
+                                               pdata->l2_office_max);
+               ret |= adp8860_write(client, ADP8860_BLDM2,
+                                               pdata->l2_office_dim);
+               ret |= adp8860_write(client, ADP8860_BLMX3,
+                                               pdata->l3_dark_max);
+               ret |= adp8860_write(client, ADP8860_BLDM3,
+                                               pdata->l3_dark_dim);
+
+               ret |= adp8860_write(client, ADP8860_L2_TRP, pdata->l2_trip);
+               ret |= adp8860_write(client, ADP8860_L2_HYS, pdata->l2_hyst);
+               ret |= adp8860_write(client, ADP8860_L3_TRP, pdata->l3_trip);
+               ret |= adp8860_write(client, ADP8860_L3_HYS, pdata->l3_hyst);
+               ret |= adp8860_write(client, ADP8860_CCFG, L2_EN | L3_EN |
+                                               ALS_CCFG_VAL(pdata->abml_filt));
+       }
+
+       ret |= adp8860_write(client, ADP8860_CFGR,
+                       BL_CFGR_VAL(pdata->bl_fade_law, 0));
+
+       ret |= adp8860_write(client, ADP8860_BLFR, FADE_VAL(pdata->bl_fade_in,
+                       pdata->bl_fade_out));
+
+       ret |= adp8860_set_bits(client, ADP8860_MDCR, BLEN | DIM_EN | NSTBY |
+                       (data->gdwn_dis ? GDWN_DIS : 0));
+
+       return ret;
+}
+
+static ssize_t adp8860_show(struct device *dev, char *buf, int reg)
+{
+       struct adp8860_bl *data = dev_get_drvdata(dev);
+       int error;
+       uint8_t reg_val;
+
+       mutex_lock(&data->lock);
+       error = adp8860_read(data->client, reg, &reg_val);
+       mutex_unlock(&data->lock);
+
+       if (error < 0)
+               return error;
+
+       return sprintf(buf, "%u\n", reg_val);
+}
+
+static ssize_t adp8860_store(struct device *dev, const char *buf,
+                        size_t count, int reg)
+{
+       struct adp8860_bl *data = dev_get_drvdata(dev);
+       unsigned long val;
+       int ret;
+
+       ret = strict_strtoul(buf, 10, &val);
+       if (ret)
+               return ret;
+
+       mutex_lock(&data->lock);
+       adp8860_write(data->client, reg, val);
+       mutex_unlock(&data->lock);
+
+       return count;
+}
+
+static ssize_t adp8860_bl_l3_dark_max_show(struct device *dev,
+                                    struct device_attribute *attr, char *buf)
+{
+       return adp8860_show(dev, buf, ADP8860_BLMX3);
+}
+
+static ssize_t adp8860_bl_l3_dark_max_store(struct device *dev,
+               struct device_attribute *attr, const char *buf, size_t count)
+{
+       return adp8860_store(dev, buf, count, ADP8860_BLMX3);
+}
+
+static DEVICE_ATTR(l3_dark_max, 0664, adp8860_bl_l3_dark_max_show,
+                       adp8860_bl_l3_dark_max_store);
+
+static ssize_t adp8860_bl_l2_office_max_show(struct device *dev,
+               struct device_attribute *attr, char *buf)
+{
+       return adp8860_show(dev, buf, ADP8860_BLMX2);
+}
+
+static ssize_t adp8860_bl_l2_office_max_store(struct device *dev,
+               struct device_attribute *attr, const char *buf, size_t count)
+{
+       return adp8860_store(dev, buf, count, ADP8860_BLMX2);
+}
+static DEVICE_ATTR(l2_office_max, 0664, adp8860_bl_l2_office_max_show,
+                       adp8860_bl_l2_office_max_store);
+
+static ssize_t adp8860_bl_l1_daylight_max_show(struct device *dev,
+                       struct device_attribute *attr, char *buf)
+{
+       return adp8860_show(dev, buf, ADP8860_BLMX1);
+}
+
+static ssize_t adp8860_bl_l1_daylight_max_store(struct device *dev,
+               struct device_attribute *attr, const char *buf, size_t count)
+{
+       struct adp8860_bl *data = dev_get_drvdata(dev);
+
+       strict_strtoul(buf, 10, &data->cached_daylight_max);
+       return adp8860_store(dev, buf, count, ADP8860_BLMX1);
+}
+static DEVICE_ATTR(l1_daylight_max, 0664, adp8860_bl_l1_daylight_max_show,
+                       adp8860_bl_l1_daylight_max_store);
+
+static ssize_t adp8860_bl_l3_dark_dim_show(struct device *dev,
+                       struct device_attribute *attr, char *buf)
+{
+       return adp8860_show(dev, buf, ADP8860_BLDM3);
+}
+
+static ssize_t adp8860_bl_l3_dark_dim_store(struct device *dev,
+                                    struct device_attribute *attr,
+                                    const char *buf, size_t count)
+{
+       return adp8860_store(dev, buf, count, ADP8860_BLDM3);
+}
+static DEVICE_ATTR(l3_dark_dim, 0664, adp8860_bl_l3_dark_dim_show,
+                       adp8860_bl_l3_dark_dim_store);
+
+static ssize_t adp8860_bl_l2_office_dim_show(struct device *dev,
+                       struct device_attribute *attr, char *buf)
+{
+       return adp8860_show(dev, buf, ADP8860_BLDM2);
+}
+
+static ssize_t adp8860_bl_l2_office_dim_store(struct device *dev,
+                                    struct device_attribute *attr,
+                                    const char *buf, size_t count)
+{
+       return adp8860_store(dev, buf, count, ADP8860_BLDM2);
+}
+static DEVICE_ATTR(l2_office_dim, 0664, adp8860_bl_l2_office_dim_show,
+                       adp8860_bl_l2_office_dim_store);
+
+static ssize_t adp8860_bl_l1_daylight_dim_show(struct device *dev,
+                                    struct device_attribute *attr, char *buf)
+{
+       return adp8860_show(dev, buf, ADP8860_BLDM1);
+}
+
+static ssize_t adp8860_bl_l1_daylight_dim_store(struct device *dev,
+                                    struct device_attribute *attr,
+                                    const char *buf, size_t count)
+{
+       return adp8860_store(dev, buf, count, ADP8860_BLDM1);
+}
+static DEVICE_ATTR(l1_daylight_dim, 0664, adp8860_bl_l1_daylight_dim_show,
+                       adp8860_bl_l1_daylight_dim_store);
+
+#ifdef ADP8860_EXT_FEATURES
+static ssize_t adp8860_bl_ambient_light_level_show(struct device *dev,
+                                    struct device_attribute *attr, char *buf)
+{
+       struct adp8860_bl *data = dev_get_drvdata(dev);
+       int error;
+       uint8_t reg_val;
+       uint16_t ret_val;
+
+       mutex_lock(&data->lock);
+       error = adp8860_read(data->client, ADP8860_PH1LEVL, &reg_val);
+       ret_val = reg_val;
+       error |= adp8860_read(data->client, ADP8860_PH1LEVH, &reg_val);
+       mutex_unlock(&data->lock);
+
+       if (error < 0)
+               return error;
+
+       /* Return 13-bit conversion value for the first light sensor */
+       ret_val += (reg_val & 0x1F) << 8;
+
+       return sprintf(buf, "%u\n", ret_val);
+}
+static DEVICE_ATTR(ambient_light_level, 0444,
+               adp8860_bl_ambient_light_level_show, NULL);
+
+static ssize_t adp8860_bl_ambient_light_zone_show(struct device *dev,
+                                    struct device_attribute *attr, char *buf)
+{
+       struct adp8860_bl *data = dev_get_drvdata(dev);
+       int error;
+       uint8_t reg_val;
+
+       mutex_lock(&data->lock);
+       error = adp8860_read(data->client, ADP8860_CFGR, &reg_val);
+       mutex_unlock(&data->lock);
+
+       if (error < 0)
+               return error;
+
+       return sprintf(buf, "%u\n",
+               ((reg_val >> CFGR_BLV_SHIFT) & CFGR_BLV_MASK) + 1);
+}
+
+static ssize_t adp8860_bl_ambient_light_zone_store(struct device *dev,
+                                    struct device_attribute *attr,
+                                    const char *buf, size_t count)
+{
+       struct adp8860_bl *data = dev_get_drvdata(dev);
+       unsigned long val;
+       uint8_t reg_val;
+       int ret;
+
+       ret = strict_strtoul(buf, 10, &val);
+       if (ret)
+               return ret;
+
+       if (val == 0) {
+               /* Enable automatic ambient light sensing */
+               adp8860_set_bits(data->client, ADP8860_MDCR, CMP_AUTOEN);
+       } else if ((val > 0) && (val < 6)) {
+               /* Disable automatic ambient light sensing */
+               adp8860_clr_bits(data->client, ADP8860_MDCR, CMP_AUTOEN);
+
+               /* Set user supplied ambient light zone */
+               mutex_lock(&data->lock);
+               adp8860_read(data->client, ADP8860_CFGR, &reg_val);
+               reg_val &= ~(CFGR_BLV_MASK << CFGR_BLV_SHIFT);
+               reg_val |= val << CFGR_BLV_SHIFT;
+               adp8860_write(data->client, ADP8860_CFGR, reg_val);
+               mutex_unlock(&data->lock);
+       }
+
+       return count;
+}
+static DEVICE_ATTR(ambient_light_zone, 0664,
+               adp8860_bl_ambient_light_zone_show,
+               adp8860_bl_ambient_light_zone_store);
+#endif
+
+static struct attribute *adp8860_bl_attributes[] = {
+       &dev_attr_l3_dark_max.attr,
+       &dev_attr_l3_dark_dim.attr,
+       &dev_attr_l2_office_max.attr,
+       &dev_attr_l2_office_dim.attr,
+       &dev_attr_l1_daylight_max.attr,
+       &dev_attr_l1_daylight_dim.attr,
+#ifdef ADP8860_EXT_FEATURES
+       &dev_attr_ambient_light_level.attr,
+       &dev_attr_ambient_light_zone.attr,
+#endif
+       NULL
+};
+
+static const struct attribute_group adp8860_bl_attr_group = {
+       .attrs = adp8860_bl_attributes,
+};
+
+static int __devinit adp8860_probe(struct i2c_client *client,
+                                       const struct i2c_device_id *id)
+{
+       struct backlight_device *bl;
+       struct adp8860_bl *data;
+       struct adp8860_backlight_platform_data *pdata =
+               client->dev.platform_data;
+       struct backlight_properties props;
+       uint8_t reg_val;
+       int ret;
+
+       if (!i2c_check_functionality(client->adapter,
+                                       I2C_FUNC_SMBUS_BYTE_DATA)) {
+               dev_err(&client->dev, "SMBUS Byte Data not Supported\n");
+               return -EIO;
+       }
+
+       if (!pdata) {
+               dev_err(&client->dev, "no platform data?\n");
+               return -EINVAL;
+       }
+
+       data = kzalloc(sizeof(*data), GFP_KERNEL);
+       if (data == NULL)
+               return -ENOMEM;
+
+       ret = adp8860_read(client, ADP8860_MFDVID, &reg_val);
+       if (ret < 0)
+               goto out2;
+
+       switch (ADP8860_MANID(reg_val)) {
+       case ADP8863_MANUFID:
+               data->gdwn_dis = !!pdata->gdwn_dis;
+       case ADP8860_MANUFID:
+               data->en_ambl_sens = !!pdata->en_ambl_sens;
+               break;
+       case ADP8861_MANUFID:
+               data->gdwn_dis = !!pdata->gdwn_dis;
+               break;
+       default:
+               dev_err(&client->dev, "failed to probe\n");
+               ret = -ENODEV;
+               goto out2;
+       }
+
+       /* It's confirmed that the DEVID field is actually a REVID */
+
+       data->revid = ADP8860_DEVID(reg_val);
+       data->client = client;
+       data->pdata = pdata;
+       data->id = id->driver_data;
+       data->current_brightness = 0;
+       i2c_set_clientdata(client, data);
+
+       memset(&props, 0, sizeof(props));
+       props.max_brightness = ADP8860_MAX_BRIGHTNESS;
+
+       mutex_init(&data->lock);
+
+       bl = backlight_device_register(dev_driver_string(&client->dev),
+                       &client->dev, data, &adp8860_bl_ops, &props);
+       if (IS_ERR(bl)) {
+               dev_err(&client->dev, "failed to register backlight\n");
+               ret = PTR_ERR(bl);
+               goto out2;
+       }
+
+       bl->props.max_brightness =
+               bl->props.brightness = ADP8860_MAX_BRIGHTNESS;
+
+       data->bl = bl;
+
+       if (data->en_ambl_sens)
+               ret = sysfs_create_group(&bl->dev.kobj,
+                       &adp8860_bl_attr_group);
+
+       if (ret) {
+               dev_err(&client->dev, "failed to register sysfs\n");
+               goto out1;
+       }
+
+       ret = adp8860_bl_setup(bl);
+       if (ret) {
+               ret = -EIO;
+               goto out;
+       }
+
+       backlight_update_status(bl);
+
+       dev_info(&client->dev, "%s Rev.%d Backlight\n",
+               client->name, data->revid);
+
+       if (pdata->num_leds)
+               adp8860_led_probe(client);
+
+       return 0;
+
+out:
+       if (data->en_ambl_sens)
+               sysfs_remove_group(&data->bl->dev.kobj,
+                       &adp8860_bl_attr_group);
+out1:
+       backlight_device_unregister(bl);
+out2:
+       i2c_set_clientdata(client, NULL);
+       kfree(data);
+
+       return ret;
+}
+
+static int __devexit adp8860_remove(struct i2c_client *client)
+{
+       struct adp8860_bl *data = i2c_get_clientdata(client);
+
+       adp8860_clr_bits(client, ADP8860_MDCR, NSTBY);
+
+       if (data->led)
+               adp8860_led_remove(client);
+
+       if (data->en_ambl_sens)
+               sysfs_remove_group(&data->bl->dev.kobj,
+                       &adp8860_bl_attr_group);
+
+       backlight_device_unregister(data->bl);
+       i2c_set_clientdata(client, NULL);
+       kfree(data);
+
+       return 0;
+}
+
+#ifdef CONFIG_PM
+static int adp8860_i2c_suspend(struct i2c_client *client, pm_message_t message)
+{
+       adp8860_clr_bits(client, ADP8860_MDCR, NSTBY);
+
+       return 0;
+}
+
+static int adp8860_i2c_resume(struct i2c_client *client)
+{
+       adp8860_set_bits(client, ADP8860_MDCR, NSTBY);
+
+       return 0;
+}
+#else
+#define adp8860_i2c_suspend NULL
+#define adp8860_i2c_resume NULL
+#endif
+
+static const struct i2c_device_id adp8860_id[] = {
+       { "adp8860", adp8860 },
+       { "adp8861", adp8861 },
+       { "adp8863", adp8863 },
+       { }
+};
+MODULE_DEVICE_TABLE(i2c, adp8860_id);
+
+static struct i2c_driver adp8860_driver = {
+       .driver = {
+               .name = KBUILD_MODNAME,
+       },
+       .probe    = adp8860_probe,
+       .remove   = __devexit_p(adp8860_remove),
+       .suspend = adp8860_i2c_suspend,
+       .resume  = adp8860_i2c_resume,
+       .id_table = adp8860_id,
+};
+
+static int __init adp8860_init(void)
+{
+       return i2c_add_driver(&adp8860_driver);
+}
+module_init(adp8860_init);
+
+static void __exit adp8860_exit(void)
+{
+       i2c_del_driver(&adp8860_driver);
+}
+module_exit(adp8860_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
+MODULE_DESCRIPTION("ADP8860 Backlight driver");
+MODULE_ALIAS("i2c:adp8860-backlight");
index 7f4a7c3..fe9af12 100644 (file)
@@ -107,8 +107,8 @@ static int __devinit adx_backlight_probe(struct platform_device *pdev)
        props.max_brightness = 0xff;
        bldev = backlight_device_register(dev_name(&pdev->dev), &pdev->dev,
                                          bl, &adx_backlight_ops, &props);
-       if (!bldev) {
-               ret = -ENOMEM;
+       if (IS_ERR(bldev)) {
+               ret = PTR_ERR(bldev);
                goto out;
        }
 
diff --git a/drivers/video/backlight/ep93xx_bl.c b/drivers/video/backlight/ep93xx_bl.c
new file mode 100644 (file)
index 0000000..b0cc491
--- /dev/null
@@ -0,0 +1,160 @@
+/*
+ * Driver for the Cirrus EP93xx lcd backlight
+ *
+ * Copyright (c) 2010 H Hartley Sweeten <hsweeten@visionengravers.com>
+ *
+ * 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 driver controls the pulse width modulated brightness control output,
+ * BRIGHT, on the Cirrus EP9307, EP9312, and EP9315 processors.
+ */
+
+
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/fb.h>
+#include <linux/backlight.h>
+
+#include <mach/hardware.h>
+
+#define EP93XX_RASTER_REG(x)           (EP93XX_RASTER_BASE + (x))
+#define EP93XX_RASTER_BRIGHTNESS       EP93XX_RASTER_REG(0x20)
+
+#define EP93XX_MAX_COUNT               255
+#define EP93XX_MAX_BRIGHT              255
+#define EP93XX_DEF_BRIGHT              128
+
+struct ep93xxbl {
+       void __iomem *mmio;
+       int brightness;
+};
+
+static int ep93xxbl_set(struct backlight_device *bl, int brightness)
+{
+       struct ep93xxbl *ep93xxbl = bl_get_data(bl);
+
+       __raw_writel((brightness << 8) | EP93XX_MAX_COUNT, ep93xxbl->mmio);
+
+       ep93xxbl->brightness = brightness;
+
+       return 0;
+}
+
+static int ep93xxbl_update_status(struct backlight_device *bl)
+{
+       int brightness = bl->props.brightness;
+
+       if (bl->props.power != FB_BLANK_UNBLANK ||
+           bl->props.fb_blank != FB_BLANK_UNBLANK)
+               brightness = 0;
+
+       return ep93xxbl_set(bl, brightness);
+}
+
+static int ep93xxbl_get_brightness(struct backlight_device *bl)
+{
+       struct ep93xxbl *ep93xxbl = bl_get_data(bl);
+
+       return ep93xxbl->brightness;
+}
+
+static const struct backlight_ops ep93xxbl_ops = {
+       .update_status  = ep93xxbl_update_status,
+       .get_brightness = ep93xxbl_get_brightness,
+};
+
+static int __init ep93xxbl_probe(struct platform_device *dev)
+{
+       struct ep93xxbl *ep93xxbl;
+       struct backlight_device *bl;
+       struct backlight_properties props;
+
+       ep93xxbl = devm_kzalloc(&dev->dev, sizeof(*ep93xxbl), GFP_KERNEL);
+       if (!ep93xxbl)
+               return -ENOMEM;
+
+       /*
+        * This register is located in the range already ioremap'ed by
+        * the framebuffer driver.  A MFD driver seems a bit of overkill
+        * to handle this so use the static I/O mapping; this address
+        * is already virtual.
+        *
+        * NOTE: No locking is required; the framebuffer does not touch
+        * this register.
+        */
+       ep93xxbl->mmio = EP93XX_RASTER_BRIGHTNESS;
+
+       memset(&props, 0, sizeof(struct backlight_properties));
+       props.max_brightness = EP93XX_MAX_BRIGHT;
+       bl = backlight_device_register(dev->name, &dev->dev, ep93xxbl,
+                                      &ep93xxbl_ops, &props);
+       if (IS_ERR(bl))
+               return PTR_ERR(bl);
+
+       bl->props.brightness = EP93XX_DEF_BRIGHT;
+
+       platform_set_drvdata(dev, bl);
+
+       ep93xxbl_update_status(bl);
+
+       return 0;
+}
+
+static int ep93xxbl_remove(struct platform_device *dev)
+{
+       struct backlight_device *bl = platform_get_drvdata(dev);
+
+       backlight_device_unregister(bl);
+       platform_set_drvdata(dev, NULL);
+       return 0;
+}
+
+#ifdef CONFIG_PM
+static int ep93xxbl_suspend(struct platform_device *dev, pm_message_t state)
+{
+       struct backlight_device *bl = platform_get_drvdata(dev);
+
+       return ep93xxbl_set(bl, 0);
+}
+
+static int ep93xxbl_resume(struct platform_device *dev)
+{
+       struct backlight_device *bl = platform_get_drvdata(dev);
+
+       backlight_update_status(bl);
+       return 0;
+}
+#else
+#define ep93xxbl_suspend       NULL
+#define ep93xxbl_resume                NULL
+#endif
+
+static struct platform_driver ep93xxbl_driver = {
+       .driver         = {
+               .name   = "ep93xx-bl",
+               .owner  = THIS_MODULE,
+       },
+       .probe          = ep93xxbl_probe,
+       .remove         = __devexit_p(ep93xxbl_remove),
+       .suspend        = ep93xxbl_suspend,
+       .resume         = ep93xxbl_resume,
+};
+
+static int __init ep93xxbl_init(void)
+{
+       return platform_driver_register(&ep93xxbl_driver);
+}
+module_init(ep93xxbl_init);
+
+static void __exit ep93xxbl_exit(void)
+{
+       platform_driver_unregister(&ep93xxbl_driver);
+}
+module_exit(ep93xxbl_exit);
+
+MODULE_DESCRIPTION("EP93xx Backlight Driver");
+MODULE_AUTHOR("H Hartley Sweeten <hsweeten@visionengravers.com>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:ep93xx-bl");
index bcdb12c..9093ef0 100644 (file)
@@ -125,8 +125,7 @@ static int __devinit l4f00242t03_probe(struct spi_device *spi)
 
        if (priv == NULL) {
                dev_err(&spi->dev, "No memory for this device.\n");
-               ret = -ENOMEM;
-               goto err;
+               return -ENOMEM;
        }
 
        dev_set_drvdata(&spi->dev, priv);
@@ -139,7 +138,7 @@ static int __devinit l4f00242t03_probe(struct spi_device *spi)
        if (ret) {
                dev_err(&spi->dev,
                        "Unable to get the lcd l4f00242t03 reset gpio.\n");
-               return ret;
+               goto err;
        }
 
        ret = gpio_direction_output(pdata->reset_gpio, 1);
@@ -151,7 +150,7 @@ static int __devinit l4f00242t03_probe(struct spi_device *spi)
        if (ret) {
                dev_err(&spi->dev,
                        "Unable to get the lcd l4f00242t03 data en gpio.\n");
-               return ret;
+               goto err2;
        }
 
        ret = gpio_direction_output(pdata->data_enable_gpio, 0);
@@ -222,9 +221,9 @@ static int __devexit l4f00242t03_remove(struct spi_device *spi)
        gpio_free(pdata->reset_gpio);
 
        if (priv->io_reg)
-               regulator_put(priv->core_reg);
-       if (priv->core_reg)
                regulator_put(priv->io_reg);
+       if (priv->core_reg)
+               regulator_put(priv->core_reg);
 
        kfree(priv);
 
index b5accc9..b2b2c7b 100644 (file)
@@ -162,6 +162,7 @@ static int __devinit max8925_backlight_probe(struct platform_device *pdev)
        backlight_update_status(bl);
        return 0;
 out:
+       backlight_device_unregister(bl);
        kfree(data);
        return ret;
 }
index 1b5d3fe..9fb533f 100644 (file)
@@ -141,7 +141,7 @@ static const struct dmi_system_id __initdata mbp_device_table[] = {
                .callback       = mbp_dmi_match,
                .ident          = "MacBook 1,1",
                .matches        = {
-                       DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."),
+                       DMI_MATCH(DMI_SYS_VENDOR, "Apple Computer, Inc."),
                        DMI_MATCH(DMI_PRODUCT_NAME, "MacBook1,1"),
                },
                .driver_data    = (void *)&intel_chipset_data,
@@ -184,6 +184,42 @@ static const struct dmi_system_id __initdata mbp_device_table[] = {
        },
        {
                .callback       = mbp_dmi_match,
+               .ident          = "MacBookPro 1,1",
+               .matches        = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro1,1"),
+               },
+               .driver_data    = (void *)&intel_chipset_data,
+       },
+       {
+               .callback       = mbp_dmi_match,
+               .ident          = "MacBookPro 1,2",
+               .matches        = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro1,2"),
+               },
+               .driver_data    = (void *)&intel_chipset_data,
+       },
+       {
+               .callback       = mbp_dmi_match,
+               .ident          = "MacBookPro 2,1",
+               .matches        = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro2,1"),
+               },
+               .driver_data    = (void *)&intel_chipset_data,
+       },
+       {
+               .callback       = mbp_dmi_match,
+               .ident          = "MacBookPro 2,2",
+               .matches        = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro2,2"),
+               },
+               .driver_data    = (void *)&intel_chipset_data,
+       },
+       {
+               .callback       = mbp_dmi_match,
                .ident          = "MacBookPro 3,1",
                .matches        = {
                        DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."),
@@ -238,6 +274,15 @@ static const struct dmi_system_id __initdata mbp_device_table[] = {
        },
        {
                .callback       = mbp_dmi_match,
+               .ident          = "MacBook 6,1",
+               .matches        = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "MacBook6,1"),
+               },
+               .driver_data    = (void *)&nvidia_chipset_data,
+       },
+       {
+               .callback       = mbp_dmi_match,
                .ident          = "MacBookAir 2,1",
                .matches        = {
                        DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."),
diff --git a/drivers/video/backlight/pcf50633-backlight.c b/drivers/video/backlight/pcf50633-backlight.c
new file mode 100644 (file)
index 0000000..3c424f7
--- /dev/null
@@ -0,0 +1,190 @@
+/*
+ *  Copyright (C) 2009-2010, Lars-Peter Clausen <lars@metafoo.de>
+ *      PCF50633 backlight device 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.
+ *
+ *  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.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+
+#include <linux/backlight.h>
+#include <linux/fb.h>
+
+#include <linux/mfd/pcf50633/core.h>
+#include <linux/mfd/pcf50633/backlight.h>
+
+struct pcf50633_bl {
+       struct pcf50633 *pcf;
+       struct backlight_device *bl;
+
+       unsigned int brightness;
+       unsigned int brightness_limit;
+};
+
+/*
+ * pcf50633_bl_set_brightness_limit
+ *
+ * Update the brightness limit for the pc50633 backlight. The actual brightness
+ * will not go above the limit. This is useful to limit power drain for example
+ * on low battery.
+ *
+ * @dev: Pointer to a pcf50633 device
+ * @limit: The brightness limit. Valid values are 0-63
+ */
+int pcf50633_bl_set_brightness_limit(struct pcf50633 *pcf, unsigned int limit)
+{
+       struct pcf50633_bl *pcf_bl = platform_get_drvdata(pcf->bl_pdev);
+
+       if (!pcf_bl)
+               return -ENODEV;
+
+       pcf_bl->brightness_limit = limit & 0x3f;
+       backlight_update_status(pcf_bl->bl);
+
+    return 0;
+}
+
+static int pcf50633_bl_update_status(struct backlight_device *bl)
+{
+       struct pcf50633_bl *pcf_bl = bl_get_data(bl);
+       unsigned int new_brightness;
+
+
+       if (bl->props.state & (BL_CORE_SUSPENDED | BL_CORE_FBBLANK) ||
+               bl->props.power != FB_BLANK_UNBLANK)
+               new_brightness = 0;
+       else if (bl->props.brightness < pcf_bl->brightness_limit)
+               new_brightness = bl->props.brightness;
+       else
+               new_brightness = pcf_bl->brightness_limit;
+
+
+       if (pcf_bl->brightness == new_brightness)
+               return 0;
+
+       if (new_brightness) {
+               pcf50633_reg_write(pcf_bl->pcf, PCF50633_REG_LEDOUT,
+                                       new_brightness);
+               if (!pcf_bl->brightness)
+                       pcf50633_reg_write(pcf_bl->pcf, PCF50633_REG_LEDENA, 1);
+       } else {
+               pcf50633_reg_write(pcf_bl->pcf, PCF50633_REG_LEDENA, 0);
+       }
+
+       pcf_bl->brightness = new_brightness;
+
+       return 0;
+}
+
+static int pcf50633_bl_get_brightness(struct backlight_device *bl)
+{
+       struct pcf50633_bl *pcf_bl = bl_get_data(bl);
+       return pcf_bl->brightness;
+}
+
+static const struct backlight_ops pcf50633_bl_ops = {
+       .get_brightness = pcf50633_bl_get_brightness,
+       .update_status  = pcf50633_bl_update_status,
+       .options        = BL_CORE_SUSPENDRESUME,
+};
+
+static int __devinit pcf50633_bl_probe(struct platform_device *pdev)
+{
+       int ret;
+       struct pcf50633_bl *pcf_bl;
+       struct device *parent = pdev->dev.parent;
+       struct pcf50633_platform_data *pcf50633_data = parent->platform_data;
+       struct pcf50633_bl_platform_data *pdata = pcf50633_data->backlight_data;
+       struct backlight_properties bl_props;
+
+       pcf_bl = kzalloc(sizeof(*pcf_bl), GFP_KERNEL);
+       if (!pcf_bl)
+               return -ENOMEM;
+
+       bl_props.max_brightness = 0x3f;
+       bl_props.power = FB_BLANK_UNBLANK;
+
+       if (pdata) {
+               bl_props.brightness = pdata->default_brightness;
+               pcf_bl->brightness_limit = pdata->default_brightness_limit;
+       } else {
+               bl_props.brightness = 0x3f;
+               pcf_bl->brightness_limit = 0x3f;
+       }
+
+       pcf_bl->pcf = dev_to_pcf50633(pdev->dev.parent);
+
+       pcf_bl->bl = backlight_device_register(pdev->name, &pdev->dev, pcf_bl,
+                                               &pcf50633_bl_ops, &bl_props);
+
+       if (IS_ERR(pcf_bl->bl)) {
+               ret = PTR_ERR(pcf_bl->bl);
+               goto err_free;
+       }
+
+       platform_set_drvdata(pdev, pcf_bl);
+
+       pcf50633_reg_write(pcf_bl->pcf, PCF50633_REG_LEDDIM, pdata->ramp_time);
+
+       /* Should be different from bl_props.brightness, so we do not exit
+        * update_status early the first time it's called */
+       pcf_bl->brightness = pcf_bl->bl->props.brightness + 1;
+
+       backlight_update_status(pcf_bl->bl);
+
+       return 0;
+
+err_free:
+       kfree(pcf_bl);
+
+       return ret;
+}
+
+static int __devexit pcf50633_bl_remove(struct platform_device *pdev)
+{
+       struct pcf50633_bl *pcf_bl = platform_get_drvdata(pdev);
+
+       backlight_device_unregister(pcf_bl->bl);
+
+       platform_set_drvdata(pdev, NULL);
+
+       kfree(pcf_bl);
+
+       return 0;
+}
+
+static struct platform_driver pcf50633_bl_driver = {
+       .probe =        pcf50633_bl_probe,
+       .remove =       __devexit_p(pcf50633_bl_remove),
+       .driver = {
+               .name = "pcf50633-backlight",
+       },
+};
+
+static int __init pcf50633_bl_init(void)
+{
+       return platform_driver_register(&pcf50633_bl_driver);
+}
+module_init(pcf50633_bl_init);
+
+static void __exit pcf50633_bl_exit(void)
+{
+       platform_driver_unregister(&pcf50633_bl_driver);
+}
+module_exit(pcf50633_bl_exit);
+
+MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
+MODULE_DESCRIPTION("PCF50633 backlight driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:pcf50633-backlight");
diff --git a/drivers/video/backlight/s6e63m0.c b/drivers/video/backlight/s6e63m0.c
new file mode 100644 (file)
index 0000000..a3128c9
--- /dev/null
@@ -0,0 +1,920 @@
+/*
+ * S6E63M0 AMOLED LCD panel driver.
+ *
+ * Author: InKi Dae  <inki.dae@samsung.com>
+ *
+ * Derived from drivers/video/omap/lcd-apollon.c
+ *
+ * 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.
+ */
+
+#include <linux/wait.h>
+#include <linux/fb.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/spi/spi.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/lcd.h>
+#include <linux/backlight.h>
+
+#include "s6e63m0_gamma.h"
+
+#define SLEEPMSEC              0x1000
+#define ENDDEF                 0x2000
+#define        DEFMASK                 0xFF00
+#define COMMAND_ONLY           0xFE
+#define DATA_ONLY              0xFF
+
+#define MIN_BRIGHTNESS         0
+#define MAX_BRIGHTNESS         10
+
+#define POWER_IS_ON(pwr)       ((pwr) <= FB_BLANK_NORMAL)
+
+struct s6e63m0 {
+       struct device                   *dev;
+       struct spi_device               *spi;
+       unsigned int                    power;
+       unsigned int                    current_brightness;
+       unsigned int                    gamma_mode;
+       unsigned int                    gamma_table_count;
+       struct lcd_device               *ld;
+       struct backlight_device         *bd;
+       struct lcd_platform_data        *lcd_pd;
+};
+
+static const unsigned short SEQ_PANEL_CONDITION_SET[] = {
+       0xF8, 0x01,
+       DATA_ONLY, 0x27,
+       DATA_ONLY, 0x27,
+       DATA_ONLY, 0x07,
+       DATA_ONLY, 0x07,
+       DATA_ONLY, 0x54,
+       DATA_ONLY, 0x9f,
+       DATA_ONLY, 0x63,
+       DATA_ONLY, 0x86,
+       DATA_ONLY, 0x1a,
+       DATA_ONLY, 0x33,
+       DATA_ONLY, 0x0d,
+       DATA_ONLY, 0x00,
+       DATA_ONLY, 0x00,
+
+       ENDDEF, 0x0000
+};
+
+static const unsigned short SEQ_DISPLAY_CONDITION_SET[] = {
+       0xf2, 0x02,
+       DATA_ONLY, 0x03,
+       DATA_ONLY, 0x1c,
+       DATA_ONLY, 0x10,
+       DATA_ONLY, 0x10,
+
+       0xf7, 0x03,
+       DATA_ONLY, 0x00,
+       DATA_ONLY, 0x00,
+
+       ENDDEF, 0x0000
+};
+
+static const unsigned short SEQ_GAMMA_SETTING[] = {
+       0xfa, 0x00,
+       DATA_ONLY, 0x18,
+       DATA_ONLY, 0x08,
+       DATA_ONLY, 0x24,
+       DATA_ONLY, 0x64,
+       DATA_ONLY, 0x56,
+       DATA_ONLY, 0x33,
+       DATA_ONLY, 0xb6,
+       DATA_ONLY, 0xba,
+       DATA_ONLY, 0xa8,
+       DATA_ONLY, 0xac,
+       DATA_ONLY, 0xb1,
+       DATA_ONLY, 0x9d,
+       DATA_ONLY, 0xc1,
+       DATA_ONLY, 0xc1,
+       DATA_ONLY, 0xb7,
+       DATA_ONLY, 0x00,
+       DATA_ONLY, 0x9c,
+       DATA_ONLY, 0x00,
+       DATA_ONLY, 0x9f,
+       DATA_ONLY, 0x00,
+       DATA_ONLY, 0xd6,
+
+       0xfa, 0x01,
+
+       ENDDEF, 0x0000
+};
+
+static const unsigned short SEQ_ETC_CONDITION_SET[] = {
+       0xf6, 0x00,
+       DATA_ONLY, 0x8c,
+       DATA_ONLY, 0x07,
+
+       0xb3, 0xc,
+
+       0xb5, 0x2c,
+       DATA_ONLY, 0x12,
+       DATA_ONLY, 0x0c,
+       DATA_ONLY, 0x0a,
+       DATA_ONLY, 0x10,
+       DATA_ONLY, 0x0e,
+       DATA_ONLY, 0x17,
+       DATA_ONLY, 0x13,
+       DATA_ONLY, 0x1f,
+       DATA_ONLY, 0x1a,
+       DATA_ONLY, 0x2a,
+       DATA_ONLY, 0x24,
+       DATA_ONLY, 0x1f,
+       DATA_ONLY, 0x1b,
+       DATA_ONLY, 0x1a,
+       DATA_ONLY, 0x17,
+
+       DATA_ONLY, 0x2b,
+       DATA_ONLY, 0x26,
+       DATA_ONLY, 0x22,
+       DATA_ONLY, 0x20,
+       DATA_ONLY, 0x3a,
+       DATA_ONLY, 0x34,
+       DATA_ONLY, 0x30,
+       DATA_ONLY, 0x2c,
+       DATA_ONLY, 0x29,
+       DATA_ONLY, 0x26,
+       DATA_ONLY, 0x25,
+       DATA_ONLY, 0x23,
+       DATA_ONLY, 0x21,
+       DATA_ONLY, 0x20,
+       DATA_ONLY, 0x1e,
+       DATA_ONLY, 0x1e,
+
+       0xb6, 0x00,
+       DATA_ONLY, 0x00,
+       DATA_ONLY, 0x11,
+       DATA_ONLY, 0x22,
+       DATA_ONLY, 0x33,
+       DATA_ONLY, 0x44,
+       DATA_ONLY, 0x44,
+       DATA_ONLY, 0x44,
+
+       DATA_ONLY, 0x55,
+       DATA_ONLY, 0x55,
+       DATA_ONLY, 0x66,
+       DATA_ONLY, 0x66,
+       DATA_ONLY, 0x66,
+       DATA_ONLY, 0x66,
+       DATA_ONLY, 0x66,
+       DATA_ONLY, 0x66,
+
+       0xb7, 0x2c,
+       DATA_ONLY, 0x12,
+       DATA_ONLY, 0x0c,
+       DATA_ONLY, 0x0a,
+       DATA_ONLY, 0x10,
+       DATA_ONLY, 0x0e,
+       DATA_ONLY, 0x17,
+       DATA_ONLY, 0x13,
+       DATA_ONLY, 0x1f,
+       DATA_ONLY, 0x1a,
+       DATA_ONLY, 0x2a,
+       DATA_ONLY, 0x24,
+       DATA_ONLY, 0x1f,
+       DATA_ONLY, 0x1b,
+       DATA_ONLY, 0x1a,
+       DATA_ONLY, 0x17,
+
+       DATA_ONLY, 0x2b,
+       DATA_ONLY, 0x26,
+       DATA_ONLY, 0x22,
+       DATA_ONLY, 0x20,
+       DATA_ONLY, 0x3a,
+       DATA_ONLY, 0x34,
+       DATA_ONLY, 0x30,
+       DATA_ONLY, 0x2c,
+       DATA_ONLY, 0x29,
+       DATA_ONLY, 0x26,
+       DATA_ONLY, 0x25,
+       DATA_ONLY, 0x23,
+       DATA_ONLY, 0x21,
+       DATA_ONLY, 0x20,
+       DATA_ONLY, 0x1e,
+       DATA_ONLY, 0x1e,
+
+       0xb8, 0x00,
+       DATA_ONLY, 0x00,
+       DATA_ONLY, 0x11,
+       DATA_ONLY, 0x22,
+       DATA_ONLY, 0x33,
+       DATA_ONLY, 0x44,
+       DATA_ONLY, 0x44,
+       DATA_ONLY, 0x44,
+
+       DATA_ONLY, 0x55,
+       DATA_ONLY, 0x55,
+       DATA_ONLY, 0x66,
+       DATA_ONLY, 0x66,
+       DATA_ONLY, 0x66,
+       DATA_ONLY, 0x66,
+       DATA_ONLY, 0x66,
+       DATA_ONLY, 0x66,
+
+       0xb9, 0x2c,
+       DATA_ONLY, 0x12,
+       DATA_ONLY, 0x0c,
+       DATA_ONLY, 0x0a,
+       DATA_ONLY, 0x10,
+       DATA_ONLY, 0x0e,
+       DATA_ONLY, 0x17,
+       DATA_ONLY, 0x13,
+       DATA_ONLY, 0x1f,
+       DATA_ONLY, 0x1a,
+       DATA_ONLY, 0x2a,
+       DATA_ONLY, 0x24,
+       DATA_ONLY, 0x1f,
+       DATA_ONLY, 0x1b,
+       DATA_ONLY, 0x1a,
+       DATA_ONLY, 0x17,
+
+       DATA_ONLY, 0x2b,
+       DATA_ONLY, 0x26,
+       DATA_ONLY, 0x22,
+       DATA_ONLY, 0x20,
+       DATA_ONLY, 0x3a,
+       DATA_ONLY, 0x34,
+       DATA_ONLY, 0x30,
+       DATA_ONLY, 0x2c,
+       DATA_ONLY, 0x29,
+       DATA_ONLY, 0x26,
+       DATA_ONLY, 0x25,
+       DATA_ONLY, 0x23,
+       DATA_ONLY, 0x21,
+       DATA_ONLY, 0x20,
+       DATA_ONLY, 0x1e,
+       DATA_ONLY, 0x1e,
+
+       0xba, 0x00,
+       DATA_ONLY, 0x00,
+       DATA_ONLY, 0x11,
+       DATA_ONLY, 0x22,
+       DATA_ONLY, 0x33,
+       DATA_ONLY, 0x44,
+       DATA_ONLY, 0x44,
+       DATA_ONLY, 0x44,
+
+       DATA_ONLY, 0x55,
+       DATA_ONLY, 0x55,
+       DATA_ONLY, 0x66,
+       DATA_ONLY, 0x66,
+       DATA_ONLY, 0x66,
+       DATA_ONLY, 0x66,
+       DATA_ONLY, 0x66,
+       DATA_ONLY, 0x66,
+
+       0xc1, 0x4d,
+       DATA_ONLY, 0x96,
+       DATA_ONLY, 0x1d,
+       DATA_ONLY, 0x00,
+       DATA_ONLY, 0x00,
+       DATA_ONLY, 0x01,
+       DATA_ONLY, 0xdf,
+       DATA_ONLY, 0x00,
+       DATA_ONLY, 0x00,
+       DATA_ONLY, 0x03,
+       DATA_ONLY, 0x1f,
+       DATA_ONLY, 0x00,
+       DATA_ONLY, 0x00,
+       DATA_ONLY, 0x00,
+       DATA_ONLY, 0x00,
+       DATA_ONLY, 0x00,
+       DATA_ONLY, 0x00,
+       DATA_ONLY, 0x00,
+       DATA_ONLY, 0x00,
+       DATA_ONLY, 0x03,
+       DATA_ONLY, 0x06,
+       DATA_ONLY, 0x09,
+       DATA_ONLY, 0x0d,
+       DATA_ONLY, 0x0f,
+       DATA_ONLY, 0x12,
+       DATA_ONLY, 0x15,
+       DATA_ONLY, 0x18,
+
+       0xb2, 0x10,
+       DATA_ONLY, 0x10,
+       DATA_ONLY, 0x0b,
+       DATA_ONLY, 0x05,
+
+       ENDDEF, 0x0000
+};
+
+static const unsigned short SEQ_ACL_ON[] = {
+       /* ACL on */
+       0xc0, 0x01,
+
+       ENDDEF, 0x0000
+};
+
+static const unsigned short SEQ_ACL_OFF[] = {
+       /* ACL off */
+       0xc0, 0x00,
+
+       ENDDEF, 0x0000
+};
+
+static const unsigned short SEQ_ELVSS_ON[] = {
+       /* ELVSS on */
+       0xb1, 0x0b,
+
+       ENDDEF, 0x0000
+};
+
+static const unsigned short SEQ_ELVSS_OFF[] = {
+       /* ELVSS off */
+       0xb1, 0x0a,
+
+       ENDDEF, 0x0000
+};
+
+static const unsigned short SEQ_STAND_BY_OFF[] = {
+       0x11, COMMAND_ONLY,
+
+       ENDDEF, 0x0000
+};
+
+static const unsigned short SEQ_STAND_BY_ON[] = {
+       0x10, COMMAND_ONLY,
+
+       ENDDEF, 0x0000
+};
+
+static const unsigned short SEQ_DISPLAY_ON[] = {
+       0x29, COMMAND_ONLY,
+
+       ENDDEF, 0x0000
+};
+
+
+static int s6e63m0_spi_write_byte(struct s6e63m0 *lcd, int addr, int data)
+{
+       u16 buf[1];
+       struct spi_message msg;
+
+       struct spi_transfer xfer = {
+               .len            = 2,
+               .tx_buf         = buf,
+       };
+
+       buf[0] = (addr << 8) | data;
+
+       spi_message_init(&msg);
+       spi_message_add_tail(&xfer, &msg);
+
+       return spi_sync(lcd->spi, &msg);
+}
+
+static int s6e63m0_spi_write(struct s6e63m0 *lcd, unsigned char address,
+       unsigned char command)
+{
+       int ret = 0;
+
+       if (address != DATA_ONLY)
+               ret = s6e63m0_spi_write_byte(lcd, 0x0, address);
+       if (command != COMMAND_ONLY)
+               ret = s6e63m0_spi_write_byte(lcd, 0x1, command);
+
+       return ret;
+}
+
+static int s6e63m0_panel_send_sequence(struct s6e63m0 *lcd,
+       const unsigned short *wbuf)
+{
+       int ret = 0, i = 0;
+
+       while ((wbuf[i] & DEFMASK) != ENDDEF) {
+               if ((wbuf[i] & DEFMASK) != SLEEPMSEC) {
+                       ret = s6e63m0_spi_write(lcd, wbuf[i], wbuf[i+1]);
+                       if (ret)
+                               break;
+               } else
+                       udelay(wbuf[i+1]*1000);
+               i += 2;
+       }
+
+       return ret;
+}
+
+static int _s6e63m0_gamma_ctl(struct s6e63m0 *lcd, const unsigned int *gamma)
+{
+       unsigned int i = 0;
+       int ret = 0;
+
+       /* disable gamma table updating. */
+       ret = s6e63m0_spi_write(lcd, 0xfa, 0x00);
+       if (ret) {
+               dev_err(lcd->dev, "failed to disable gamma table updating.\n");
+               goto gamma_err;
+       }
+
+       for (i = 0 ; i < GAMMA_TABLE_COUNT; i++) {
+               ret = s6e63m0_spi_write(lcd, DATA_ONLY, gamma[i]);
+               if (ret) {
+                       dev_err(lcd->dev, "failed to set gamma table.\n");
+                       goto gamma_err;
+               }
+       }
+
+       /* update gamma table. */
+       ret = s6e63m0_spi_write(lcd, 0xfa, 0x01);
+       if (ret)
+               dev_err(lcd->dev, "failed to update gamma table.\n");
+
+gamma_err:
+       return ret;
+}
+
+static int s6e63m0_gamma_ctl(struct s6e63m0 *lcd, int gamma)
+{
+       int ret = 0;
+
+       ret = _s6e63m0_gamma_ctl(lcd, gamma_table.gamma_22_table[gamma]);
+
+       return ret;
+}
+
+
+static int s6e63m0_ldi_init(struct s6e63m0 *lcd)
+{
+       int ret, i;
+       const unsigned short *init_seq[] = {
+               SEQ_PANEL_CONDITION_SET,
+               SEQ_DISPLAY_CONDITION_SET,
+               SEQ_GAMMA_SETTING,
+               SEQ_ETC_CONDITION_SET,
+               SEQ_ACL_ON,
+               SEQ_ELVSS_ON,
+       };
+
+       for (i = 0; i < ARRAY_SIZE(init_seq); i++) {
+               ret = s6e63m0_panel_send_sequence(lcd, init_seq[i]);
+               if (ret)
+                       break;
+       }
+
+       return ret;
+}
+
+static int s6e63m0_ldi_enable(struct s6e63m0 *lcd)
+{
+       int ret = 0, i;
+       const unsigned short *enable_seq[] = {
+               SEQ_STAND_BY_OFF,
+               SEQ_DISPLAY_ON,
+       };
+
+       for (i = 0; i < ARRAY_SIZE(enable_seq); i++) {
+               ret = s6e63m0_panel_send_sequence(lcd, enable_seq[i]);
+               if (ret)
+                       break;
+       }
+
+       return ret;
+}
+
+static int s6e63m0_ldi_disable(struct s6e63m0 *lcd)
+{
+       int ret;
+
+       ret = s6e63m0_panel_send_sequence(lcd, SEQ_STAND_BY_ON);
+
+       return ret;
+}
+
+static int s6e63m0_power_on(struct s6e63m0 *lcd)
+{
+       int ret = 0;
+       struct lcd_platform_data *pd = NULL;
+       struct backlight_device *bd = NULL;
+
+       pd = lcd->lcd_pd;
+       if (!pd) {
+               dev_err(lcd->dev, "platform data is NULL.\n");
+               return -EFAULT;
+       }
+
+       bd = lcd->bd;
+       if (!bd) {
+               dev_err(lcd->dev, "backlight device is NULL.\n");
+               return -EFAULT;
+       }
+
+       if (!pd->power_on) {
+               dev_err(lcd->dev, "power_on is NULL.\n");
+               return -EFAULT;
+       } else {
+               pd->power_on(lcd->ld, 1);
+               mdelay(pd->power_on_delay);
+       }
+
+       if (!pd->reset) {
+               dev_err(lcd->dev, "reset is NULL.\n");
+               return -EFAULT;
+       } else {
+               pd->reset(lcd->ld);
+               mdelay(pd->reset_delay);
+       }
+
+       ret = s6e63m0_ldi_init(lcd);
+       if (ret) {
+               dev_err(lcd->dev, "failed to initialize ldi.\n");
+               return ret;
+       }
+
+       ret = s6e63m0_ldi_enable(lcd);
+       if (ret) {
+               dev_err(lcd->dev, "failed to enable ldi.\n");
+               return ret;
+       }
+
+       /* set brightness to current value after power on or resume. */
+       ret = s6e63m0_gamma_ctl(lcd, bd->props.brightness);
+       if (ret) {
+               dev_err(lcd->dev, "lcd gamma setting failed.\n");
+               return ret;
+       }
+
+       return 0;
+}
+
+static int s6e63m0_power_off(struct s6e63m0 *lcd)
+{
+       int ret = 0;
+       struct lcd_platform_data *pd = NULL;
+
+       pd = lcd->lcd_pd;
+       if (!pd) {
+               dev_err(lcd->dev, "platform data is NULL.\n");
+               return -EFAULT;
+       }
+
+       ret = s6e63m0_ldi_disable(lcd);
+       if (ret) {
+               dev_err(lcd->dev, "lcd setting failed.\n");
+               return -EIO;
+       }
+
+       mdelay(pd->power_off_delay);
+
+       if (!pd->power_on) {
+               dev_err(lcd->dev, "power_on is NULL.\n");
+               return -EFAULT;
+       } else
+               pd->power_on(lcd->ld, 0);
+
+       return 0;
+}
+
+static int s6e63m0_power(struct s6e63m0 *lcd, int power)
+{
+       int ret = 0;
+
+       if (POWER_IS_ON(power) && !POWER_IS_ON(lcd->power))
+               ret = s6e63m0_power_on(lcd);
+       else if (!POWER_IS_ON(power) && POWER_IS_ON(lcd->power))
+               ret = s6e63m0_power_off(lcd);
+
+       if (!ret)
+               lcd->power = power;
+
+       return ret;
+}
+
+static int s6e63m0_set_power(struct lcd_device *ld, int power)
+{
+       struct s6e63m0 *lcd = lcd_get_data(ld);
+
+       if (power != FB_BLANK_UNBLANK && power != FB_BLANK_POWERDOWN &&
+               power != FB_BLANK_NORMAL) {
+               dev_err(lcd->dev, "power value should be 0, 1 or 4.\n");
+               return -EINVAL;
+       }
+
+       return s6e63m0_power(lcd, power);
+}
+
+static int s6e63m0_get_power(struct lcd_device *ld)
+{
+       struct s6e63m0 *lcd = lcd_get_data(ld);
+
+       return lcd->power;
+}
+
+static int s6e63m0_get_brightness(struct backlight_device *bd)
+{
+       return bd->props.brightness;
+}
+
+static int s6e63m0_set_brightness(struct backlight_device *bd)
+{
+       int ret = 0, brightness = bd->props.brightness;
+       struct s6e63m0 *lcd = bl_get_data(bd);
+
+       if (brightness < MIN_BRIGHTNESS ||
+               brightness > bd->props.max_brightness) {
+               dev_err(&bd->dev, "lcd brightness should be %d to %d.\n",
+                       MIN_BRIGHTNESS, MAX_BRIGHTNESS);
+               return -EINVAL;
+       }
+
+       ret = s6e63m0_gamma_ctl(lcd, bd->props.brightness);
+       if (ret) {
+               dev_err(&bd->dev, "lcd brightness setting failed.\n");
+               return -EIO;
+       }
+
+       return ret;
+}
+
+static struct lcd_ops s6e63m0_lcd_ops = {
+       .set_power = s6e63m0_set_power,
+       .get_power = s6e63m0_get_power,
+};
+
+static const struct backlight_ops s6e63m0_backlight_ops  = {
+       .get_brightness = s6e63m0_get_brightness,
+       .update_status = s6e63m0_set_brightness,
+};
+
+static ssize_t s6e63m0_sysfs_show_gamma_mode(struct device *dev,
+                                     struct device_attribute *attr, char *buf)
+{
+       struct s6e63m0 *lcd = dev_get_drvdata(dev);
+       char temp[10];
+
+       switch (lcd->gamma_mode) {
+       case 0:
+               sprintf(temp, "2.2 mode\n");
+               strcat(buf, temp);
+               break;
+       case 1:
+               sprintf(temp, "1.9 mode\n");
+               strcat(buf, temp);
+               break;
+       case 2:
+               sprintf(temp, "1.7 mode\n");
+               strcat(buf, temp);
+               break;
+       default:
+               dev_info(dev, "gamma mode could be 0:2.2, 1:1.9 or 2:1.7)n");
+               break;
+       }
+
+       return strlen(buf);
+}
+
+static ssize_t s6e63m0_sysfs_store_gamma_mode(struct device *dev,
+                                      struct device_attribute *attr,
+                                      const char *buf, size_t len)
+{
+       struct s6e63m0 *lcd = dev_get_drvdata(dev);
+       struct backlight_device *bd = NULL;
+       int brightness, rc;
+
+       rc = strict_strtoul(buf, 0, (unsigned long *)&lcd->gamma_mode);
+       if (rc < 0)
+               return rc;
+
+       bd = lcd->bd;
+
+       brightness = bd->props.brightness;
+
+       switch (lcd->gamma_mode) {
+       case 0:
+               _s6e63m0_gamma_ctl(lcd, gamma_table.gamma_22_table[brightness]);
+               break;
+       case 1:
+               _s6e63m0_gamma_ctl(lcd, gamma_table.gamma_19_table[brightness]);
+               break;
+       case 2:
+               _s6e63m0_gamma_ctl(lcd, gamma_table.gamma_17_table[brightness]);
+               break;
+       default:
+               dev_info(dev, "gamma mode could be 0:2.2, 1:1.9 or 2:1.7\n");
+               _s6e63m0_gamma_ctl(lcd, gamma_table.gamma_22_table[brightness]);
+               break;
+       }
+       return len;
+}
+
+static DEVICE_ATTR(gamma_mode, 0644,
+               s6e63m0_sysfs_show_gamma_mode, s6e63m0_sysfs_store_gamma_mode);
+
+static ssize_t s6e63m0_sysfs_show_gamma_table(struct device *dev,
+                                     struct device_attribute *attr, char *buf)
+{
+       struct s6e63m0 *lcd = dev_get_drvdata(dev);
+       char temp[3];
+
+       sprintf(temp, "%d\n", lcd->gamma_table_count);
+       strcpy(buf, temp);
+
+       return strlen(buf);
+}
+static DEVICE_ATTR(gamma_table, 0644,
+               s6e63m0_sysfs_show_gamma_table, NULL);
+
+static int __init s6e63m0_probe(struct spi_device *spi)
+{
+       int ret = 0;
+       struct s6e63m0 *lcd = NULL;
+       struct lcd_device *ld = NULL;
+       struct backlight_device *bd = NULL;
+
+       lcd = kzalloc(sizeof(struct s6e63m0), GFP_KERNEL);
+       if (!lcd)
+               return -ENOMEM;
+
+       /* s6e63m0 lcd panel uses 3-wire 9bits SPI Mode. */
+       spi->bits_per_word = 9;
+
+       ret = spi_setup(spi);
+       if (ret < 0) {
+               dev_err(&spi->dev, "spi setup failed.\n");
+               goto out_free_lcd;
+       }
+
+       lcd->spi = spi;
+       lcd->dev = &spi->dev;
+
+       lcd->lcd_pd = (struct lcd_platform_data *)spi->dev.platform_data;
+       if (!lcd->lcd_pd) {
+               dev_err(&spi->dev, "platform data is NULL.\n");
+               goto out_free_lcd;
+       }
+
+       ld = lcd_device_register("s6e63m0", &spi->dev, lcd, &s6e63m0_lcd_ops);
+       if (IS_ERR(ld)) {
+               ret = PTR_ERR(ld);
+               goto out_free_lcd;
+       }
+
+       lcd->ld = ld;
+
+       bd = backlight_device_register("s6e63m0bl-bl", &spi->dev, lcd,
+               &s6e63m0_backlight_ops, NULL);
+       if (IS_ERR(bd)) {
+               ret =  PTR_ERR(bd);
+               goto out_lcd_unregister;
+       }
+
+       bd->props.max_brightness = MAX_BRIGHTNESS;
+       bd->props.brightness = MAX_BRIGHTNESS;
+       lcd->bd = bd;
+
+       /*
+        * it gets gamma table count available so it gets user
+        * know that.
+        */
+       lcd->gamma_table_count =
+           sizeof(gamma_table) / (MAX_GAMMA_LEVEL * sizeof(int));
+
+       ret = device_create_file(&(spi->dev), &dev_attr_gamma_mode);
+       if (ret < 0)
+               dev_err(&(spi->dev), "failed to add sysfs entries\n");
+
+       ret = device_create_file(&(spi->dev), &dev_attr_gamma_table);
+       if (ret < 0)
+               dev_err(&(spi->dev), "failed to add sysfs entries\n");
+
+       /*
+        * if lcd panel was on from bootloader like u-boot then
+        * do not lcd on.
+        */
+       if (!lcd->lcd_pd->lcd_enabled) {
+               /*
+                * if lcd panel was off from bootloader then
+                * current lcd status is powerdown and then
+                * it enables lcd panel.
+                */
+               lcd->power = FB_BLANK_POWERDOWN;
+
+               s6e63m0_power(lcd, FB_BLANK_UNBLANK);
+       } else
+               lcd->power = FB_BLANK_UNBLANK;
+
+       dev_set_drvdata(&spi->dev, lcd);
+
+       dev_info(&spi->dev, "s6e63m0 panel driver has been probed.\n");
+
+       return 0;
+
+out_lcd_unregister:
+       lcd_device_unregister(ld);
+out_free_lcd:
+       kfree(lcd);
+       return ret;
+}
+
+static int __devexit s6e63m0_remove(struct spi_device *spi)
+{
+       struct s6e63m0 *lcd = dev_get_drvdata(&spi->dev);
+
+       s6e63m0_power(lcd, FB_BLANK_POWERDOWN);
+       lcd_device_unregister(lcd->ld);
+       kfree(lcd);
+
+       return 0;
+}
+
+#if defined(CONFIG_PM)
+unsigned int before_power;
+
+static int s6e63m0_suspend(struct spi_device *spi, pm_message_t mesg)
+{
+       int ret = 0;
+       struct s6e63m0 *lcd = dev_get_drvdata(&spi->dev);
+
+       dev_dbg(&spi->dev, "lcd->power = %d\n", lcd->power);
+
+       before_power = lcd->power;
+
+       /*
+        * when lcd panel is suspend, lcd panel becomes off
+        * regardless of status.
+        */
+       ret = s6e63m0_power(lcd, FB_BLANK_POWERDOWN);
+
+       return ret;
+}
+
+static int s6e63m0_resume(struct spi_device *spi)
+{
+       int ret = 0;
+       struct s6e63m0 *lcd = dev_get_drvdata(&spi->dev);
+
+       /*
+        * after suspended, if lcd panel status is FB_BLANK_UNBLANK
+        * (at that time, before_power is FB_BLANK_UNBLANK) then
+        * it changes that status to FB_BLANK_POWERDOWN to get lcd on.
+        */
+       if (before_power == FB_BLANK_UNBLANK)
+               lcd->power = FB_BLANK_POWERDOWN;
+
+       dev_dbg(&spi->dev, "before_power = %d\n", before_power);
+
+       ret = s6e63m0_power(lcd, before_power);
+
+       return ret;
+}
+#else
+#define s6e63m0_suspend                NULL
+#define s6e63m0_resume         NULL
+#endif
+
+/* Power down all displays on reboot, poweroff or halt. */
+static void s6e63m0_shutdown(struct spi_device *spi)
+{
+       struct s6e63m0 *lcd = dev_get_drvdata(&spi->dev);
+
+       s6e63m0_power(lcd, FB_BLANK_POWERDOWN);
+}
+
+static struct spi_driver s6e63m0_driver = {
+       .driver = {
+               .name   = "s6e63m0",
+               .bus    = &spi_bus_type,
+               .owner  = THIS_MODULE,
+       },
+       .probe          = s6e63m0_probe,
+       .remove         = __devexit_p(s6e63m0_remove),
+       .shutdown       = s6e63m0_shutdown,
+       .suspend        = s6e63m0_suspend,
+       .resume         = s6e63m0_resume,
+};
+
+static int __init s6e63m0_init(void)
+{
+       return spi_register_driver(&s6e63m0_driver);
+}
+
+static void __exit s6e63m0_exit(void)
+{
+       spi_unregister_driver(&s6e63m0_driver);
+}
+
+module_init(s6e63m0_init);
+module_exit(s6e63m0_exit);
+
+MODULE_AUTHOR("InKi Dae <inki.dae@samsung.com>");
+MODULE_DESCRIPTION("S6E63M0 LCD Driver");
+MODULE_LICENSE("GPL");
+
diff --git a/drivers/video/backlight/s6e63m0_gamma.h b/drivers/video/backlight/s6e63m0_gamma.h
new file mode 100644 (file)
index 0000000..2c44bdb
--- /dev/null
@@ -0,0 +1,266 @@
+/* linux/drivers/video/samsung/s6e63m0_brightness.h
+ *
+ * Gamma level definitions.
+ *
+ * Copyright (c) 2009 Samsung Electronics
+ * InKi Dae <inki.dae@samsung.com>
+ *
+ * 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.
+*/
+
+#ifndef _S6E63M0_BRIGHTNESS_H
+#define _S6E63M0_BRIGHTNESS_H
+
+#define MAX_GAMMA_LEVEL                11
+#define GAMMA_TABLE_COUNT      21
+
+/* gamma value: 2.2 */
+static const unsigned int s6e63m0_22_300[] = {
+       0x18, 0x08, 0x24, 0x5f, 0x50, 0x2d, 0xB6,
+       0xB9, 0xA7, 0xAd, 0xB1, 0x9f, 0xbe, 0xC0,
+       0xB5, 0x00, 0xa0, 0x00, 0xa4, 0x00, 0xdb
+};
+
+static const unsigned int s6e63m0_22_280[] = {
+       0x18, 0x08, 0x24, 0x64, 0x56, 0x33, 0xB6,
+       0xBA, 0xA8, 0xAC, 0xB1, 0x9D, 0xC1, 0xC1,
+       0xB7, 0x00, 0x9C, 0x00, 0x9F, 0x00, 0xD6
+};
+
+static const unsigned int s6e63m0_22_260[] = {
+       0x18, 0x08, 0x24, 0x66, 0x58, 0x34, 0xB6,
+       0xBA, 0xA7, 0xAF, 0xB3, 0xA0, 0xC1, 0xC2,
+       0xB7, 0x00, 0x97, 0x00, 0x9A, 0x00, 0xD1
+
+};
+
+static const unsigned int s6e63m0_22_240[] = {
+       0x18, 0x08, 0x24, 0x62, 0x54, 0x30, 0xB9,
+       0xBB, 0xA9, 0xB0, 0xB3, 0xA1, 0xC1, 0xC3,
+       0xB7, 0x00, 0x91, 0x00, 0x95, 0x00, 0xDA
+
+};
+static const unsigned int s6e63m0_22_220[] = {
+       0x18, 0x08, 0x24, 0x63, 0x53, 0x31, 0xB8,
+       0xBC, 0xA9, 0xB0, 0xB5, 0xA2, 0xC4, 0xC4,
+       0xB8, 0x00, 0x8B, 0x00, 0x8E, 0x00, 0xC2
+};
+
+static const unsigned int s6e63m0_22_200[] = {
+       0x18, 0x08, 0x24, 0x66, 0x55, 0x34, 0xBA,
+       0xBD, 0xAB, 0xB1, 0xB5, 0xA3, 0xC5, 0xC6,
+       0xB9, 0x00, 0x85, 0x00, 0x88, 0x00, 0xBA
+};
+
+static const unsigned int s6e63m0_22_170[] = {
+       0x18, 0x08, 0x24, 0x69, 0x54, 0x37, 0xBB,
+       0xBE, 0xAC, 0xB4, 0xB7, 0xA6, 0xC7, 0xC8,
+       0xBC, 0x00, 0x7B, 0x00, 0x7E, 0x00, 0xAB
+};
+
+static const unsigned int s6e63m0_22_140[] = {
+       0x18, 0x08, 0x24, 0x6C, 0x54, 0x3A, 0xBC,
+       0xBF, 0xAC, 0xB7, 0xBB, 0xA9, 0xC9, 0xC9,
+       0xBE, 0x00, 0x71, 0x00, 0x73, 0x00, 0x9E
+};
+
+static const unsigned int s6e63m0_22_110[] = {
+       0x18, 0x08, 0x24, 0x70, 0x51, 0x3E, 0xBF,
+       0xC1, 0xAF, 0xB9, 0xBC, 0xAB, 0xCC, 0xCC,
+       0xC2, 0x00, 0x65, 0x00, 0x67, 0x00, 0x8D
+};
+
+static const unsigned int s6e63m0_22_90[] = {
+       0x18, 0x08, 0x24, 0x73, 0x4A, 0x3D, 0xC0,
+       0xC2, 0xB1, 0xBB, 0xBE, 0xAC, 0xCE, 0xCF,
+       0xC5, 0x00, 0x5D, 0x00, 0x5E, 0x00, 0x82
+};
+
+static const unsigned int s6e63m0_22_30[] = {
+       0x18, 0x08, 0x24, 0x78, 0xEC, 0x3D, 0xC8,
+       0xC2, 0xB6, 0xC4, 0xC7, 0xB6, 0xD5, 0xD7,
+       0xCC, 0x00, 0x39, 0x00, 0x36, 0x00, 0x51
+};
+
+/* gamma value: 1.9 */
+static const unsigned int s6e63m0_19_300[] = {
+       0x18, 0x08, 0x24, 0x61, 0x5F, 0x39, 0xBA,
+       0xBD, 0xAD, 0xB1, 0xB6, 0xA5, 0xC4, 0xC5,
+       0xBC, 0x00, 0xA0, 0x00, 0xA4, 0x00, 0xDB
+};
+
+static const unsigned int s6e63m0_19_280[] = {
+       0x18, 0x08, 0x24, 0x61, 0x60, 0x39, 0xBB,
+       0xBE, 0xAD, 0xB2, 0xB6, 0xA6, 0xC5, 0xC7,
+       0xBD, 0x00, 0x9B, 0x00, 0x9E, 0x00, 0xD5
+};
+
+static const unsigned int s6e63m0_19_260[] = {
+       0x18, 0x08, 0x24, 0x63, 0x61, 0x3B, 0xBA,
+       0xBE, 0xAC, 0xB3, 0xB8, 0xA7, 0xC6, 0xC8,
+       0xBD, 0x00, 0x96, 0x00, 0x98, 0x00, 0xCF
+};
+
+static const unsigned int s6e63m0_19_240[] = {
+       0x18, 0x08, 0x24, 0x67, 0x64, 0x3F, 0xBB,
+       0xBE, 0xAD, 0xB3, 0xB9, 0xA7, 0xC8, 0xC9,
+       0xBE, 0x00, 0x90, 0x00, 0x92, 0x00, 0xC8
+};
+
+static const unsigned int s6e63m0_19_220[] = {
+       0x18, 0x08, 0x24, 0x68, 0x64, 0x40, 0xBC,
+       0xBF, 0xAF, 0xB4, 0xBA, 0xA9, 0xC8, 0xCA,
+       0xBE, 0x00, 0x8B, 0x00, 0x8C, 0x00, 0xC0
+};
+
+static const unsigned int s6e63m0_19_200[] = {
+       0x18, 0x08, 0x24, 0x68, 0x64, 0x3F, 0xBE,
+       0xC0, 0xB0, 0xB6, 0xBB, 0xAB, 0xC8, 0xCB,
+       0xBF, 0x00, 0x85, 0x00, 0x86, 0x00, 0xB8
+};
+
+static const unsigned int s6e63m0_19_170[] = {
+       0x18, 0x08, 0x24, 0x69, 0x64, 0x40, 0xBF,
+       0xC1, 0xB0, 0xB9, 0xBE, 0xAD, 0xCB, 0xCD,
+       0xC2, 0x00, 0x7A, 0x00, 0x7B, 0x00, 0xAA
+};
+
+static const unsigned int s6e63m0_19_140[] = {
+       0x18, 0x08, 0x24, 0x6E, 0x65, 0x45, 0xC0,
+       0xC3, 0xB2, 0xBA, 0xBE, 0xAE, 0xCD, 0xD0,
+       0xC4, 0x00, 0x70, 0x00, 0x70, 0x00, 0x9C
+};
+
+static const unsigned int s6e63m0_19_110[] = {
+       0x18, 0x08, 0x24, 0x6F, 0x65, 0x46, 0xC2,
+       0xC4, 0xB3, 0xBF, 0xC2, 0xB2, 0xCF, 0xD1,
+       0xC6, 0x00, 0x64, 0x00, 0x64, 0x00, 0x8D
+};
+
+static const unsigned int s6e63m0_19_90[] = {
+       0x18, 0x08, 0x24, 0x74, 0x60, 0x4A, 0xC3,
+       0xC6, 0xB5, 0xBF, 0xC3, 0xB2, 0xD2, 0xD3,
+       0xC8, 0x00, 0x5B, 0x00, 0x5B, 0x00, 0x81
+};
+
+static const unsigned int s6e63m0_19_30[] = {
+       0x18, 0x08, 0x24, 0x84, 0x45, 0x4F, 0xCA,
+       0xCB, 0xBC, 0xC9, 0xCB, 0xBC, 0xDA, 0xDA,
+       0xD0, 0x00, 0x35, 0x00, 0x34, 0x00, 0x4E
+};
+
+/* gamma value: 1.7 */
+static const unsigned int s6e63m0_17_300[] = {
+       0x18, 0x08, 0x24, 0x70, 0x70, 0x4F, 0xBF,
+       0xC2, 0xB2, 0xB8, 0xBC, 0xAC, 0xCB, 0xCD,
+       0xC3, 0x00, 0xA0, 0x00, 0xA4, 0x00, 0xDB
+};
+
+static const unsigned int s6e63m0_17_280[] = {
+       0x18, 0x08, 0x24, 0x71, 0x71, 0x50, 0xBF,
+       0xC2, 0xB2, 0xBA, 0xBE, 0xAE, 0xCB, 0xCD,
+       0xC3, 0x00, 0x9C, 0x00, 0x9F, 0x00, 0xD6
+};
+
+static const unsigned int s6e63m0_17_260[] = {
+       0x18, 0x08, 0x24, 0x72, 0x72, 0x50, 0xC0,
+       0xC3, 0xB4, 0xB9, 0xBE, 0xAE, 0xCC, 0xCF,
+       0xC4, 0x00, 0x97, 0x00, 0x9A, 0x00, 0xD1
+};
+
+static const unsigned int s6e63m0_17_240[] = {
+       0x18, 0x08, 0x24, 0x71, 0x72, 0x4F, 0xC2,
+       0xC4, 0xB5, 0xBB, 0xBF, 0xB0, 0xCC, 0xCF,
+       0xC3, 0x00, 0x91, 0x00, 0x95, 0x00, 0xCA
+};
+
+static const unsigned int s6e63m0_17_220[] = {
+       0x18, 0x08, 0x24, 0x71, 0x73, 0x4F, 0xC2,
+       0xC5, 0xB5, 0xBD, 0xC0, 0xB2, 0xCD, 0xD1,
+       0xC5, 0x00, 0x8B, 0x00, 0x8E, 0x00, 0xC2
+};
+
+static const unsigned int s6e63m0_17_200[] = {
+       0x18, 0x08, 0x24, 0x72, 0x75, 0x51, 0xC2,
+       0xC6, 0xB5, 0xBF, 0xC1, 0xB3, 0xCE, 0xD1,
+       0xC6, 0x00, 0x85, 0x00, 0x88, 0x00, 0xBA
+};
+
+static const unsigned int s6e63m0_17_170[] = {
+       0x18, 0x08, 0x24, 0x75, 0x77, 0x54, 0xC3,
+       0xC7, 0xB7, 0xC0, 0xC3, 0xB4, 0xD1, 0xD3,
+       0xC9, 0x00, 0x7B, 0x00, 0x7E, 0x00, 0xAB
+};
+
+static const unsigned int s6e63m0_17_140[] = {
+       0x18, 0x08, 0x24, 0x7B, 0x77, 0x58, 0xC3,
+       0xC8, 0xB8, 0xC2, 0xC6, 0xB6, 0xD3, 0xD4,
+       0xCA, 0x00, 0x71, 0x00, 0x73, 0x00, 0x9E
+};
+
+static const unsigned int s6e63m0_17_110[] = {
+       0x18, 0x08, 0x24, 0x81, 0x7B, 0x5D, 0xC6,
+       0xCA, 0xBB, 0xC3, 0xC7, 0xB8, 0xD6, 0xD8,
+       0xCD, 0x00, 0x65, 0x00, 0x67, 0x00, 0x8D
+};
+
+static const unsigned int s6e63m0_17_90[] = {
+       0x18, 0x08, 0x24, 0x82, 0x7A, 0x5B, 0xC8,
+       0xCB, 0xBD, 0xC5, 0xCA, 0xBA, 0xD6, 0xD8,
+       0xCE, 0x00, 0x5D, 0x00, 0x5E, 0x00, 0x82
+};
+
+static const unsigned int s6e63m0_17_30[] = {
+       0x18, 0x08, 0x24, 0x8F, 0x73, 0x63, 0xD1,
+       0xD0, 0xC5, 0xCC, 0xD1, 0xC2, 0xDE, 0xE0,
+       0xD6, 0x00, 0x39, 0x00, 0x36, 0x00, 0x51
+};
+
+struct s6e63m0_gamma {
+       unsigned int *gamma_22_table[MAX_GAMMA_LEVEL];
+       unsigned int *gamma_19_table[MAX_GAMMA_LEVEL];
+       unsigned int *gamma_17_table[MAX_GAMMA_LEVEL];
+};
+
+static struct s6e63m0_gamma gamma_table = {
+       .gamma_22_table[0] = (unsigned int *)&s6e63m0_22_30,
+       .gamma_22_table[1] = (unsigned int *)&s6e63m0_22_90,
+       .gamma_22_table[2] = (unsigned int *)&s6e63m0_22_110,
+       .gamma_22_table[3] = (unsigned int *)&s6e63m0_22_140,
+       .gamma_22_table[4] = (unsigned int *)&s6e63m0_22_170,
+       .gamma_22_table[5] = (unsigned int *)&s6e63m0_22_200,
+       .gamma_22_table[6] = (unsigned int *)&s6e63m0_22_220,
+       .gamma_22_table[7] = (unsigned int *)&s6e63m0_22_240,
+       .gamma_22_table[8] = (unsigned int *)&s6e63m0_22_260,
+       .gamma_22_table[9] = (unsigned int *)&s6e63m0_22_280,
+       .gamma_22_table[10] = (unsigned int *)&s6e63m0_22_300,
+
+       .gamma_19_table[0] = (unsigned int *)&s6e63m0_19_30,
+       .gamma_19_table[1] = (unsigned int *)&s6e63m0_19_90,
+       .gamma_19_table[2] = (unsigned int *)&s6e63m0_19_110,
+       .gamma_19_table[3] = (unsigned int *)&s6e63m0_19_140,
+       .gamma_19_table[4] = (unsigned int *)&s6e63m0_19_170,
+       .gamma_19_table[5] = (unsigned int *)&s6e63m0_19_200,
+       .gamma_19_table[6] = (unsigned int *)&s6e63m0_19_220,
+       .gamma_19_table[7] = (unsigned int *)&s6e63m0_19_240,
+       .gamma_19_table[8] = (unsigned int *)&s6e63m0_19_260,
+       .gamma_19_table[9] = (unsigned int *)&s6e63m0_19_280,
+       .gamma_19_table[10] = (unsigned int *)&s6e63m0_19_300,
+
+       .gamma_17_table[0] = (unsigned int *)&s6e63m0_17_30,
+       .gamma_17_table[1] = (unsigned int *)&s6e63m0_17_90,
+       .gamma_17_table[2] = (unsigned int *)&s6e63m0_17_110,
+       .gamma_17_table[3] = (unsigned int *)&s6e63m0_17_140,
+       .gamma_17_table[4] = (unsigned int *)&s6e63m0_17_170,
+       .gamma_17_table[5] = (unsigned int *)&s6e63m0_17_200,
+       .gamma_17_table[6] = (unsigned int *)&s6e63m0_17_220,
+       .gamma_17_table[7] = (unsigned int *)&s6e63m0_17_240,
+       .gamma_17_table[8] = (unsigned int *)&s6e63m0_17_260,
+       .gamma_17_table[9] = (unsigned int *)&s6e63m0_17_280,
+       .gamma_17_table[10] = (unsigned int *)&s6e63m0_17_300,
+};
+
+#endif
+
index 1105a59..073c9b4 100644 (file)
@@ -66,7 +66,7 @@ static int fb_deferred_io_fault(struct vm_area_struct *vma,
        return 0;
 }
 
-int fb_deferred_io_fsync(struct file *file, struct dentry *dentry, int datasync)
+int fb_deferred_io_fsync(struct file *file, int datasync)
 {
        struct fb_info *info = file->private_data;
 
index 69c6adb..428f8a1 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * RDC321x watchdog driver
  *
- * Copyright (C) 2007 Florian Fainelli <florian@openwrt.org>
+ * Copyright (C) 2007-2010 Florian Fainelli <florian@openwrt.org>
  *
  * This driver is highly inspired from the cpu5_wdt driver
  *
@@ -36,8 +36,7 @@
 #include <linux/watchdog.h>
 #include <linux/io.h>
 #include <linux/uaccess.h>
-
-#include <asm/rdc321x_defs.h>
+#include <linux/mfd/rdc321x.h>
 
 #define RDC_WDT_MASK   0x80000000 /* Mask */
 #define RDC_WDT_EN     0x00800000 /* Enable bit */
@@ -63,6 +62,8 @@ static struct {
        int default_ticks;
        unsigned long inuse;
        spinlock_t lock;
+       struct pci_dev *sb_pdev;
+       int base_reg;
 } rdc321x_wdt_device;
 
 /* generic helper functions */
@@ -70,14 +71,18 @@ static struct {
 static void rdc321x_wdt_trigger(unsigned long unused)
 {
        unsigned long flags;
+       u32 val;
 
        if (rdc321x_wdt_device.running)
                ticks--;
 
        /* keep watchdog alive */
        spin_lock_irqsave(&rdc321x_wdt_device.lock, flags);
-       outl(RDC_WDT_EN | inl(RDC3210_CFGREG_DATA),
-               RDC3210_CFGREG_DATA);
+       pci_read_config_dword(rdc321x_wdt_device.sb_pdev,
+                                       rdc321x_wdt_device.base_reg, &val);
+       val |= RDC_WDT_EN;
+       pci_write_config_dword(rdc321x_wdt_device.sb_pdev,
+                                       rdc321x_wdt_device.base_reg, val);
        spin_unlock_irqrestore(&rdc321x_wdt_device.lock, flags);
 
        /* requeue?? */
@@ -105,10 +110,13 @@ static void rdc321x_wdt_start(void)
 
                /* Clear the timer */
                spin_lock_irqsave(&rdc321x_wdt_device.lock, flags);
-               outl(RDC_CLS_TMR, RDC3210_CFGREG_ADDR);
+               pci_write_config_dword(rdc321x_wdt_device.sb_pdev,
+                               rdc321x_wdt_device.base_reg, RDC_CLS_TMR);
 
                /* Enable watchdog and set the timeout to 81.92 us */
-               outl(RDC_WDT_EN | RDC_WDT_CNT, RDC3210_CFGREG_DATA);
+               pci_write_config_dword(rdc321x_wdt_device.sb_pdev,
+                                       rdc321x_wdt_device.base_reg,
+                                       RDC_WDT_EN | RDC_WDT_CNT);
                spin_unlock_irqrestore(&rdc321x_wdt_device.lock, flags);
 
                mod_timer(&rdc321x_wdt_device.timer,
@@ -148,7 +156,7 @@ static long rdc321x_wdt_ioctl(struct file *file, unsigned int cmd,
                                unsigned long arg)
 {
        void __user *argp = (void __user *)arg;
-       unsigned int value;
+       u32 value;
        static const struct watchdog_info ident = {
                .options = WDIOF_CARDRESET,
                .identity = "RDC321x WDT",
@@ -162,9 +170,10 @@ static long rdc321x_wdt_ioctl(struct file *file, unsigned int cmd,
        case WDIOC_GETSTATUS:
                /* Read the value from the DATA register */
                spin_lock_irqsave(&rdc321x_wdt_device.lock, flags);
-               value = inl(RDC3210_CFGREG_DATA);
+               pci_read_config_dword(rdc321x_wdt_device.sb_pdev,
+                                       rdc321x_wdt_device.base_reg, &value);
                spin_unlock_irqrestore(&rdc321x_wdt_device.lock, flags);
-               if (copy_to_user(argp, &value, sizeof(int)))
+               if (copy_to_user(argp, &value, sizeof(u32)))
                        return -EFAULT;
                break;
        case WDIOC_GETSUPPORT:
@@ -219,17 +228,35 @@ static struct miscdevice rdc321x_wdt_misc = {
 static int __devinit rdc321x_wdt_probe(struct platform_device *pdev)
 {
        int err;
+       struct resource *r;
+       struct rdc321x_wdt_pdata *pdata;
+
+       pdata = pdev->dev.platform_data;
+       if (!pdata) {
+               dev_err(&pdev->dev, "no platform data supplied\n");
+               return -ENODEV;
+       }
+
+       r = platform_get_resource_byname(pdev, IORESOURCE_IO, "wdt-reg");
+       if (!r) {
+               dev_err(&pdev->dev, "failed to get wdt-reg resource\n");
+               return -ENODEV;
+       }
+
+       rdc321x_wdt_device.sb_pdev = pdata->sb_pdev;
+       rdc321x_wdt_device.base_reg = r->start;
 
        err = misc_register(&rdc321x_wdt_misc);
        if (err < 0) {
-               printk(KERN_ERR PFX "watchdog misc_register failed\n");
+               dev_err(&pdev->dev, "misc_register failed\n");
                return err;
        }
 
        spin_lock_init(&rdc321x_wdt_device.lock);
 
        /* Reset the watchdog */
-       outl(RDC_WDT_RST, RDC3210_CFGREG_DATA);
+       pci_write_config_dword(rdc321x_wdt_device.sb_pdev,
+                               rdc321x_wdt_device.base_reg, RDC_WDT_RST);
 
        init_completion(&rdc321x_wdt_device.stop);
        rdc321x_wdt_device.queue = 0;
@@ -240,7 +267,7 @@ static int __devinit rdc321x_wdt_probe(struct platform_device *pdev)
 
        rdc321x_wdt_device.default_ticks = ticks;
 
-       printk(KERN_INFO PFX "watchdog init success\n");
+       dev_info(&pdev->dev, "watchdog init success\n");
 
        return 0;
 }
index 25b300e..2bedc6c 100644 (file)
@@ -257,15 +257,13 @@ v9fs_file_write(struct file *filp, const char __user * data,
        return total;
 }
 
-static int v9fs_file_fsync(struct file *filp, struct dentry *dentry,
-                                       int datasync)
+static int v9fs_file_fsync(struct file *filp, int datasync)
 {
        struct p9_fid *fid;
        struct p9_wstat wstat;
        int retval;
 
-       P9_DPRINTK(P9_DEBUG_VFS, "filp %p dentry %p datasync %x\n", filp,
-                                               dentry, datasync);
+       P9_DPRINTK(P9_DEBUG_VFS, "filp %p datasync %x\n", filp, datasync);
 
        fid = filp->private_data;
        v9fs_blank_wstat(&wstat);
index 23aa52f..f4287e4 100644 (file)
@@ -197,7 +197,7 @@ const struct file_operations adfs_dir_operations = {
        .read           = generic_read_dir,
        .llseek         = generic_file_llseek,
        .readdir        = adfs_readdir,
-       .fsync          = simple_fsync,
+       .fsync          = generic_file_fsync,
 };
 
 static int
index 005ea34..a36da53 100644 (file)
@@ -26,7 +26,7 @@ const struct file_operations adfs_file_operations = {
        .read           = do_sync_read,
        .aio_read       = generic_file_aio_read,
        .mmap           = generic_file_mmap,
-       .fsync          = simple_fsync,
+       .fsync          = generic_file_fsync,
        .write          = do_sync_write,
        .aio_write      = generic_file_aio_write,
        .splice_read    = generic_file_splice_read,
index 0f5e309..6f850b0 100644 (file)
@@ -322,8 +322,9 @@ adfs_notify_change(struct dentry *dentry, struct iattr *attr)
        if (error)
                goto out;
 
+       /* XXX: this is missing some actual on-disk truncation.. */
        if (ia_valid & ATTR_SIZE)
-               error = vmtruncate(inode, attr->ia_size);
+               error = simple_setsize(inode, attr->ia_size);
 
        if (error)
                goto out;
index 861dae6..f05b615 100644 (file)
@@ -183,7 +183,7 @@ extern int                   affs_add_entry(struct inode *dir, struct inode *inode, struct dent
 
 void           affs_free_prealloc(struct inode *inode);
 extern void    affs_truncate(struct inode *);
-int            affs_file_fsync(struct file *, struct dentry *, int);
+int            affs_file_fsync(struct file *, int);
 
 /* dir.c */
 
index 184e55c..322710c 100644 (file)
@@ -916,9 +916,9 @@ affs_truncate(struct inode *inode)
        affs_free_prealloc(inode);
 }
 
-int affs_file_fsync(struct file *filp, struct dentry *dentry, int datasync)
+int affs_file_fsync(struct file *filp, int datasync)
 {
-       struct inode * inode = dentry->d_inode;
+       struct inode *inode = filp->f_mapping->host;
        int ret, err;
 
        ret = write_inode_now(inode, 0);
index 807f284..5f679b7 100644 (file)
@@ -740,7 +740,7 @@ extern void afs_pages_written_back(struct afs_vnode *, struct afs_call *);
 extern ssize_t afs_file_write(struct kiocb *, const struct iovec *,
                              unsigned long, loff_t);
 extern int afs_writeback_all(struct afs_vnode *);
-extern int afs_fsync(struct file *, struct dentry *, int);
+extern int afs_fsync(struct file *, int);
 
 
 /*****************************************************************************/
index 3bed54a..3dab9e9 100644 (file)
@@ -701,8 +701,9 @@ int afs_writeback_all(struct afs_vnode *vnode)
  * - the return status from this call provides a reliable indication of
  *   whether any write errors occurred for this process.
  */
-int afs_fsync(struct file *file, struct dentry *dentry, int datasync)
+int afs_fsync(struct file *file, int datasync)
 {
+       struct dentry *dentry = file->f_path.dentry;
        struct afs_writeback *wb, *xwb;
        struct afs_vnode *vnode = AFS_FS_I(dentry->d_inode);
        int ret;
index 48fdeeb..1ccf25c 100644 (file)
--- a/fs/aio.c
+++ b/fs/aio.c
@@ -527,7 +527,7 @@ static void aio_fput_routine(struct work_struct *data)
 
                /* Complete the fput(s) */
                if (req->ki_filp != NULL)
-                       __fput(req->ki_filp);
+                       fput(req->ki_filp);
 
                /* Link the iocb into the context's free list */
                spin_lock_irq(&ctx->ctx_lock);
@@ -560,11 +560,11 @@ static int __aio_put_req(struct kioctx *ctx, struct kiocb *req)
 
        /*
         * Try to optimize the aio and eventfd file* puts, by avoiding to
-        * schedule work in case it is not __fput() time. In normal cases,
+        * schedule work in case it is not final fput() time. In normal cases,
         * we would not be holding the last reference to the file*, so
         * this function will be executed w/out any aio kthread wakeup.
         */
-       if (unlikely(atomic_long_dec_and_test(&req->ki_filp->f_count))) {
+       if (unlikely(!fput_atomic(req->ki_filp))) {
                get_ioctx(ctx);
                spin_lock(&fput_lock);
                list_add(&req->ki_list, &fput_head);
index 9bd4b38..e4b75d6 100644 (file)
@@ -205,7 +205,7 @@ static struct inode *anon_inode_mkinode(void)
         * that it already _is_ on the dirty list.
         */
        inode->i_state = I_DIRTY;
-       inode->i_mode = S_IFREG | S_IRUSR | S_IWUSR;
+       inode->i_mode = S_IRUSR | S_IWUSR;
        inode->i_uid = current_fsuid();
        inode->i_gid = current_fsgid();
        inode->i_flags |= S_PRIVATE;
index 0815e93..b4fa3b0 100644 (file)
--- a/fs/attr.c
+++ b/fs/attr.c
@@ -67,14 +67,14 @@ EXPORT_SYMBOL(inode_change_ok);
  * @offset:    the new size to assign to the inode
  * @Returns:   0 on success, -ve errno on failure
  *
+ * inode_newsize_ok must be called with i_mutex held.
+ *
  * inode_newsize_ok will check filesystem limits and ulimits to check that the
  * new inode size is within limits. inode_newsize_ok will also send SIGXFSZ
  * when necessary. Caller must not proceed with inode size change if failure is
  * returned. @inode must be a file (not directory), with appropriate
  * permissions to allow truncate (inode_newsize_ok does NOT check these
  * conditions).
- *
- * inode_newsize_ok must be called with i_mutex held.
  */
 int inode_newsize_ok(const struct inode *inode, loff_t offset)
 {
@@ -104,17 +104,25 @@ out_big:
 }
 EXPORT_SYMBOL(inode_newsize_ok);
 
-int inode_setattr(struct inode * inode, struct iattr * attr)
+/**
+ * generic_setattr - copy simple metadata updates into the generic inode
+ * @inode:     the inode to be updated
+ * @attr:      the new attributes
+ *
+ * generic_setattr must be called with i_mutex held.
+ *
+ * generic_setattr updates the inode's metadata with that specified
+ * in attr. Noticably missing is inode size update, which is more complex
+ * as it requires pagecache updates. See simple_setsize.
+ *
+ * The inode is not marked as dirty after this operation. The rationale is
+ * that for "simple" filesystems, the struct inode is the inode storage.
+ * The caller is free to mark the inode dirty afterwards if needed.
+ */
+void generic_setattr(struct inode *inode, const struct iattr *attr)
 {
        unsigned int ia_valid = attr->ia_valid;
 
-       if (ia_valid & ATTR_SIZE &&
-           attr->ia_size != i_size_read(inode)) {
-               int error = vmtruncate(inode, attr->ia_size);
-               if (error)
-                       return error;
-       }
-
        if (ia_valid & ATTR_UID)
                inode->i_uid = attr->ia_uid;
        if (ia_valid & ATTR_GID)
@@ -135,6 +143,28 @@ int inode_setattr(struct inode * inode, struct iattr * attr)
                        mode &= ~S_ISGID;
                inode->i_mode = mode;
        }
+}
+EXPORT_SYMBOL(generic_setattr);
+
+/*
+ * note this function is deprecated, the new truncate sequence should be
+ * used instead -- see eg. simple_setsize, generic_setattr.
+ */
+int inode_setattr(struct inode *inode, const struct iattr *attr)
+{
+       unsigned int ia_valid = attr->ia_valid;
+
+       if (ia_valid & ATTR_SIZE &&
+           attr->ia_size != i_size_read(inode)) {
+               int error;
+
+               error = vmtruncate(inode, attr->ia_size);
+               if (error)
+                       return error;
+       }
+
+       generic_setattr(inode, attr);
+
        mark_inode_dirty(inode);
 
        return 0;
index a05287a..52e59bf 100644 (file)
@@ -93,8 +93,7 @@ static int bad_file_release(struct inode *inode, struct file *filp)
        return -EIO;
 }
 
-static int bad_file_fsync(struct file *file, struct dentry *dentry,
-                       int datasync)
+static int bad_file_fsync(struct file *file, int datasync)
 {
        return -EIO;
 }
index 8f73841..d967e05 100644 (file)
@@ -78,7 +78,7 @@ static int bfs_readdir(struct file *f, void *dirent, filldir_t filldir)
 const struct file_operations bfs_dir_operations = {
        .read           = generic_read_dir,
        .readdir        = bfs_readdir,
-       .fsync          = simple_fsync,
+       .fsync          = generic_file_fsync,
        .llseek         = generic_file_llseek,
 };
 
index 26e5f50..7346c96 100644 (file)
@@ -172,8 +172,9 @@ blkdev_direct_IO(int rw, struct kiocb *iocb, const struct iovec *iov,
        struct file *file = iocb->ki_filp;
        struct inode *inode = file->f_mapping->host;
 
-       return blockdev_direct_IO_no_locking(rw, iocb, inode, I_BDEV(inode),
-                               iov, offset, nr_segs, blkdev_get_blocks, NULL);
+       return blockdev_direct_IO_no_locking_newtrunc(rw, iocb, inode,
+                               I_BDEV(inode), iov, offset, nr_segs,
+                               blkdev_get_blocks, NULL);
 }
 
 int __sync_blockdev(struct block_device *bdev, int wait)
@@ -309,8 +310,8 @@ static int blkdev_write_begin(struct file *file, struct address_space *mapping,
                        struct page **pagep, void **fsdata)
 {
        *pagep = NULL;
-       return block_write_begin(file, mapping, pos, len, flags, pagep, fsdata,
-                               blkdev_get_block);
+       return block_write_begin_newtrunc(file, mapping, pos, len, flags,
+                               pagep, fsdata, blkdev_get_block);
 }
 
 static int blkdev_write_end(struct file *file, struct address_space *mapping,
@@ -358,12 +359,7 @@ static loff_t block_llseek(struct file *file, loff_t offset, int origin)
        return retval;
 }
        
-/*
- *     Filp is never NULL; the only case when ->fsync() is called with
- *     NULL first argument is nfsd_sync_dir() and that's not a directory.
- */
-int blkdev_fsync(struct file *filp, struct dentry *dentry, int datasync)
+int blkdev_fsync(struct file *filp, int datasync)
 {
        struct inode *bd_inode = filp->f_mapping->host;
        struct block_device *bdev = I_BDEV(bd_inode);
index e9bf864..29c2009 100644 (file)
@@ -2434,7 +2434,7 @@ void btrfs_update_iflags(struct inode *inode);
 void btrfs_inherit_iflags(struct inode *inode, struct inode *dir);
 
 /* file.c */
-int btrfs_sync_file(struct file *file, struct dentry *dentry, int datasync);
+int btrfs_sync_file(struct file *file, int datasync);
 int btrfs_drop_extent_cache(struct inode *inode, u64 start, u64 end,
                            int skip_pinned);
 int btrfs_check_file(struct btrfs_root *root, struct inode *inode);
index 79437c5..787b50a 100644 (file)
@@ -1101,8 +1101,9 @@ int btrfs_release_file(struct inode *inode, struct file *filp)
  * important optimization for directories because holding the mutex prevents
  * new operations on the dir while we write to disk.
  */
-int btrfs_sync_file(struct file *file, struct dentry *dentry, int datasync)
+int btrfs_sync_file(struct file *file, int datasync)
 {
+       struct dentry *dentry = file->f_path.dentry;
        struct inode *inode = dentry->d_inode;
        struct btrfs_root *root = BTRFS_I(inode)->root;
        int ret = 0;
index e8aa708..d54812b 100644 (file)
@@ -1949,14 +1949,11 @@ static int __block_commit_write(struct inode *inode, struct page *page,
 }
 
 /*
- * block_write_begin takes care of the basic task of block allocation and
- * bringing partial write blocks uptodate first.
- *
- * If *pagep is not NULL, then block_write_begin uses the locked page
- * at *pagep rather than allocating its own. In this case, the page will
- * not be unlocked or deallocated on failure.
+ * Filesystems implementing the new truncate sequence should use the
+ * _newtrunc postfix variant which won't incorrectly call vmtruncate.
+ * The filesystem needs to handle block truncation upon failure.
  */
-int block_write_begin(struct file *file, struct address_space *mapping,
+int block_write_begin_newtrunc(struct file *file, struct address_space *mapping,
                        loff_t pos, unsigned len, unsigned flags,
                        struct page **pagep, void **fsdata,
                        get_block_t *get_block)
@@ -1992,20 +1989,50 @@ int block_write_begin(struct file *file, struct address_space *mapping,
                        unlock_page(page);
                        page_cache_release(page);
                        *pagep = NULL;
-
-                       /*
-                        * prepare_write() may have instantiated a few blocks
-                        * outside i_size.  Trim these off again. Don't need
-                        * i_size_read because we hold i_mutex.
-                        */
-                       if (pos + len > inode->i_size)
-                               vmtruncate(inode, inode->i_size);
                }
        }
 
 out:
        return status;
 }
+EXPORT_SYMBOL(block_write_begin_newtrunc);
+
+/*
+ * block_write_begin takes care of the basic task of block allocation and
+ * bringing partial write blocks uptodate first.
+ *
+ * If *pagep is not NULL, then block_write_begin uses the locked page
+ * at *pagep rather than allocating its own. In this case, the page will
+ * not be unlocked or deallocated on failure.
+ */
+int block_write_begin(struct file *file, struct address_space *mapping,
+                       loff_t pos, unsigned len, unsigned flags,
+                       struct page **pagep, void **fsdata,
+                       get_block_t *get_block)
+{
+       int ret;
+
+       ret = block_write_begin_newtrunc(file, mapping, pos, len, flags,
+                                       pagep, fsdata, get_block);
+
+       /*
+        * prepare_write() may have instantiated a few blocks
+        * outside i_size.  Trim these off again. Don't need
+        * i_size_read because we hold i_mutex.
+        *
+        * Filesystems which pass down their own page also cannot
+        * call into vmtruncate here because it would lead to lock
+        * inversion problems (*pagep is locked). This is a further
+        * example of where the old truncate sequence is inadequate.
+        */
+       if (unlikely(ret) && *pagep == NULL) {
+               loff_t isize = mapping->host->i_size;
+               if (pos + len > isize)
+                       vmtruncate(mapping->host, isize);
+       }
+
+       return ret;
+}
 EXPORT_SYMBOL(block_write_begin);
 
 int block_write_end(struct file *file, struct address_space *mapping,
@@ -2324,7 +2351,7 @@ out:
  * For moronic filesystems that do not allow holes in file.
  * We may have to extend the file.
  */
-int cont_write_begin(struct file *file, struct address_space *mapping,
+int cont_write_begin_newtrunc(struct file *file, struct address_space *mapping,
                        loff_t pos, unsigned len, unsigned flags,
                        struct page **pagep, void **fsdata,
                        get_block_t *get_block, loff_t *bytes)
@@ -2345,11 +2372,30 @@ int cont_write_begin(struct file *file, struct address_space *mapping,
        }
 
        *pagep = NULL;
-       err = block_write_begin(file, mapping, pos, len,
+       err = block_write_begin_newtrunc(file, mapping, pos, len,
                                flags, pagep, fsdata, get_block);
 out:
        return err;
 }
+EXPORT_SYMBOL(cont_write_begin_newtrunc);
+
+int cont_write_begin(struct file *file, struct address_space *mapping,
+                       loff_t pos, unsigned len, unsigned flags,
+                       struct page **pagep, void **fsdata,
+                       get_block_t *get_block, loff_t *bytes)
+{
+       int ret;
+
+       ret = cont_write_begin_newtrunc(file, mapping, pos, len, flags,
+                                       pagep, fsdata, get_block, bytes);
+       if (unlikely(ret)) {
+               loff_t isize = mapping->host->i_size;
+               if (pos + len > isize)
+                       vmtruncate(mapping->host, isize);
+       }
+
+       return ret;
+}
 EXPORT_SYMBOL(cont_write_begin);
 
 int block_prepare_write(struct page *page, unsigned from, unsigned to,
@@ -2381,7 +2427,7 @@ EXPORT_SYMBOL(block_commit_write);
  *
  * We are not allowed to take the i_mutex here so we have to play games to
  * protect against truncate races as the page could now be beyond EOF.  Because
- * vmtruncate() writes the inode size before removing pages, once we have the
+ * truncate writes the inode size before removing pages, once we have the
  * page lock we can determine safely if the page is beyond EOF. If it is not
  * beyond EOF, then the page is guaranteed safe against truncation until we
  * unlock the page.
@@ -2464,10 +2510,11 @@ static void attach_nobh_buffers(struct page *page, struct buffer_head *head)
 }
 
 /*
- * On entry, the page is fully not uptodate.
- * On exit the page is fully uptodate in the areas outside (from,to)
+ * Filesystems implementing the new truncate sequence should use the
+ * _newtrunc postfix variant which won't incorrectly call vmtruncate.
+ * The filesystem needs to handle block truncation upon failure.
  */
-int nobh_write_begin(struct file *file, struct address_space *mapping,
+int nobh_write_begin_newtrunc(struct file *file, struct address_space *mapping,
                        loff_t pos, unsigned len, unsigned flags,
                        struct page **pagep, void **fsdata,
                        get_block_t *get_block)
@@ -2500,8 +2547,8 @@ int nobh_write_begin(struct file *file, struct address_space *mapping,
                unlock_page(page);
                page_cache_release(page);
                *pagep = NULL;
-               return block_write_begin(file, mapping, pos, len, flags, pagep,
-                                       fsdata, get_block);
+               return block_write_begin_newtrunc(file, mapping, pos, len,
+                                       flags, pagep, fsdata, get_block);
        }
 
        if (PageMappedToDisk(page))
@@ -2605,8 +2652,34 @@ out_release:
        page_cache_release(page);
        *pagep = NULL;
 
-       if (pos + len > inode->i_size)
-               vmtruncate(inode, inode->i_size);
+       return ret;
+}
+EXPORT_SYMBOL(nobh_write_begin_newtrunc);
+
+/*
+ * On entry, the page is fully not uptodate.
+ * On exit the page is fully uptodate in the areas outside (from,to)
+ */
+int nobh_write_begin(struct file *file, struct address_space *mapping,
+                       loff_t pos, unsigned len, unsigned flags,
+                       struct page **pagep, void **fsdata,
+                       get_block_t *get_block)
+{
+       int ret;
+
+       ret = nobh_write_begin_newtrunc(file, mapping, pos, len, flags,
+                                       pagep, fsdata, get_block);
+
+       /*
+        * prepare_write() may have instantiated a few blocks
+        * outside i_size.  Trim these off again. Don't need
+        * i_size_read because we hold i_mutex.
+        */
+       if (unlikely(ret)) {
+               loff_t isize = mapping->host->i_size;
+               if (pos + len > isize)
+                       vmtruncate(mapping->host, isize);
+       }
 
        return ret;
 }
index 9f46de2..89490be 100644 (file)
@@ -1,7 +1,6 @@
 #include "ceph_debug.h"
 
 #include <linux/module.h>
-#include <linux/slab.h>
 #include <linux/err.h>
 #include <linux/slab.h>
 
@@ -217,8 +216,8 @@ int ceph_handle_auth_reply(struct ceph_auth_client *ac,
                if (ac->protocol != protocol) {
                        ret = ceph_auth_init_protocol(ac, protocol);
                        if (ret) {
-                               pr_err("error %d on auth method %s init\n",
-                                      ret, ac->ops->name);
+                               pr_err("error %d on auth protocol %d init\n",
+                                      ret, protocol);
                                goto out;
                        }
                }
@@ -247,7 +246,7 @@ int ceph_build_auth(struct ceph_auth_client *ac,
        if (!ac->protocol)
                return ceph_auth_build_hello(ac, msg_buf, msg_len);
        BUG_ON(!ac->ops);
-       if (!ac->ops->is_authenticated(ac))
+       if (ac->ops->should_authenticate(ac))
                return ceph_build_auth_request(ac, msg_buf, msg_len);
        return 0;
 }
index 4429a70..d38a2fb 100644 (file)
@@ -24,6 +24,12 @@ struct ceph_auth_client_ops {
        int (*is_authenticated)(struct ceph_auth_client *ac);
 
        /*
+        * true if we should (re)authenticate, e.g., when our tickets
+        * are getting old and crusty.
+        */
+       int (*should_authenticate)(struct ceph_auth_client *ac);
+
+       /*
         * build requests and process replies during monitor
         * handshake.  if handle_reply returns -EAGAIN, we build
         * another request.
index 24407c1..ad1dc21 100644 (file)
@@ -31,6 +31,13 @@ static int is_authenticated(struct ceph_auth_client *ac)
        return !xi->starting;
 }
 
+static int should_authenticate(struct ceph_auth_client *ac)
+{
+       struct ceph_auth_none_info *xi = ac->private;
+
+       return xi->starting;
+}
+
 /*
  * the generic auth code decode the global_id, and we carry no actual
  * authenticate state, so nothing happens here.
@@ -98,6 +105,7 @@ static const struct ceph_auth_client_ops ceph_auth_none_ops = {
        .reset = reset,
        .destroy = destroy,
        .is_authenticated = is_authenticated,
+       .should_authenticate = should_authenticate,
        .handle_reply = handle_reply,
        .create_authorizer = ceph_auth_none_create_authorizer,
        .destroy_authorizer = ceph_auth_none_destroy_authorizer,
index 7b20623..83d4d27 100644 (file)
@@ -27,6 +27,17 @@ static int ceph_x_is_authenticated(struct ceph_auth_client *ac)
        return (ac->want_keys & xi->have_keys) == ac->want_keys;
 }
 
+static int ceph_x_should_authenticate(struct ceph_auth_client *ac)
+{
+       struct ceph_x_info *xi = ac->private;
+       int need;
+
+       ceph_x_validate_tickets(ac, &need);
+       dout("ceph_x_should_authenticate want=%d need=%d have=%d\n",
+            ac->want_keys, need, xi->have_keys);
+       return need != 0;
+}
+
 static int ceph_x_encrypt_buflen(int ilen)
 {
        return sizeof(struct ceph_x_encrypt_header) + ilen + 16 +
@@ -620,6 +631,7 @@ static void ceph_x_invalidate_authorizer(struct ceph_auth_client *ac,
 static const struct ceph_auth_client_ops ceph_x_ops = {
        .name = "x",
        .is_authenticated = ceph_x_is_authenticated,
+       .should_authenticate = ceph_x_should_authenticate,
        .build_request = ceph_x_build_request,
        .handle_reply = ceph_x_handle_reply,
        .create_authorizer = ceph_x_create_authorizer,
index 0dd0b81..ae3e3a3 100644 (file)
@@ -1776,9 +1776,9 @@ out:
        spin_unlock(&ci->i_unsafe_lock);
 }
 
-int ceph_fsync(struct file *file, struct dentry *dentry, int datasync)
+int ceph_fsync(struct file *file, int datasync)
 {
-       struct inode *inode = dentry->d_inode;
+       struct inode *inode = file->f_mapping->host;
        struct ceph_inode_info *ci = ceph_inode(inode);
        unsigned flush_tid;
        int ret;
index 3b9eeed..2fa992e 100644 (file)
@@ -265,16 +265,17 @@ extern const char *ceph_mds_state_name(int s);
  *  - they also define the lock ordering by the MDS
  *  - a few of these are internal to the mds
  */
-#define CEPH_LOCK_DN          1
-#define CEPH_LOCK_ISNAP       2
-#define CEPH_LOCK_IVERSION    4     /* mds internal */
-#define CEPH_LOCK_IFILE       8     /* mds internal */
-#define CEPH_LOCK_IAUTH       32
-#define CEPH_LOCK_ILINK       64
-#define CEPH_LOCK_IDFT        128   /* dir frag tree */
-#define CEPH_LOCK_INEST       256   /* mds internal */
-#define CEPH_LOCK_IXATTR      512
-#define CEPH_LOCK_INO         2048  /* immutable inode bits; not a lock */
+#define CEPH_LOCK_DVERSION    1
+#define CEPH_LOCK_DN          2
+#define CEPH_LOCK_ISNAP       16
+#define CEPH_LOCK_IVERSION    32    /* mds internal */
+#define CEPH_LOCK_IFILE       64
+#define CEPH_LOCK_IAUTH       128
+#define CEPH_LOCK_ILINK       256
+#define CEPH_LOCK_IDFT        512   /* dir frag tree */
+#define CEPH_LOCK_INEST       1024  /* mds internal */
+#define CEPH_LOCK_IXATTR      2048
+#define CEPH_LOCK_INO         8192  /* immutable inode bits; not a lock */
 
 /* client_session ops */
 enum {
index 4fd3090..f857193 100644 (file)
@@ -587,7 +587,7 @@ static struct dentry *ceph_lookup(struct inode *dir, struct dentry *dentry,
                CEPH_MDS_OP_LOOKUPSNAP : CEPH_MDS_OP_LOOKUP;
        req = ceph_mdsc_create_request(mdsc, op, USE_ANY_MDS);
        if (IS_ERR(req))
-               return ERR_PTR(PTR_ERR(req));
+               return ERR_CAST(req);
        req->r_dentry = dget(dentry);
        req->r_num_caps = 2;
        /* we only need inode linkage */
@@ -1107,10 +1107,9 @@ static ssize_t ceph_read_dir(struct file *file, char __user *buf, size_t size,
  * an fsync() on a dir will wait for any uncommitted directory
  * operations to commit.
  */
-static int ceph_dir_fsync(struct file *file, struct dentry *dentry,
-                         int datasync)
+static int ceph_dir_fsync(struct file *file, int datasync)
 {
-       struct inode *inode = dentry->d_inode;
+       struct inode *inode = file->f_path.dentry->d_inode;
        struct ceph_inode_info *ci = ceph_inode(inode);
        struct list_head *head = &ci->i_unsafe_dirops;
        struct ceph_mds_request *req;
index 1744764..4480cb1 100644 (file)
@@ -133,7 +133,7 @@ static struct dentry *__cfh_to_dentry(struct super_block *sb,
                req = ceph_mdsc_create_request(mdsc, CEPH_MDS_OP_LOOKUPHASH,
                                               USE_ANY_MDS);
                if (IS_ERR(req))
-                       return ERR_PTR(PTR_ERR(req));
+                       return ERR_CAST(req);
 
                req->r_ino1 = vino;
                req->r_ino2.ino = cfh->parent_ino;
index 6512b67..6251a15 100644 (file)
@@ -230,7 +230,7 @@ struct dentry *ceph_lookup_open(struct inode *dir, struct dentry *dentry,
        /* do the open */
        req = prepare_open_request(dir->i_sb, flags, mode);
        if (IS_ERR(req))
-               return ERR_PTR(PTR_ERR(req));
+               return ERR_CAST(req);
        req->r_dentry = dget(dentry);
        req->r_num_caps = 2;
        if (flags & O_CREAT) {
index a81b8b6..226f5a5 100644 (file)
@@ -69,7 +69,7 @@ struct inode *ceph_get_snapdir(struct inode *parent)
 
        BUG_ON(!S_ISDIR(parent->i_mode));
        if (IS_ERR(inode))
-               return ERR_PTR(PTR_ERR(inode));
+               return inode;
        inode->i_mode = parent->i_mode;
        inode->i_uid = parent->i_uid;
        inode->i_gid = parent->i_gid;
index 885aa57..b49f128 100644 (file)
@@ -1768,12 +1768,12 @@ int ceph_mdsc_do_request(struct ceph_mds_client *mdsc,
        mutex_unlock(&mdsc->mutex);
        dout("do_request waiting\n");
        if (req->r_timeout) {
-               err = (long)wait_for_completion_interruptible_timeout(
+               err = (long)wait_for_completion_killable_timeout(
                        &req->r_completion, req->r_timeout);
                if (err == 0)
                        err = -EIO;
        } else {
-               err = wait_for_completion_interruptible(&req->r_completion);
+               err = wait_for_completion_killable(&req->r_completion);
        }
        dout("do_request waited, got %d\n", err);
        mutex_lock(&mdsc->mutex);
@@ -2014,16 +2014,21 @@ static void handle_forward(struct ceph_mds_client *mdsc,
        mutex_lock(&mdsc->mutex);
        req = __lookup_request(mdsc, tid);
        if (!req) {
-               dout("forward %llu to mds%d - req dne\n", tid, next_mds);
+               dout("forward tid %llu to mds%d - req dne\n", tid, next_mds);
                goto out;  /* dup reply? */
        }
 
-       if (fwd_seq <= req->r_num_fwd) {
-               dout("forward %llu to mds%d - old seq %d <= %d\n",
+       if (req->r_aborted) {
+               dout("forward tid %llu aborted, unregistering\n", tid);
+               __unregister_request(mdsc, req);
+       } else if (fwd_seq <= req->r_num_fwd) {
+               dout("forward tid %llu to mds%d - old seq %d <= %d\n",
                     tid, next_mds, req->r_num_fwd, fwd_seq);
        } else {
                /* resend. forward race not possible; mds would drop */
-               dout("forward %llu to mds%d (we resend)\n", tid, next_mds);
+               dout("forward tid %llu to mds%d (we resend)\n", tid, next_mds);
+               BUG_ON(req->r_err);
+               BUG_ON(req->r_got_result);
                req->r_num_fwd = fwd_seq;
                req->r_resend_mds = next_mds;
                put_request_session(req);
@@ -2541,7 +2546,7 @@ void ceph_mdsc_lease_send_msg(struct ceph_mds_session *session,
                return;
        lease = msg->front.iov_base;
        lease->action = action;
-       lease->mask = cpu_to_le16(CEPH_LOCK_DN);
+       lease->mask = cpu_to_le16(1);
        lease->ino = cpu_to_le64(ceph_vino(inode).ino);
        lease->first = lease->last = cpu_to_le64(ceph_vino(inode).snap);
        lease->seq = cpu_to_le32(seq);
@@ -2571,7 +2576,7 @@ void ceph_mdsc_lease_release(struct ceph_mds_client *mdsc, struct inode *inode,
 
        BUG_ON(inode == NULL);
        BUG_ON(dentry == NULL);
-       BUG_ON(mask != CEPH_LOCK_DN);
+       BUG_ON(mask == 0);
 
        /* is dentry lease valid? */
        spin_lock(&dentry->d_lock);
index 60b7483..64b8b1f 100644 (file)
@@ -120,6 +120,12 @@ void ceph_msgr_exit(void)
        destroy_workqueue(ceph_msgr_wq);
 }
 
+void ceph_msgr_flush()
+{
+       flush_workqueue(ceph_msgr_wq);
+}
+
+
 /*
  * socket callback functions
  */
index 00a9430..76fbc95 100644 (file)
@@ -213,6 +213,7 @@ extern int ceph_parse_ips(const char *c, const char *end,
 
 extern int ceph_msgr_init(void);
 extern void ceph_msgr_exit(void);
+extern void ceph_msgr_flush(void);
 
 extern struct ceph_messenger *ceph_messenger_create(
        struct ceph_entity_addr *myaddr);
index f6510a4..21c62e9 100644 (file)
@@ -704,8 +704,11 @@ static void handle_auth_reply(struct ceph_mon_client *monc,
                              struct ceph_msg *msg)
 {
        int ret;
+       int was_auth = 0;
 
        mutex_lock(&monc->mutex);
+       if (monc->auth->ops)
+               was_auth = monc->auth->ops->is_authenticated(monc->auth);
        monc->pending_auth = 0;
        ret = ceph_handle_auth_reply(monc->auth, msg->front.iov_base,
                                     msg->front.iov_len,
@@ -716,7 +719,7 @@ static void handle_auth_reply(struct ceph_mon_client *monc,
                wake_up(&monc->client->auth_wq);
        } else if (ret > 0) {
                __send_prepared_auth_request(monc, ret);
-       } else if (monc->auth->ops->is_authenticated(monc->auth)) {
+       } else if (!was_auth && monc->auth->ops->is_authenticated(monc->auth)) {
                dout("authenticated, starting session\n");
 
                monc->client->msgr->inst.name.type = CEPH_ENTITY_TYPE_CLIENT;
index afa7bb3..d25b4ad 100644 (file)
@@ -361,8 +361,13 @@ static void put_osd(struct ceph_osd *osd)
 {
        dout("put_osd %p %d -> %d\n", osd, atomic_read(&osd->o_ref),
             atomic_read(&osd->o_ref) - 1);
-       if (atomic_dec_and_test(&osd->o_ref))
+       if (atomic_dec_and_test(&osd->o_ref)) {
+               struct ceph_auth_client *ac = osd->o_osdc->client->monc.auth;
+
+               if (osd->o_authorizer)
+                       ac->ops->destroy_authorizer(ac, osd->o_authorizer);
                kfree(osd);
+       }
 }
 
 /*
index cfdd8f4..ddc656f 100644 (file)
@@ -706,7 +706,7 @@ struct ceph_osdmap *osdmap_apply_incremental(void **p, void *end,
                     len, *p, end);
                newcrush = crush_decode(*p, min(*p+len, end));
                if (IS_ERR(newcrush))
-                       return ERR_PTR(PTR_ERR(newcrush));
+                       return ERR_CAST(newcrush);
        }
 
        /* new flags? */
index 7c663d9..4e0bee2 100644 (file)
@@ -669,9 +669,17 @@ static void ceph_destroy_client(struct ceph_client *client)
 
        /* unmount */
        ceph_mdsc_stop(&client->mdsc);
-       ceph_monc_stop(&client->monc);
        ceph_osdc_stop(&client->osdc);
 
+       /*
+        * make sure mds and osd connections close out before destroying
+        * the auth module, which is needed to free those connections'
+        * ceph_authorizers.
+        */
+       ceph_msgr_flush();
+
+       ceph_monc_stop(&client->monc);
+
        ceph_adjust_min_caps(-client->min_caps);
 
        ceph_debugfs_client_cleanup(client);
@@ -738,7 +746,7 @@ static struct dentry *open_root_dentry(struct ceph_client *client,
        dout("open_root_inode opening '%s'\n", path);
        req = ceph_mdsc_create_request(mdsc, CEPH_MDS_OP_GETATTR, USE_ANY_MDS);
        if (IS_ERR(req))
-               return ERR_PTR(PTR_ERR(req));
+               return ERR_CAST(req);
        req->r_path1 = kstrdup(path, GFP_NOFS);
        req->r_ino1.ino = CEPH_INO_ROOT;
        req->r_ino1.snap = CEPH_NOSNAP;
index 3725c9e..10a4a40 100644 (file)
@@ -10,7 +10,6 @@
 #include <linux/fs.h>
 #include <linux/mempool.h>
 #include <linux/pagemap.h>
-#include <linux/slab.h>
 #include <linux/wait.h>
 #include <linux/writeback.h>
 #include <linux/slab.h>
@@ -811,7 +810,7 @@ extern void ceph_put_cap(struct ceph_cap *cap);
 
 extern void ceph_queue_caps_release(struct inode *inode);
 extern int ceph_write_inode(struct inode *inode, struct writeback_control *wbc);
-extern int ceph_fsync(struct file *file, struct dentry *dentry, int datasync);
+extern int ceph_fsync(struct file *file, int datasync);
 extern void ceph_kick_flushing_caps(struct ceph_mds_client *mdsc,
                                    struct ceph_mds_session *session);
 extern int ceph_get_cap_mds(struct inode *inode);
index 0242ff9..a7eb65c 100644 (file)
@@ -84,7 +84,7 @@ extern ssize_t cifs_user_read(struct file *file, char __user *read_data,
 extern ssize_t cifs_user_write(struct file *file, const char __user *write_data,
                         size_t write_size, loff_t *poffset);
 extern int cifs_lock(struct file *, int, struct file_lock *);
-extern int cifs_fsync(struct file *, struct dentry *, int);
+extern int cifs_fsync(struct file *, int);
 extern int cifs_flush(struct file *, fl_owner_t id);
 extern int cifs_file_mmap(struct file * , struct vm_area_struct *);
 extern const struct file_operations cifs_dir_ops;
index a83541e..f1ff785 100644 (file)
@@ -1676,7 +1676,7 @@ static int cifs_write_end(struct file *file, struct address_space *mapping,
        return rc;
 }
 
-int cifs_fsync(struct file *file, struct dentry *dentry, int datasync)
+int cifs_fsync(struct file *file, int datasync)
 {
        int xid;
        int rc = 0;
@@ -1688,7 +1688,7 @@ int cifs_fsync(struct file *file, struct dentry *dentry, int datasync)
        xid = GetXid();
 
        cFYI(1, "Sync file - name: %s datasync: 0x%x",
-               dentry->d_name.name, datasync);
+               file->f_path.dentry->d_name.name, datasync);
 
        rc = filemap_write_and_wait(inode->i_mapping);
        if (rc == 0) {
index d99860a..6b443ff 100644 (file)
@@ -11,8 +11,7 @@ extern int coda_fake_statfs;
 
 void coda_destroy_inodecache(void);
 int coda_init_inodecache(void);
-int coda_fsync(struct file *coda_file, struct dentry *coda_dentry,
-              int datasync);
+int coda_fsync(struct file *coda_file, int datasync);
 void coda_sysctl_init(void);
 void coda_sysctl_clean(void);
 
index 7196077..ad3cd2a 100644 (file)
@@ -202,10 +202,10 @@ int coda_release(struct inode *coda_inode, struct file *coda_file)
        return 0;
 }
 
-int coda_fsync(struct file *coda_file, struct dentry *coda_dentry, int datasync)
+int coda_fsync(struct file *coda_file, int datasync)
 {
        struct file *host_file;
-       struct inode *coda_inode = coda_dentry->d_inode;
+       struct inode *coda_inode = coda_file->f_path.dentry->d_inode;
        struct coda_file_info *cfi;
        int err = 0;
 
index c8af2d9..4164514 100644 (file)
@@ -72,16 +72,11 @@ int configfs_setattr(struct dentry * dentry, struct iattr * iattr)
        if (!sd)
                return -EINVAL;
 
-       sd_iattr = sd->s_iattr;
-
-       error = inode_change_ok(inode, iattr);
-       if (error)
-               return error;
-
-       error = inode_setattr(inode, iattr);
+       error = simple_setattr(dentry, iattr);
        if (error)
                return error;
 
+       sd_iattr = sd->s_iattr;
        if (!sd_iattr) {
                /* setting attributes for the first time, allocate now */
                sd_iattr = kzalloc(sizeof(struct iattr), GFP_KERNEL);
index 4d74fc7..0210898 100644 (file)
@@ -277,8 +277,10 @@ DEFINE_SIMPLE_ATTRIBUTE(fops_x32, debugfs_u32_get, debugfs_u32_set, "0x%08llx\n"
 DEFINE_SIMPLE_ATTRIBUTE(fops_x32_ro, debugfs_u32_get, NULL, "0x%08llx\n");
 DEFINE_SIMPLE_ATTRIBUTE(fops_x32_wo, NULL, debugfs_u32_set, "0x%08llx\n");
 
+DEFINE_SIMPLE_ATTRIBUTE(fops_x64, debugfs_u64_get, debugfs_u64_set, "0x%016llx\n");
+
 /*
- * debugfs_create_x{8,16,32} - create a debugfs file that is used to read and write an unsigned {8,16,32}-bit value
+ * debugfs_create_x{8,16,32,64} - create a debugfs file that is used to read and write an unsigned {8,16,32,64}-bit value
  *
  * These functions are exactly the same as the above functions (but use a hex
  * output for the decimal challenged). For details look at the above unsigned
@@ -357,6 +359,23 @@ struct dentry *debugfs_create_x32(const char *name, mode_t mode,
 }
 EXPORT_SYMBOL_GPL(debugfs_create_x32);
 
+/**
+ * debugfs_create_x64 - create a debugfs file that is used to read and write an unsigned 64-bit value
+ * @name: a pointer to a string containing the name of the file to create.
+ * @mode: the permission that the file should have
+ * @parent: a pointer to the parent dentry for this file.  This should be a
+ *          directory dentry if set.  If this parameter is %NULL, then the
+ *          file will be created in the root of the debugfs filesystem.
+ * @value: a pointer to the variable that the file should read to and write
+ *         from.
+ */
+struct dentry *debugfs_create_x64(const char *name, mode_t mode,
+                                struct dentry *parent, u64 *value)
+{
+       return debugfs_create_file(name, mode, parent, value, &fops_x64);
+}
+EXPORT_SYMBOL_GPL(debugfs_create_x64);
+
 
 static int debugfs_size_t_set(void *data, u64 val)
 {
index da111aa..7600aac 100644 (file)
@@ -1134,27 +1134,8 @@ direct_io_worker(int rw, struct kiocb *iocb, struct inode *inode,
        return ret;
 }
 
-/*
- * This is a library function for use by filesystem drivers.
- *
- * The locking rules are governed by the flags parameter:
- *  - if the flags value contains DIO_LOCKING we use a fancy locking
- *    scheme for dumb filesystems.
- *    For writes this function is called under i_mutex and returns with
- *    i_mutex held, for reads, i_mutex is not held on entry, but it is
- *    taken and dropped again before returning.
- *    For reads and writes i_alloc_sem is taken in shared mode and released
- *    on I/O completion (which may happen asynchronously after returning to
- *    the caller).
- *
- *  - if the flags value does NOT contain DIO_LOCKING we don't use any
- *    internal locking but rather rely on the filesystem to synchronize
- *    direct I/O reads/writes versus each other and truncate.
- *    For reads and writes both i_mutex and i_alloc_sem are not held on
- *    entry and are never taken.
- */
 ssize_t
-__blockdev_direct_IO(int rw, struct kiocb *iocb, struct inode *inode,
+__blockdev_direct_IO_newtrunc(int rw, struct kiocb *iocb, struct inode *inode,
        struct block_device *bdev, const struct iovec *iov, loff_t offset, 
        unsigned long nr_segs, get_block_t get_block, dio_iodone_t end_io,
        dio_submit_t submit_io, int flags)
@@ -1247,9 +1228,46 @@ __blockdev_direct_IO(int rw, struct kiocb *iocb, struct inode *inode,
                                nr_segs, blkbits, get_block, end_io,
                                submit_io, dio);
 
+out:
+       return retval;
+}
+EXPORT_SYMBOL(__blockdev_direct_IO_newtrunc);
+
+/*
+ * This is a library function for use by filesystem drivers.
+ *
+ * The locking rules are governed by the flags parameter:
+ *  - if the flags value contains DIO_LOCKING we use a fancy locking
+ *    scheme for dumb filesystems.
+ *    For writes this function is called under i_mutex and returns with
+ *    i_mutex held, for reads, i_mutex is not held on entry, but it is
+ *    taken and dropped again before returning.
+ *    For reads and writes i_alloc_sem is taken in shared mode and released
+ *    on I/O completion (which may happen asynchronously after returning to
+ *    the caller).
+ *
+ *  - if the flags value does NOT contain DIO_LOCKING we don't use any
+ *    internal locking but rather rely on the filesystem to synchronize
+ *    direct I/O reads/writes versus each other and truncate.
+ *    For reads and writes both i_mutex and i_alloc_sem are not held on
+ *    entry and are never taken.
+ */
+ssize_t
+__blockdev_direct_IO(int rw, struct kiocb *iocb, struct inode *inode,
+       struct block_device *bdev, const struct iovec *iov, loff_t offset,
+       unsigned long nr_segs, get_block_t get_block, dio_iodone_t end_io,
+       dio_submit_t submit_io, int flags)
+{
+       ssize_t retval;
+
+       retval = __blockdev_direct_IO_newtrunc(rw, iocb, inode, bdev, iov,
+                       offset, nr_segs, get_block, end_io, submit_io, flags);
        /*
         * In case of error extending write may have instantiated a few
         * blocks outside i_size. Trim these off again for DIO_LOCKING.
+        * NOTE: DIO_NO_LOCK/DIO_OWN_LOCK callers have to handle this in
+        * their own manner. This is a further example of where the old
+        * truncate sequence is inadequate.
         *
         * NOTE: filesystems with their own locking have to handle this
         * on their own.
@@ -1257,12 +1275,13 @@ __blockdev_direct_IO(int rw, struct kiocb *iocb, struct inode *inode,
        if (flags & DIO_LOCKING) {
                if (unlikely((rw & WRITE) && retval < 0)) {
                        loff_t isize = i_size_read(inode);
+                       loff_t end = offset + iov_length(iov, nr_segs);
+
                        if (end > isize)
                                vmtruncate(inode, isize);
                }
        }
 
-out:
        return retval;
 }
 EXPORT_SYMBOL(__blockdev_direct_IO);
index 3bdddbc..e8fcf4e 100644 (file)
@@ -274,7 +274,7 @@ static int ecryptfs_release(struct inode *inode, struct file *file)
 }
 
 static int
-ecryptfs_fsync(struct file *file, struct dentry *dentry, int datasync)
+ecryptfs_fsync(struct file *file, int datasync)
 {
        return vfs_fsync(ecryptfs_file_to_lower(file), datasync);
 }
index 65dee2f..31ef525 100644 (file)
@@ -805,7 +805,7 @@ static int truncate_upper(struct dentry *dentry, struct iattr *ia,
                                    - (ia->ia_size & ~PAGE_CACHE_MASK));
 
                if (!(crypt_stat->flags & ECRYPTFS_ENCRYPTED)) {
-                       rc = vmtruncate(inode, ia->ia_size);
+                       rc = simple_setsize(inode, ia->ia_size);
                        if (rc)
                                goto out;
                        lower_ia->ia_size = ia->ia_size;
@@ -830,7 +830,7 @@ static int truncate_upper(struct dentry *dentry, struct iattr *ia,
                                goto out;
                        }
                }
-               vmtruncate(inode, ia->ia_size);
+               simple_setsize(inode, ia->ia_size);
                rc = ecryptfs_write_inode_size_to_metadata(inode);
                if (rc) {
                        printk(KERN_ERR "Problem with "
index 839b9dc..fef6899 100644 (file)
@@ -40,12 +40,11 @@ static int exofs_release_file(struct inode *inode, struct file *filp)
        return 0;
 }
 
-static int exofs_file_fsync(struct file *filp, struct dentry *dentry,
-                           int datasync)
+static int exofs_file_fsync(struct file *filp, int datasync)
 {
        int ret;
        struct address_space *mapping = filp->f_mapping;
-       struct inode *inode = dentry->d_inode;
+       struct inode *inode = mapping->host;
        struct super_block *sb;
 
        ret = filemap_write_and_wait(mapping);
@@ -66,7 +65,7 @@ static int exofs_file_fsync(struct file *filp, struct dentry *dentry,
 
 static int exofs_flush(struct file *file, fl_owner_t id)
 {
-       exofs_file_fsync(file, file->f_path.dentry, 1);
+       exofs_file_fsync(file, 1);
        /* TODO: Flush the OSD target */
        return 0;
 }
index 0b038e4..52b34f1 100644 (file)
@@ -122,7 +122,6 @@ extern int ext2_write_inode (struct inode *, struct writeback_control *);
 extern void ext2_delete_inode (struct inode *);
 extern int ext2_sync_inode (struct inode *);
 extern int ext2_get_block(struct inode *, sector_t, struct buffer_head *, int);
-extern void ext2_truncate (struct inode *);
 extern int ext2_setattr (struct dentry *, struct iattr *);
 extern void ext2_set_inode_flags(struct inode *inode);
 extern void ext2_get_inode_flags(struct ext2_inode_info *);
@@ -155,7 +154,7 @@ extern void ext2_write_super (struct super_block *);
 extern const struct file_operations ext2_dir_operations;
 
 /* file.c */
-extern int ext2_fsync(struct file *file, struct dentry *dentry, int datasync);
+extern int ext2_fsync(struct file *file, int datasync);
 extern const struct inode_operations ext2_file_inode_operations;
 extern const struct file_operations ext2_file_operations;
 extern const struct file_operations ext2_xip_file_operations;
index 5d198d0..49eec94 100644 (file)
@@ -40,13 +40,13 @@ static int ext2_release_file (struct inode * inode, struct file * filp)
        return 0;
 }
 
-int ext2_fsync(struct file *file, struct dentry *dentry, int datasync)
+int ext2_fsync(struct file *file, int datasync)
 {
        int ret;
-       struct super_block *sb = dentry->d_inode->i_sb;
+       struct super_block *sb = file->f_mapping->host->i_sb;
        struct address_space *mapping = sb->s_bdev->bd_inode->i_mapping;
 
-       ret = simple_fsync(file, dentry, datasync);
+       ret = generic_file_fsync(file, datasync);
        if (ret == -EIO || test_and_clear_bit(AS_EIO, &mapping->flags)) {
                /* We don't really know where the IO error happened... */
                ext2_error(sb, __func__,
@@ -95,7 +95,6 @@ const struct file_operations ext2_xip_file_operations = {
 #endif
 
 const struct inode_operations ext2_file_inode_operations = {
-       .truncate       = ext2_truncate,
 #ifdef CONFIG_EXT2_FS_XATTR
        .setxattr       = generic_setxattr,
        .getxattr       = generic_getxattr,
index 527c46d..1921443 100644 (file)
@@ -54,6 +54,18 @@ static inline int ext2_inode_is_fast_symlink(struct inode *inode)
                inode->i_blocks - ea_blocks == 0);
 }
 
+static void ext2_truncate_blocks(struct inode *inode, loff_t offset);
+
+static void ext2_write_failed(struct address_space *mapping, loff_t to)
+{
+       struct inode *inode = mapping->host;
+
+       if (to > inode->i_size) {
+               truncate_pagecache(inode, to, inode->i_size);
+               ext2_truncate_blocks(inode, inode->i_size);
+       }
+}
+
 /*
  * Called at the last iput() if i_nlink is zero.
  */
@@ -71,7 +83,7 @@ void ext2_delete_inode (struct inode * inode)
 
        inode->i_size = 0;
        if (inode->i_blocks)
-               ext2_truncate (inode);
+               ext2_truncate_blocks(inode, 0);
        ext2_free_inode (inode);
 
        return;
@@ -757,8 +769,8 @@ int __ext2_write_begin(struct file *file, struct address_space *mapping,
                loff_t pos, unsigned len, unsigned flags,
                struct page **pagep, void **fsdata)
 {
-       return block_write_begin(file, mapping, pos, len, flags, pagep, fsdata,
-                                                       ext2_get_block);
+       return block_write_begin_newtrunc(file, mapping, pos, len, flags,
+                                       pagep, fsdata, ext2_get_block);
 }
 
 static int
@@ -766,8 +778,25 @@ ext2_write_begin(struct file *file, struct address_space *mapping,
                loff_t pos, unsigned len, unsigned flags,
                struct page **pagep, void **fsdata)
 {
+       int ret;
+
        *pagep = NULL;
-       return __ext2_write_begin(file, mapping, pos, len, flags, pagep,fsdata);
+       ret = __ext2_write_begin(file, mapping, pos, len, flags, pagep, fsdata);
+       if (ret < 0)
+               ext2_write_failed(mapping, pos + len);
+       return ret;
+}
+
+static int ext2_write_end(struct file *file, struct address_space *mapping,
+                       loff_t pos, unsigned len, unsigned copied,
+                       struct page *page, void *fsdata)
+{
+       int ret;
+
+       ret = generic_write_end(file, mapping, pos, len, copied, page, fsdata);
+       if (ret < len)
+               ext2_write_failed(mapping, pos + len);
+       return ret;
 }
 
 static int
@@ -775,13 +804,18 @@ ext2_nobh_write_begin(struct file *file, struct address_space *mapping,
                loff_t pos, unsigned len, unsigned flags,
                struct page **pagep, void **fsdata)
 {
+       int ret;
+
        /*
         * Dir-in-pagecache still uses ext2_write_begin. Would have to rework
         * directory handling code to pass around offsets rather than struct
         * pages in order to make this work easily.
         */
-       return nobh_write_begin(file, mapping, pos, len, flags, pagep, fsdata,
-                                                       ext2_get_block);
+       ret = nobh_write_begin_newtrunc(file, mapping, pos, len, flags, pagep,
+                                               fsdata, ext2_get_block);
+       if (ret < 0)
+               ext2_write_failed(mapping, pos + len);
+       return ret;
 }
 
 static int ext2_nobh_writepage(struct page *page,
@@ -800,10 +834,15 @@ ext2_direct_IO(int rw, struct kiocb *iocb, const struct iovec *iov,
                        loff_t offset, unsigned long nr_segs)
 {
        struct file *file = iocb->ki_filp;
-       struct inode *inode = file->f_mapping->host;
-
-       return blockdev_direct_IO(rw, iocb, inode, inode->i_sb->s_bdev, iov,
-                               offset, nr_segs, ext2_get_block, NULL);
+       struct address_space *mapping = file->f_mapping;
+       struct inode *inode = mapping->host;
+       ssize_t ret;
+
+       ret = blockdev_direct_IO_newtrunc(rw, iocb, inode, inode->i_sb->s_bdev,
+                               iov, offset, nr_segs, ext2_get_block, NULL);
+       if (ret < 0 && (rw & WRITE))
+               ext2_write_failed(mapping, offset + iov_length(iov, nr_segs));
+       return ret;
 }
 
 static int
@@ -818,7 +857,7 @@ const struct address_space_operations ext2_aops = {
        .writepage              = ext2_writepage,
        .sync_page              = block_sync_page,
        .write_begin            = ext2_write_begin,
-       .write_end              = generic_write_end,
+       .write_end              = ext2_write_end,
        .bmap                   = ext2_bmap,
        .direct_IO              = ext2_direct_IO,
        .writepages             = ext2_writepages,
@@ -1027,7 +1066,7 @@ static void ext2_free_branches(struct inode *inode, __le32 *p, __le32 *q, int de
                ext2_free_data(inode, p, q);
 }
 
-void ext2_truncate(struct inode *inode)
+static void __ext2_truncate_blocks(struct inode *inode, loff_t offset)
 {
        __le32 *i_data = EXT2_I(inode)->i_data;
        struct ext2_inode_info *ei = EXT2_I(inode);
@@ -1039,27 +1078,8 @@ void ext2_truncate(struct inode *inode)
        int n;
        long iblock;
        unsigned blocksize;
-
-       if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) ||
-           S_ISLNK(inode->i_mode)))
-               return;
-       if (ext2_inode_is_fast_symlink(inode))
-               return;
-       if (IS_APPEND(inode) || IS_IMMUTABLE(inode))
-               return;
-
        blocksize = inode->i_sb->s_blocksize;
-       iblock = (inode->i_size + blocksize-1)
-                                       >> EXT2_BLOCK_SIZE_BITS(inode->i_sb);
-
-       if (mapping_is_xip(inode->i_mapping))
-               xip_truncate_page(inode->i_mapping, inode->i_size);
-       else if (test_opt(inode->i_sb, NOBH))
-               nobh_truncate_page(inode->i_mapping,
-                               inode->i_size, ext2_get_block);
-       else
-               block_truncate_page(inode->i_mapping,
-                               inode->i_size, ext2_get_block);
+       iblock = (offset + blocksize-1) >> EXT2_BLOCK_SIZE_BITS(inode->i_sb);
 
        n = ext2_block_to_path(inode, iblock, offsets, NULL);
        if (n == 0)
@@ -1127,6 +1147,62 @@ do_indirects:
        ext2_discard_reservation(inode);
 
        mutex_unlock(&ei->truncate_mutex);
+}
+
+static void ext2_truncate_blocks(struct inode *inode, loff_t offset)
+{
+       /*
+        * XXX: it seems like a bug here that we don't allow
+        * IS_APPEND inode to have blocks-past-i_size trimmed off.
+        * review and fix this.
+        *
+        * Also would be nice to be able to handle IO errors and such,
+        * but that's probably too much to ask.
+        */
+       if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) ||
+           S_ISLNK(inode->i_mode)))
+               return;
+       if (ext2_inode_is_fast_symlink(inode))
+               return;
+       if (IS_APPEND(inode) || IS_IMMUTABLE(inode))
+               return;
+       __ext2_truncate_blocks(inode, offset);
+}
+
+int ext2_setsize(struct inode *inode, loff_t newsize)
+{
+       loff_t oldsize;
+       int error;
+
+       error = inode_newsize_ok(inode, newsize);
+       if (error)
+               return error;
+
+       if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) ||
+           S_ISLNK(inode->i_mode)))
+               return -EINVAL;
+       if (ext2_inode_is_fast_symlink(inode))
+               return -EINVAL;
+       if (IS_APPEND(inode) || IS_IMMUTABLE(inode))
+               return -EPERM;
+
+       if (mapping_is_xip(inode->i_mapping))
+               error = xip_truncate_page(inode->i_mapping, newsize);
+       else if (test_opt(inode->i_sb, NOBH))
+               error = nobh_truncate_page(inode->i_mapping,
+                               newsize, ext2_get_block);
+       else
+               error = block_truncate_page(inode->i_mapping,
+                               newsize, ext2_get_block);
+       if (error)
+               return error;
+
+       oldsize = inode->i_size;
+       i_size_write(inode, newsize);
+       truncate_pagecache(inode, oldsize, newsize);
+
+       __ext2_truncate_blocks(inode, newsize);
+
        inode->i_mtime = inode->i_ctime = CURRENT_TIME_SEC;
        if (inode_needs_sync(inode)) {
                sync_mapping_buffers(inode->i_mapping);
@@ -1134,6 +1210,8 @@ do_indirects:
        } else {
                mark_inode_dirty(inode);
        }
+
+       return 0;
 }
 
 static struct ext2_inode *ext2_get_inode(struct super_block *sb, ino_t ino,
@@ -1474,8 +1552,15 @@ int ext2_setattr(struct dentry *dentry, struct iattr *iattr)
                if (error)
                        return error;
        }
-       error = inode_setattr(inode, iattr);
-       if (!error && (iattr->ia_valid & ATTR_MODE))
+       if (iattr->ia_valid & ATTR_SIZE) {
+               error = ext2_setsize(inode, iattr->ia_size);
+               if (error)
+                       return error;
+       }
+       generic_setattr(inode, iattr);
+       if (iattr->ia_valid & ATTR_MODE)
                error = ext2_acl_chmod(inode);
+       mark_inode_dirty(inode);
+
        return error;
 }
index 71e9eb1..7ff43f4 100644 (file)
@@ -119,6 +119,8 @@ static void ext2_put_super (struct super_block * sb)
        int i;
        struct ext2_sb_info *sbi = EXT2_SB(sb);
 
+       dquot_disable(sb, -1, DQUOT_USAGE_ENABLED | DQUOT_LIMITS_ENABLED);
+
        if (sb->s_dirt)
                ext2_write_super(sb);
 
@@ -1063,6 +1065,12 @@ static int ext2_fill_super(struct super_block *sb, void *data, int silent)
        sb->s_op = &ext2_sops;
        sb->s_export_op = &ext2_export_ops;
        sb->s_xattr = ext2_xattr_handlers;
+
+#ifdef CONFIG_QUOTA
+       sb->dq_op = &dquot_operations;
+       sb->s_qcop = &dquot_quotactl_ops;
+#endif
+
        root = ext2_iget(sb, EXT2_ROOT_INO);
        if (IS_ERR(root)) {
                ret = PTR_ERR(root);
@@ -1241,6 +1249,7 @@ static int ext2_remount (struct super_block * sb, int * flags, char * data)
                        spin_unlock(&sbi->s_lock);
                        return 0;
                }
+
                /*
                 * OK, we are remounting a valid rw partition rdonly, so set
                 * the rdonly flag and then mark the partition as valid again.
@@ -1248,6 +1257,13 @@ static int ext2_remount (struct super_block * sb, int * flags, char * data)
                es->s_state = cpu_to_le16(sbi->s_mount_state);
                es->s_mtime = cpu_to_le32(get_seconds());
                spin_unlock(&sbi->s_lock);
+
+               err = dquot_suspend(sb, -1);
+               if (err < 0) {
+                       spin_lock(&sbi->s_lock);
+                       goto restore_opts;
+               }
+
                ext2_sync_super(sb, es, 1);
        } else {
                __le32 ret = EXT2_HAS_RO_COMPAT_FEATURE(sb,
@@ -1269,8 +1285,12 @@ static int ext2_remount (struct super_block * sb, int * flags, char * data)
                if (!ext2_setup_super (sb, es, 0))
                        sb->s_flags &= ~MS_RDONLY;
                spin_unlock(&sbi->s_lock);
+
                ext2_write_super(sb);
+
+               dquot_resume(sb, -1);
        }
+
        return 0;
 restore_opts:
        sbi->s_mount_opt = old_opts.s_mount_opt;
index 373fa90..e2e72c3 100644 (file)
@@ -297,7 +297,7 @@ static void free_rb_tree_fname(struct rb_root *root)
                        kfree (old);
                }
                if (!parent)
-                       root->rb_node = NULL;
+                       *root = RB_ROOT;
                else if (parent->rb_left == n)
                        parent->rb_left = NULL;
                else if (parent->rb_right == n)
index fcf7487..d7e9f74 100644 (file)
@@ -43,9 +43,9 @@
  * inode to disk.
  */
 
-int ext3_sync_file(struct file * file, struct dentry *dentry, int datasync)
+int ext3_sync_file(struct file *file, int datasync)
 {
-       struct inode *inode = dentry->d_inode;
+       struct inode *inode = file->f_mapping->host;
        struct ext3_inode_info *ei = EXT3_I(inode);
        journal_t *journal = EXT3_SB(inode->i_sb)->s_journal;
        int ret, needs_barrier = 0;
index 0fc1293..6c953bb 100644 (file)
@@ -410,6 +410,8 @@ static void ext3_put_super (struct super_block * sb)
        struct ext3_super_block *es = sbi->s_es;
        int i, err;
 
+       dquot_disable(sb, -1, DQUOT_USAGE_ENABLED | DQUOT_LIMITS_ENABLED);
+
        lock_kernel();
 
        ext3_xattr_put_super(sb);
@@ -748,7 +750,7 @@ static int ext3_release_dquot(struct dquot *dquot);
 static int ext3_mark_dquot_dirty(struct dquot *dquot);
 static int ext3_write_info(struct super_block *sb, int type);
 static int ext3_quota_on(struct super_block *sb, int type, int format_id,
-                               char *path, int remount);
+                               char *path);
 static int ext3_quota_on_mount(struct super_block *sb, int type);
 static ssize_t ext3_quota_read(struct super_block *sb, int type, char *data,
                               size_t len, loff_t off);
@@ -767,12 +769,12 @@ static const struct dquot_operations ext3_quota_operations = {
 
 static const struct quotactl_ops ext3_qctl_operations = {
        .quota_on       = ext3_quota_on,
-       .quota_off      = vfs_quota_off,
-       .quota_sync     = vfs_quota_sync,
-       .get_info       = vfs_get_dqinfo,
-       .set_info       = vfs_set_dqinfo,
-       .get_dqblk      = vfs_get_dqblk,
-       .set_dqblk      = vfs_set_dqblk
+       .quota_off      = dquot_quota_off,
+       .quota_sync     = dquot_quota_sync,
+       .get_info       = dquot_get_dqinfo,
+       .set_info       = dquot_set_dqinfo,
+       .get_dqblk      = dquot_get_dqblk,
+       .set_dqblk      = dquot_set_dqblk
 };
 #endif
 
@@ -1527,7 +1529,7 @@ static void ext3_orphan_cleanup (struct super_block * sb,
        /* Turn quotas off */
        for (i = 0; i < MAXQUOTAS; i++) {
                if (sb_dqopt(sb)->files[i])
-                       vfs_quota_off(sb, i, 0);
+                       dquot_quota_off(sb, i);
        }
 #endif
        sb->s_flags = s_flags; /* Restore MS_RDONLY status */
@@ -2551,6 +2553,7 @@ static int ext3_remount (struct super_block * sb, int * flags, char * data)
        ext3_fsblk_t n_blocks_count = 0;
        unsigned long old_sb_flags;
        struct ext3_mount_options old_opts;
+       int enable_quota = 0;
        int err;
 #ifdef CONFIG_QUOTA
        int i;
@@ -2597,6 +2600,10 @@ static int ext3_remount (struct super_block * sb, int * flags, char * data)
                }
 
                if (*flags & MS_RDONLY) {
+                       err = dquot_suspend(sb, -1);
+                       if (err < 0)
+                               goto restore_opts;
+
                        /*
                         * First of all, the unconditional stuff we have to do
                         * to disable replay of the journal when we next remount
@@ -2651,6 +2658,7 @@ static int ext3_remount (struct super_block * sb, int * flags, char * data)
                                goto restore_opts;
                        if (!ext3_setup_super (sb, es, 0))
                                sb->s_flags &= ~MS_RDONLY;
+                       enable_quota = 1;
                }
        }
 #ifdef CONFIG_QUOTA
@@ -2662,6 +2670,9 @@ static int ext3_remount (struct super_block * sb, int * flags, char * data)
 #endif
        unlock_super(sb);
        unlock_kernel();
+
+       if (enable_quota)
+               dquot_resume(sb, -1);
        return 0;
 restore_opts:
        sb->s_flags = old_sb_flags;
@@ -2851,24 +2862,21 @@ static int ext3_write_info(struct super_block *sb, int type)
  */
 static int ext3_quota_on_mount(struct super_block *sb, int type)
 {
-       return vfs_quota_on_mount(sb, EXT3_SB(sb)->s_qf_names[type],
-                       EXT3_SB(sb)->s_jquota_fmt, type);
+       return dquot_quota_on_mount(sb, EXT3_SB(sb)->s_qf_names[type],
+                                       EXT3_SB(sb)->s_jquota_fmt, type);
 }
 
 /*
  * Standard function to be called on quota_on
  */
 static int ext3_quota_on(struct super_block *sb, int type, int format_id,
-                        char *name, int remount)
+                        char *name)
 {
        int err;
        struct path path;
 
        if (!test_opt(sb, QUOTA))
                return -EINVAL;
-       /* When remounting, no checks are needed and in fact, name is NULL */
-       if (remount)
-               return vfs_quota_on(sb, type, format_id, name, remount);
 
        err = kern_path(name, LOOKUP_FOLLOW, &path);
        if (err)
@@ -2906,7 +2914,7 @@ static int ext3_quota_on(struct super_block *sb, int type, int format_id,
                }
        }
 
-       err = vfs_quota_on_path(sb, type, format_id, &path);
+       err = dquot_quota_on_path(sb, type, format_id, &path);
        path_put(&path);
        return err;
 }
index 60bd310..19a4de5 100644 (file)
@@ -1519,7 +1519,7 @@ extern int ext4_htree_store_dirent(struct file *dir_file, __u32 hash,
 extern void ext4_htree_free_dir_info(struct dir_private_info *p);
 
 /* fsync.c */
-extern int ext4_sync_file(struct file *, struct dentry *, int);
+extern int ext4_sync_file(struct file *, int);
 
 /* hash.c */
 extern int ext4fs_dirhash(const char *name, int len, struct
index b6a74f9..592adf2 100644 (file)
@@ -71,9 +71,9 @@ static void ext4_sync_parent(struct inode *inode)
  * i_mutex lock is held when entering and exiting this function
  */
 
-int ext4_sync_file(struct file *file, struct dentry *dentry, int datasync)
+int ext4_sync_file(struct file *file, int datasync)
 {
-       struct inode *inode = dentry->d_inode;
+       struct inode *inode = file->f_mapping->host;
        struct ext4_inode_info *ei = EXT4_I(inode);
        journal_t *journal = EXT4_SB(inode->i_sb)->s_journal;
        int ret;
@@ -81,7 +81,7 @@ int ext4_sync_file(struct file *file, struct dentry *dentry, int datasync)
 
        J_ASSERT(ext4_journal_current_handle() == NULL);
 
-       trace_ext4_sync_file(file, dentry, datasync);
+       trace_ext4_sync_file(file, datasync);
 
        if (inode->i_sb->s_flags & MS_RDONLY)
                return 0;
@@ -91,7 +91,7 @@ int ext4_sync_file(struct file *file, struct dentry *dentry, int datasync)
                return ret;
 
        if (!journal) {
-               ret = simple_fsync(file, dentry, datasync);
+               ret = generic_file_fsync(file, datasync);
                if (!ret && !list_empty(&inode->i_dentry))
                        ext4_sync_parent(inode);
                return ret;
index 49d88c0..4e8983a 100644 (file)
@@ -646,6 +646,8 @@ static void ext4_put_super(struct super_block *sb)
        struct ext4_super_block *es = sbi->s_es;
        int i, err;
 
+       dquot_disable(sb, -1, DQUOT_USAGE_ENABLED | DQUOT_LIMITS_ENABLED);
+
        flush_workqueue(sbi->dio_unwritten_wq);
        destroy_workqueue(sbi->dio_unwritten_wq);
 
@@ -1062,7 +1064,7 @@ static int ext4_release_dquot(struct dquot *dquot);
 static int ext4_mark_dquot_dirty(struct dquot *dquot);
 static int ext4_write_info(struct super_block *sb, int type);
 static int ext4_quota_on(struct super_block *sb, int type, int format_id,
-                               char *path, int remount);
+                               char *path);
 static int ext4_quota_on_mount(struct super_block *sb, int type);
 static ssize_t ext4_quota_read(struct super_block *sb, int type, char *data,
                               size_t len, loff_t off);
@@ -1084,12 +1086,12 @@ static const struct dquot_operations ext4_quota_operations = {
 
 static const struct quotactl_ops ext4_qctl_operations = {
        .quota_on       = ext4_quota_on,
-       .quota_off      = vfs_quota_off,
-       .quota_sync     = vfs_quota_sync,
-       .get_info       = vfs_get_dqinfo,
-       .set_info       = vfs_set_dqinfo,
-       .get_dqblk      = vfs_get_dqblk,
-       .set_dqblk      = vfs_set_dqblk
+       .quota_off      = dquot_quota_off,
+       .quota_sync     = dquot_quota_sync,
+       .get_info       = dquot_get_dqinfo,
+       .set_info       = dquot_set_dqinfo,
+       .get_dqblk      = dquot_get_dqblk,
+       .set_dqblk      = dquot_set_dqblk
 };
 #endif
 
@@ -2054,7 +2056,7 @@ static void ext4_orphan_cleanup(struct super_block *sb,
        /* Turn quotas off */
        for (i = 0; i < MAXQUOTAS; i++) {
                if (sb_dqopt(sb)->files[i])
-                       vfs_quota_off(sb, i, 0);
+                       dquot_quota_off(sb, i);
        }
 #endif
        sb->s_flags = s_flags; /* Restore MS_RDONLY status */
@@ -3576,6 +3578,7 @@ static int ext4_remount(struct super_block *sb, int *flags, char *data)
        ext4_fsblk_t n_blocks_count = 0;
        unsigned long old_sb_flags;
        struct ext4_mount_options old_opts;
+       int enable_quota = 0;
        ext4_group_t g;
        unsigned int journal_ioprio = DEFAULT_JOURNAL_IOPRIO;
        int err;
@@ -3633,6 +3636,10 @@ static int ext4_remount(struct super_block *sb, int *flags, char *data)
                }
 
                if (*flags & MS_RDONLY) {
+                       err = dquot_suspend(sb, -1);
+                       if (err < 0)
+                               goto restore_opts;
+
                        /*
                         * First of all, the unconditional stuff we have to do
                         * to disable replay of the journal when we next remount
@@ -3701,6 +3708,7 @@ static int ext4_remount(struct super_block *sb, int *flags, char *data)
                                goto restore_opts;
                        if (!ext4_setup_super(sb, es, 0))
                                sb->s_flags &= ~MS_RDONLY;
+                       enable_quota = 1;
                }
        }
        ext4_setup_system_zone(sb);
@@ -3716,6 +3724,8 @@ static int ext4_remount(struct super_block *sb, int *flags, char *data)
 #endif
        unlock_super(sb);
        unlock_kernel();
+       if (enable_quota)
+               dquot_resume(sb, -1);
 
        ext4_msg(sb, KERN_INFO, "re-mounted. Opts: %s", orig_data);
        kfree(orig_data);
@@ -3913,24 +3923,21 @@ static int ext4_write_info(struct super_block *sb, int type)
  */
 static int ext4_quota_on_mount(struct super_block *sb, int type)
 {
-       return vfs_quota_on_mount(sb, EXT4_SB(sb)->s_qf_names[type],
-                                 EXT4_SB(sb)->s_jquota_fmt, type);
+       return dquot_quota_on_mount(sb, EXT4_SB(sb)->s_qf_names[type],
+                                       EXT4_SB(sb)->s_jquota_fmt, type);
 }
 
 /*
  * Standard function to be called on quota_on
  */
 static int ext4_quota_on(struct super_block *sb, int type, int format_id,
-                        char *name, int remount)
+                        char *name)
 {
        int err;
        struct path path;
 
        if (!test_opt(sb, QUOTA))
                return -EINVAL;
-       /* When remounting, no checks are needed and in fact, name is NULL */
-       if (remount)
-               return vfs_quota_on(sb, type, format_id, name, remount);
 
        err = kern_path(name, LOOKUP_FOLLOW, &path);
        if (err)
@@ -3969,7 +3976,7 @@ static int ext4_quota_on(struct super_block *sb, int type, int format_id,
                }
        }
 
-       err = vfs_quota_on_path(sb, type, format_id, &path);
+       err = dquot_quota_on_path(sb, type, format_id, &path);
        path_put(&path);
        return err;
 }
index 53dba57..27ac257 100644 (file)
@@ -306,11 +306,11 @@ extern long fat_generic_ioctl(struct file *filp, unsigned int cmd,
 extern const struct file_operations fat_file_operations;
 extern const struct inode_operations fat_file_inode_operations;
 extern int fat_setattr(struct dentry * dentry, struct iattr * attr);
-extern void fat_truncate(struct inode *inode);
+extern int fat_setsize(struct inode *inode, loff_t offset);
+extern void fat_truncate_blocks(struct inode *inode, loff_t offset);
 extern int fat_getattr(struct vfsmount *mnt, struct dentry *dentry,
                       struct kstat *stat);
-extern int fat_file_fsync(struct file *file, struct dentry *dentry,
-                         int datasync);
+extern int fat_file_fsync(struct file *file, int datasync);
 
 /* fat/inode.c */
 extern void fat_attach(struct inode *inode, loff_t i_pos);
index a14c2f6..990dfae 100644 (file)
@@ -149,12 +149,12 @@ static int fat_file_release(struct inode *inode, struct file *filp)
        return 0;
 }
 
-int fat_file_fsync(struct file *filp, struct dentry *dentry, int datasync)
+int fat_file_fsync(struct file *filp, int datasync)
 {
-       struct inode *inode = dentry->d_inode;
+       struct inode *inode = filp->f_mapping->host;
        int res, err;
 
-       res = simple_fsync(filp, dentry, datasync);
+       res = generic_file_fsync(filp, datasync);
        err = sync_mapping_buffers(MSDOS_SB(inode->i_sb)->fat_inode->i_mapping);
 
        return res ? res : err;
@@ -283,7 +283,7 @@ static int fat_free(struct inode *inode, int skip)
        return fat_free_clusters(inode, free_start);
 }
 
-void fat_truncate(struct inode *inode)
+void fat_truncate_blocks(struct inode *inode, loff_t offset)
 {
        struct msdos_sb_info *sbi = MSDOS_SB(inode->i_sb);
        const unsigned int cluster_size = sbi->cluster_size;
@@ -293,10 +293,10 @@ void fat_truncate(struct inode *inode)
         * This protects against truncating a file bigger than it was then
         * trying to write into the hole.
         */
-       if (MSDOS_I(inode)->mmu_private > inode->i_size)
-               MSDOS_I(inode)->mmu_private = inode->i_size;
+       if (MSDOS_I(inode)->mmu_private > offset)
+               MSDOS_I(inode)->mmu_private = offset;
 
-       nr_clusters = (inode->i_size + (cluster_size - 1)) >> sbi->cluster_bits;
+       nr_clusters = (offset + (cluster_size - 1)) >> sbi->cluster_bits;
 
        fat_free(inode, nr_clusters);
        fat_flush_inodes(inode->i_sb, inode, NULL);
@@ -364,6 +364,18 @@ static int fat_allow_set_time(struct msdos_sb_info *sbi, struct inode *inode)
        return 0;
 }
 
+int fat_setsize(struct inode *inode, loff_t offset)
+{
+       int error;
+
+       error = simple_setsize(inode, offset);
+       if (error)
+               return error;
+       fat_truncate_blocks(inode, offset);
+
+       return error;
+}
+
 #define TIMES_SET_FLAGS        (ATTR_MTIME_SET | ATTR_ATIME_SET | ATTR_TIMES_SET)
 /* valid file mode bits */
 #define FAT_VALID_MODE (S_IFREG | S_IFDIR | S_IRWXUGO)
@@ -378,7 +390,8 @@ int fat_setattr(struct dentry *dentry, struct iattr *attr)
        /*
         * Expand the file. Since inode_setattr() updates ->i_size
         * before calling the ->truncate(), but FAT needs to fill the
-        * hole before it.
+        * hole before it. XXX: this is no longer true with new truncate
+        * sequence.
         */
        if (attr->ia_valid & ATTR_SIZE) {
                if (attr->ia_size > inode->i_size) {
@@ -427,15 +440,20 @@ int fat_setattr(struct dentry *dentry, struct iattr *attr)
                        attr->ia_valid &= ~ATTR_MODE;
        }
 
-       if (attr->ia_valid)
-               error = inode_setattr(inode, attr);
+       if (attr->ia_valid & ATTR_SIZE) {
+               error = fat_setsize(inode, attr->ia_size);
+               if (error)
+                       goto out;
+       }
+
+       generic_setattr(inode, attr);
+       mark_inode_dirty(inode);
 out:
        return error;
 }
 EXPORT_SYMBOL_GPL(fat_setattr);
 
 const struct inode_operations fat_file_inode_operations = {
-       .truncate       = fat_truncate,
        .setattr        = fat_setattr,
        .getattr        = fat_getattr,
 };
index ed33904..7bf45ae 100644 (file)
@@ -142,14 +142,29 @@ static int fat_readpages(struct file *file, struct address_space *mapping,
        return mpage_readpages(mapping, pages, nr_pages, fat_get_block);
 }
 
+static void fat_write_failed(struct address_space *mapping, loff_t to)
+{
+       struct inode *inode = mapping->host;
+
+       if (to > inode->i_size) {
+               truncate_pagecache(inode, to, inode->i_size);
+               fat_truncate_blocks(inode, inode->i_size);
+       }
+}
+
 static int fat_write_begin(struct file *file, struct address_space *mapping,
                        loff_t pos, unsigned len, unsigned flags,
                        struct page **pagep, void **fsdata)
 {
+       int err;
+
        *pagep = NULL;
-       return cont_write_begin(file, mapping, pos, len, flags, pagep, fsdata,
-                               fat_get_block,
+       err = cont_write_begin_newtrunc(file, mapping, pos, len, flags,
+                               pagep, fsdata, fat_get_block,
                                &MSDOS_I(mapping->host)->mmu_private);
+       if (err < 0)
+               fat_write_failed(mapping, pos + len);
+       return err;
 }
 
 static int fat_write_end(struct file *file, struct address_space *mapping,
@@ -159,6 +174,8 @@ static int fat_write_end(struct file *file, struct address_space *mapping,
        struct inode *inode = mapping->host;
        int err;
        err = generic_write_end(file, mapping, pos, len, copied, pagep, fsdata);
+       if (err < len)
+               fat_write_failed(mapping, pos + len);
        if (!(err < 0) && !(MSDOS_I(inode)->i_attrs & ATTR_ARCH)) {
                inode->i_mtime = inode->i_ctime = CURRENT_TIME_SEC;
                MSDOS_I(inode)->i_attrs |= ATTR_ARCH;
@@ -172,7 +189,9 @@ static ssize_t fat_direct_IO(int rw, struct kiocb *iocb,
                             loff_t offset, unsigned long nr_segs)
 {
        struct file *file = iocb->ki_filp;
-       struct inode *inode = file->f_mapping->host;
+       struct address_space *mapping = file->f_mapping;
+       struct inode *inode = mapping->host;
+       ssize_t ret;
 
        if (rw == WRITE) {
                /*
@@ -193,8 +212,12 @@ static ssize_t fat_direct_IO(int rw, struct kiocb *iocb,
         * FAT need to use the DIO_LOCKING for avoiding the race
         * condition of fat_get_block() and ->truncate().
         */
-       return blockdev_direct_IO(rw, iocb, inode, inode->i_sb->s_bdev, iov,
-                                 offset, nr_segs, fat_get_block, NULL);
+       ret = blockdev_direct_IO_newtrunc(rw, iocb, inode, inode->i_sb->s_bdev,
+                               iov, offset, nr_segs, fat_get_block, NULL);
+       if (ret < 0 && (rw & WRITE))
+               fat_write_failed(mapping, offset + iov_length(iov, nr_segs));
+
+       return ret;
 }
 
 static sector_t _fat_bmap(struct address_space *mapping, sector_t block)
@@ -429,7 +452,7 @@ static void fat_delete_inode(struct inode *inode)
 {
        truncate_inode_pages(&inode->i_data, 0);
        inode->i_size = 0;
-       fat_truncate(inode);
+       fat_truncate_blocks(inode, 0);
        clear_inode(inode);
 }
 
index 32d12b7..5c7d10e 100644 (file)
@@ -194,14 +194,6 @@ struct file *alloc_file(struct path *path, fmode_t mode,
 }
 EXPORT_SYMBOL(alloc_file);
 
-void fput(struct file *file)
-{
-       if (atomic_long_dec_and_test(&file->f_count))
-               __fput(file);
-}
-
-EXPORT_SYMBOL(fput);
-
 /**
  * drop_file_write_access - give up ability to write to a file
  * @file: the file to which we will stop writing
@@ -227,10 +219,9 @@ void drop_file_write_access(struct file *file)
 }
 EXPORT_SYMBOL_GPL(drop_file_write_access);
 
-/* __fput is called from task context when aio completion releases the last
- * last use of a struct file *.  Do not use otherwise.
+/* the real guts of fput() - releasing the last reference to file
  */
-void __fput(struct file *file)
+static void __fput(struct file *file)
 {
        struct dentry *dentry = file->f_path.dentry;
        struct vfsmount *mnt = file->f_path.mnt;
@@ -268,6 +259,14 @@ void __fput(struct file *file)
        mntput(mnt);
 }
 
+void fput(struct file *file)
+{
+       if (atomic_long_dec_and_test(&file->f_count))
+               __fput(file);
+}
+
+EXPORT_SYMBOL(fput);
+
 struct file *fget(unsigned int fd)
 {
        struct file *file;
index 4787ae6..3cdc5f7 100644 (file)
@@ -1156,10 +1156,9 @@ static int fuse_dir_release(struct inode *inode, struct file *file)
        return 0;
 }
 
-static int fuse_dir_fsync(struct file *file, struct dentry *de, int datasync)
+static int fuse_dir_fsync(struct file *file, int datasync)
 {
-       /* nfsd can call this with no file */
-       return file ? fuse_fsync_common(file, de, datasync, 1) : 0;
+       return fuse_fsync_common(file, datasync, 1);
 }
 
 static bool update_mtime(unsigned ivalid)
index a9f5e13..b5fd6f9 100644 (file)
@@ -351,10 +351,9 @@ static void fuse_sync_writes(struct inode *inode)
        fuse_release_nowrite(inode);
 }
 
-int fuse_fsync_common(struct file *file, struct dentry *de, int datasync,
-                     int isdir)
+int fuse_fsync_common(struct file *file, int datasync, int isdir)
 {
-       struct inode *inode = de->d_inode;
+       struct inode *inode = file->f_mapping->host;
        struct fuse_conn *fc = get_fuse_conn(inode);
        struct fuse_file *ff = file->private_data;
        struct fuse_req *req;
@@ -403,9 +402,9 @@ int fuse_fsync_common(struct file *file, struct dentry *de, int datasync,
        return err;
 }
 
-static int fuse_fsync(struct file *file, struct dentry *de, int datasync)
+static int fuse_fsync(struct file *file, int datasync)
 {
-       return fuse_fsync_common(file, de, datasync, 0);
+       return fuse_fsync_common(file, datasync, 0);
 }
 
 void fuse_read_fill(struct fuse_req *req, struct file *file, loff_t pos,
index 01cc462..2c0d14a 100644 (file)
@@ -568,8 +568,7 @@ void fuse_release_common(struct file *file, int opcode);
 /**
  * Send FSYNC or FSYNCDIR request
  */
-int fuse_fsync_common(struct file *file, struct dentry *de, int datasync,
-                     int isdir);
+int fuse_fsync_common(struct file *file, int datasync, int isdir);
 
 /**
  * Notify poll wakeup
index a739a0a..9f8b525 100644 (file)
@@ -700,8 +700,14 @@ out:
                return 0;
 
        page_cache_release(page);
+
+       /*
+        * XXX(hch): the call below should probably be replaced with
+        * a call to the gfs2-specific truncate blocks helper to actually
+        * release disk blocks..
+        */
        if (pos + len > ip->i_inode.i_size)
-               vmtruncate(&ip->i_inode, ip->i_inode.i_size);
+               simple_setsize(&ip->i_inode, ip->i_inode.i_size);
 out_endtrans:
        gfs2_trans_end(sdp);
 out_trans_fail:
index b20bfcc..ed9a94f 100644 (file)
@@ -554,9 +554,9 @@ static int gfs2_close(struct inode *inode, struct file *file)
  * Returns: errno
  */
 
-static int gfs2_fsync(struct file *file, struct dentry *dentry, int datasync)
+static int gfs2_fsync(struct file *file, int datasync)
 {
-       struct inode *inode = dentry->d_inode;
+       struct inode *inode = file->f_mapping->host;
        int sync_state = inode->i_state & (I_DIRTY_SYNC|I_DIRTY_DATASYNC);
        int ret = 0;
 
index 4e64352..98cdd05 100644 (file)
@@ -1071,6 +1071,9 @@ int gfs2_permission(struct inode *inode, int mask)
        return error;
 }
 
+/*
+ * XXX: should be changed to have proper ordering by opencoding simple_setsize
+ */
 static int setattr_size(struct inode *inode, struct iattr *attr)
 {
        struct gfs2_inode *ip = GFS2_I(inode);
@@ -1081,7 +1084,7 @@ static int setattr_size(struct inode *inode, struct iattr *attr)
                error = gfs2_trans_begin(sdp, 0, sdp->sd_jdesc->jd_blocks);
                if (error)
                        return error;
-               error = vmtruncate(inode, attr->ia_size);
+               error = simple_setsize(inode, attr->ia_size);
                gfs2_trans_end(sdp);
                if (error) 
                        return error;
index 3a029d8..87ac189 100644 (file)
@@ -411,9 +411,9 @@ int hostfs_file_open(struct inode *ino, struct file *file)
        return 0;
 }
 
-int hostfs_fsync(struct file *file, struct dentry *dentry, int datasync)
+int hostfs_fsync(struct file *file, int datasync)
 {
-       return fsync_file(HOSTFS_I(dentry->d_inode)->fd, datasync);
+       return fsync_file(HOSTFS_I(file->f_mapping->host)->fd, datasync);
 }
 
 static const struct file_operations hostfs_file_fops = {
index 3efabff..a9ae9bf 100644 (file)
@@ -19,9 +19,9 @@ static int hpfs_file_release(struct inode *inode, struct file *file)
        return 0;
 }
 
-int hpfs_file_fsync(struct file *file, struct dentry *dentry, int datasync)
+int hpfs_file_fsync(struct file *file, int datasync)
 {
-       /*return file_fsync(file, dentry);*/
+       /*return file_fsync(file, datasync);*/
        return 0; /* Don't fsync :-) */
 }
 
index 97bf738..75f9d43 100644 (file)
@@ -268,7 +268,7 @@ void hpfs_set_ea(struct inode *, struct fnode *, const char *,
 
 /* file.c */
 
-int hpfs_file_fsync(struct file *, struct dentry *, int);
+int hpfs_file_fsync(struct file *, int);
 extern const struct file_operations hpfs_file_ops;
 extern const struct inode_operations hpfs_file_iops;
 extern const struct address_space_operations hpfs_aops;
index 2e4dfa8..826c3f9 100644 (file)
@@ -587,7 +587,7 @@ static int hppfs_readdir(struct file *file, void *ent, filldir_t filldir)
        return err;
 }
 
-static int hppfs_fsync(struct file *file, struct dentry *dentry, int datasync)
+static int hppfs_fsync(struct file *file, int datasync)
 {
        return 0;
 }
index a0bbd3d..a4e9a7e 100644 (file)
@@ -688,7 +688,7 @@ static void init_once(void *foo)
 const struct file_operations hugetlbfs_file_operations = {
        .read                   = hugetlbfs_read,
        .mmap                   = hugetlbfs_file_mmap,
-       .fsync                  = simple_sync_file,
+       .fsync                  = noop_fsync,
        .get_unmapped_area      = hugetlb_get_unmapped_area,
 };
 
index e7291c1..8134970 100644 (file)
@@ -26,9 +26,9 @@ static int jffs2_write_begin(struct file *filp, struct address_space *mapping,
                        struct page **pagep, void **fsdata);
 static int jffs2_readpage (struct file *filp, struct page *pg);
 
-int jffs2_fsync(struct file *filp, struct dentry *dentry, int datasync)
+int jffs2_fsync(struct file *filp, int datasync)
 {
-       struct inode *inode = dentry->d_inode;
+       struct inode *inode = filp->f_mapping->host;
        struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb);
 
        /* Trigger GC to flush any pending writes for this inode */
index 86e0821..8bc2c80 100644 (file)
@@ -169,13 +169,13 @@ int jffs2_do_setattr (struct inode *inode, struct iattr *iattr)
        mutex_unlock(&f->sem);
        jffs2_complete_reservation(c);
 
-       /* We have to do the vmtruncate() without f->sem held, since
+       /* We have to do the simple_setsize() without f->sem held, since
           some pages may be locked and waiting for it in readpage().
           We are protected from a simultaneous write() extending i_size
           back past iattr->ia_size, because do_truncate() holds the
           generic inode semaphore. */
        if (ivalid & ATTR_SIZE && inode->i_size > iattr->ia_size) {
-               vmtruncate(inode, iattr->ia_size);      
+               simple_setsize(inode, iattr->ia_size);
                inode->i_blocks = (inode->i_size + 511) >> 9;
        }       
 
index 035a767..4791aac 100644 (file)
@@ -158,7 +158,7 @@ extern const struct inode_operations jffs2_dir_inode_operations;
 extern const struct file_operations jffs2_file_operations;
 extern const struct inode_operations jffs2_file_inode_operations;
 extern const struct address_space_operations jffs2_file_address_operations;
-int jffs2_fsync(struct file *, struct dentry *, int);
+int jffs2_fsync(struct file *, int);
 int jffs2_do_readpage_unlock (struct inode *inode, struct page *pg);
 
 /* ioctl.c */
index 85d9ec6..127263c 100644 (file)
@@ -27,9 +27,9 @@
 #include "jfs_acl.h"
 #include "jfs_debug.h"
 
-int jfs_fsync(struct file *file, struct dentry *dentry, int datasync)
+int jfs_fsync(struct file *file, int datasync)
 {
-       struct inode *inode = dentry->d_inode;
+       struct inode *inode = file->f_mapping->host;
        int rc = 0;
 
        if (!(inode->i_state & I_DIRTY) ||
index 9e6bda3..11042b1 100644 (file)
@@ -21,7 +21,7 @@
 struct fid;
 
 extern struct inode *ialloc(struct inode *, umode_t);
-extern int jfs_fsync(struct file *, struct dentry *, int);
+extern int jfs_fsync(struct file *, int);
 extern long jfs_ioctl(struct file *, unsigned int, unsigned long);
 extern long jfs_compat_ioctl(struct file *, unsigned int, unsigned long);
 extern struct inode *jfs_iget(struct super_block *, unsigned long);
index b66832a..b38f96b 100644 (file)
@@ -179,6 +179,8 @@ static void jfs_put_super(struct super_block *sb)
 
        jfs_info("In jfs_put_super");
 
+       dquot_disable(sb, -1, DQUOT_USAGE_ENABLED | DQUOT_LIMITS_ENABLED);
+
        lock_kernel();
 
        rc = jfs_umount(sb);
@@ -396,10 +398,20 @@ static int jfs_remount(struct super_block *sb, int *flags, char *data)
 
                JFS_SBI(sb)->flag = flag;
                ret = jfs_mount_rw(sb, 1);
+
+               /* mark the fs r/w for quota activity */
+               sb->s_flags &= ~MS_RDONLY;
+
                unlock_kernel();
+               dquot_resume(sb, -1);
                return ret;
        }
        if ((!(sb->s_flags & MS_RDONLY)) && (*flags & MS_RDONLY)) {
+               rc = dquot_suspend(sb, -1);
+               if (rc < 0) {
+                       unlock_kernel();
+                       return rc;
+               }
                rc = jfs_umount_rw(sb);
                JFS_SBI(sb)->flag = flag;
                unlock_kernel();
@@ -469,6 +481,10 @@ static int jfs_fill_super(struct super_block *sb, void *data, int silent)
         */
        sb->s_op = &jfs_super_operations;
        sb->s_export_op = &jfs_export_operations;
+#ifdef CONFIG_QUOTA
+       sb->dq_op = &dquot_operations;
+       sb->s_qcop = &dquot_quotactl_ops;
+#endif
 
        /*
         * Initialize direct-mapping inode/address-space
index 232bea4..09e1016 100644 (file)
@@ -8,6 +8,7 @@
 #include <linux/slab.h>
 #include <linux/mount.h>
 #include <linux/vfs.h>
+#include <linux/quotaops.h>
 #include <linux/mutex.h>
 #include <linux/exportfs.h>
 #include <linux/writeback.h>
@@ -58,11 +59,6 @@ struct dentry *simple_lookup(struct inode *dir, struct dentry *dentry, struct na
        return NULL;
 }
 
-int simple_sync_file(struct file * file, struct dentry *dentry, int datasync)
-{
-       return 0;
-}
 int dcache_dir_open(struct inode *inode, struct file *file)
 {
        static struct qstr cursor_name = {.len = 1, .name = "."};
@@ -190,7 +186,7 @@ const struct file_operations simple_dir_operations = {
        .llseek         = dcache_dir_lseek,
        .read           = generic_read_dir,
        .readdir        = dcache_readdir,
-       .fsync          = simple_sync_file,
+       .fsync          = noop_fsync,
 };
 
 const struct inode_operations simple_dir_inode_operations = {
@@ -330,6 +326,81 @@ int simple_rename(struct inode *old_dir, struct dentry *old_dentry,
        return 0;
 }
 
+/**
+ * simple_setsize - handle core mm and vfs requirements for file size change
+ * @inode: inode
+ * @newsize: new file size
+ *
+ * Returns 0 on success, -error on failure.
+ *
+ * simple_setsize must be called with inode_mutex held.
+ *
+ * simple_setsize will check that the requested new size is OK (see
+ * inode_newsize_ok), and then will perform the necessary i_size update
+ * and pagecache truncation (if necessary). It will be typically be called
+ * from the filesystem's setattr function when ATTR_SIZE is passed in.
+ *
+ * The inode itself must have correct permissions and attributes to allow
+ * i_size to be changed, this function then just checks that the new size
+ * requested is valid.
+ *
+ * In the case of simple in-memory filesystems with inodes stored solely
+ * in the inode cache, and file data in the pagecache, nothing more needs
+ * to be done to satisfy a truncate request. Filesystems with on-disk
+ * blocks for example will need to free them in the case of truncate, in
+ * that case it may be easier not to use simple_setsize (but each of its
+ * components will likely be required at some point to update pagecache
+ * and inode etc).
+ */
+int simple_setsize(struct inode *inode, loff_t newsize)
+{
+       loff_t oldsize;
+       int error;
+
+       error = inode_newsize_ok(inode, newsize);
+       if (error)
+               return error;
+
+       oldsize = inode->i_size;
+       i_size_write(inode, newsize);
+       truncate_pagecache(inode, oldsize, newsize);
+
+       return error;
+}
+EXPORT_SYMBOL(simple_setsize);
+
+/**
+ * simple_setattr - setattr for simple in-memory filesystem
+ * @dentry: dentry
+ * @iattr: iattr structure
+ *
+ * Returns 0 on success, -error on failure.
+ *
+ * simple_setattr implements setattr for an in-memory filesystem which
+ * does not store its own file data or metadata (eg. uses the page cache
+ * and inode cache as its data store).
+ */
+int simple_setattr(struct dentry *dentry, struct iattr *iattr)
+{
+       struct inode *inode = dentry->d_inode;
+       int error;
+
+       error = inode_change_ok(inode, iattr);
+       if (error)
+               return error;
+
+       if (iattr->ia_valid & ATTR_SIZE) {
+               error = simple_setsize(inode, iattr->ia_size);
+               if (error)
+                       return error;
+       }
+
+       generic_setattr(inode, iattr);
+
+       return error;
+}
+EXPORT_SYMBOL(simple_setattr);
+
 int simple_readpage(struct file *file, struct page *page)
 {
        clear_highpage(page);
@@ -851,13 +922,22 @@ struct dentry *generic_fh_to_parent(struct super_block *sb, struct fid *fid,
 }
 EXPORT_SYMBOL_GPL(generic_fh_to_parent);
 
-int simple_fsync(struct file *file, struct dentry *dentry, int datasync)
+/**
+ * generic_file_fsync - generic fsync implementation for simple filesystems
+ * @file:      file to synchronize
+ * @datasync:  only synchronize essential metadata if true
+ *
+ * This is a generic implementation of the fsync method for simple
+ * filesystems which track all non-inode metadata in the buffers list
+ * hanging off the address_space structure.
+ */
+int generic_file_fsync(struct file *file, int datasync)
 {
        struct writeback_control wbc = {
                .sync_mode = WB_SYNC_ALL,
                .nr_to_write = 0, /* metadata-only; caller takes care of data */
        };
-       struct inode *inode = dentry->d_inode;
+       struct inode *inode = file->f_mapping->host;
        int err;
        int ret;
 
@@ -872,7 +952,15 @@ int simple_fsync(struct file *file, struct dentry *dentry, int datasync)
                ret = err;
        return ret;
 }
-EXPORT_SYMBOL(simple_fsync);
+EXPORT_SYMBOL(generic_file_fsync);
+
+/*
+ * No-op implementation of ->fsync for in-memory filesystems.
+ */
+int noop_fsync(struct file *file, int datasync)
+{
+       return 0;
+}
 
 EXPORT_SYMBOL(dcache_dir_close);
 EXPORT_SYMBOL(dcache_dir_lseek);
@@ -895,7 +983,7 @@ EXPORT_SYMBOL(simple_release_fs);
 EXPORT_SYMBOL(simple_rename);
 EXPORT_SYMBOL(simple_rmdir);
 EXPORT_SYMBOL(simple_statfs);
-EXPORT_SYMBOL(simple_sync_file);
+EXPORT_SYMBOL(noop_fsync);
 EXPORT_SYMBOL(simple_unlink);
 EXPORT_SYMBOL(simple_read_from_buffer);
 EXPORT_SYMBOL(simple_write_to_buffer);
index 0de5240..abe1caf 100644 (file)
@@ -219,9 +219,9 @@ int logfs_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
        }
 }
 
-int logfs_fsync(struct file *file, struct dentry *dentry, int datasync)
+int logfs_fsync(struct file *file, int datasync)
 {
-       struct super_block *sb = dentry->d_inode->i_sb;
+       struct super_block *sb = file->f_mapping->host->i_sb;
 
        logfs_write_anchor(sb);
        return 0;
index 1a9db84..c838c4d 100644 (file)
@@ -506,7 +506,7 @@ extern const struct address_space_operations logfs_reg_aops;
 int logfs_readpage(struct file *file, struct page *page);
 int logfs_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
                unsigned long arg);
-int logfs_fsync(struct file *file, struct dentry *dentry, int datasync);
+int logfs_fsync(struct file *file, int datasync);
 
 /* gc.c */
 u32 get_best_cand(struct super_block *sb, struct candidate_list *list, u32 *ec);
index 6198731..9196958 100644 (file)
@@ -22,7 +22,7 @@ const struct file_operations minix_dir_operations = {
        .llseek         = generic_file_llseek,
        .read           = generic_read_dir,
        .readdir        = minix_readdir,
-       .fsync          = simple_fsync,
+       .fsync          = generic_file_fsync,
 };
 
 static inline void dir_put_page(struct page *page)
@@ -72,11 +72,8 @@ static struct page * dir_get_page(struct inode *dir, unsigned long n)
 {
        struct address_space *mapping = dir->i_mapping;
        struct page *page = read_mapping_page(mapping, n, NULL);
-       if (!IS_ERR(page)) {
+       if (!IS_ERR(page))
                kmap(page);
-               if (!PageUptodate(page))
-                       goto fail;
-       }
        return page;
 
 fail:
index 3eec3e6..d5320ff 100644 (file)
@@ -19,7 +19,7 @@ const struct file_operations minix_file_operations = {
        .write          = do_sync_write,
        .aio_write      = generic_file_aio_write,
        .mmap           = generic_file_mmap,
-       .fsync          = simple_fsync,
+       .fsync          = generic_file_fsync,
        .splice_read    = generic_file_splice_read,
 };
 
index f230109..13487ad 100644 (file)
@@ -20,6 +20,9 @@ static inline block_t *i_data(struct inode *inode)
        return (block_t *)minix_i(inode)->u.i2_data;
 }
 
+#define DIRCOUNT 7
+#define INDIRCOUNT(sb) (1 << ((sb)->s_blocksize_bits - 2))
+
 static int block_to_path(struct inode * inode, long block, int offsets[DEPTH])
 {
        int n = 0;
@@ -34,21 +37,21 @@ static int block_to_path(struct inode * inode, long block, int offsets[DEPTH])
                        printk("MINIX-fs: block_to_path: "
                               "block %ld too big on dev %s\n",
                                block, bdevname(sb->s_bdev, b));
-       } else if (block < 7) {
+       } else if (block < DIRCOUNT) {
                offsets[n++] = block;
-       } else if ((block -= 7) < 256) {
-               offsets[n++] = 7;
+       } else if ((block -= DIRCOUNT) < INDIRCOUNT(sb)) {
+               offsets[n++] = DIRCOUNT;
                offsets[n++] = block;
-       } else if ((block -= 256) < 256*256) {
-               offsets[n++] = 8;
-               offsets[n++] = block>>8;
-               offsets[n++] = block & 255;
+       } else if ((block -= INDIRCOUNT(sb)) < INDIRCOUNT(sb) * INDIRCOUNT(sb)) {
+               offsets[n++] = DIRCOUNT + 1;
+               offsets[n++] = block / INDIRCOUNT(sb);
+               offsets[n++] = block % INDIRCOUNT(sb);
        } else {
-               block -= 256*256;
-               offsets[n++] = 9;
-               offsets[n++] = block>>16;
-               offsets[n++] = (block>>8) & 255;
-               offsets[n++] = block & 255;
+               block -= INDIRCOUNT(sb) * INDIRCOUNT(sb);
+               offsets[n++] = DIRCOUNT + 2;
+               offsets[n++] = (block / INDIRCOUNT(sb)) / INDIRCOUNT(sb);
+               offsets[n++] = (block / INDIRCOUNT(sb)) % INDIRCOUNT(sb);
+               offsets[n++] = block % INDIRCOUNT(sb);
        }
        return n;
 }
index 48e1f60..868d0cb 100644 (file)
@@ -1621,6 +1621,7 @@ static struct file *do_last(struct nameidata *nd, struct path *path,
        case LAST_DOTDOT:
                follow_dotdot(nd);
                dir = nd->path.dentry;
+       case LAST_DOT:
                if (nd->path.mnt->mnt_sb->s_type->fs_flags & FS_REVAL_DOT) {
                        if (!dir->d_op->d_revalidate(dir, nd)) {
                                error = -ESTALE;
@@ -1628,7 +1629,6 @@ static struct file *do_last(struct nameidata *nd, struct path *path,
                        }
                }
                /* fallthrough */
-       case LAST_DOT:
        case LAST_ROOT:
                if (open_flag & O_CREAT)
                        goto exit;
index b938708..3639cc5 100644 (file)
@@ -22,7 +22,7 @@
 #include <linux/ncp_fs.h>
 #include "ncplib_kernel.h"
 
-static int ncp_fsync(struct file *file, struct dentry *dentry, int datasync)
+static int ncp_fsync(struct file *file, int datasync)
 {
        return 0;
 }
index db64854..782b431 100644 (file)
@@ -53,7 +53,7 @@ static int nfs_link(struct dentry *, struct inode *, struct dentry *);
 static int nfs_mknod(struct inode *, struct dentry *, int, dev_t);
 static int nfs_rename(struct inode *, struct dentry *,
                      struct inode *, struct dentry *);
-static int nfs_fsync_dir(struct file *, struct dentry *, int);
+static int nfs_fsync_dir(struct file *, int);
 static loff_t nfs_llseek_dir(struct file *, loff_t, int);
 
 const struct file_operations nfs_dir_operations = {
@@ -641,8 +641,10 @@ out:
  * All directory operations under NFS are synchronous, so fsync()
  * is a dummy operation.
  */
-static int nfs_fsync_dir(struct file *filp, struct dentry *dentry, int datasync)
+static int nfs_fsync_dir(struct file *filp, int datasync)
 {
+       struct dentry *dentry = filp->f_path.dentry;
+
        dfprintk(FILE, "NFS: fsync dir(%s/%s) datasync %d\n",
                        dentry->d_parent->d_name.name, dentry->d_name.name,
                        datasync);
index cac96bc..36a5e74 100644 (file)
@@ -53,7 +53,7 @@ static ssize_t nfs_file_splice_write(struct pipe_inode_info *pipe,
 static ssize_t nfs_file_write(struct kiocb *, const struct iovec *iov,
                                unsigned long nr_segs, loff_t pos);
 static int  nfs_file_flush(struct file *, fl_owner_t id);
-static int  nfs_file_fsync(struct file *, struct dentry *dentry, int datasync);
+static int  nfs_file_fsync(struct file *, int datasync);
 static int nfs_check_flags(int flags);
 static int nfs_lock(struct file *filp, int cmd, struct file_lock *fl);
 static int nfs_flock(struct file *filp, int cmd, struct file_lock *fl);
@@ -322,8 +322,9 @@ nfs_file_mmap(struct file * file, struct vm_area_struct * vma)
  * whether any write errors occurred for this process.
  */
 static int
-nfs_file_fsync(struct file *file, struct dentry *dentry, int datasync)
+nfs_file_fsync(struct file *file, int datasync)
 {
+       struct dentry *dentry = file->f_path.dentry;
        struct nfs_open_context *ctx = nfs_file_open_context(file);
        struct inode *inode = dentry->d_inode;
 
index 30292df..c9a30d7 100644 (file)
@@ -27,7 +27,7 @@
 #include "nilfs.h"
 #include "segment.h"
 
-int nilfs_sync_file(struct file *file, struct dentry *dentry, int datasync)
+int nilfs_sync_file(struct file *file, int datasync)
 {
        /*
         * Called from fsync() system call
@@ -37,7 +37,7 @@ int nilfs_sync_file(struct file *file, struct dentry *dentry, int datasync)
         * This function should be implemented when the writeback function
         * will be implemented.
         */
-       struct inode *inode = dentry->d_inode;
+       struct inode *inode = file->f_mapping->host;
        int err;
 
        if (!nilfs_inode_dirty(inode))
index 8723e5b..47d6d79 100644 (file)
@@ -228,7 +228,7 @@ extern void nilfs_set_link(struct inode *, struct nilfs_dir_entry *,
                           struct page *, struct inode *);
 
 /* file.c */
-extern int nilfs_sync_file(struct file *, struct dentry *, int);
+extern int nilfs_sync_file(struct file *, int);
 
 /* ioctl.c */
 long nilfs_ioctl(struct file *, unsigned int, unsigned long);
index fe44d3f..0f48e7c 100644 (file)
@@ -1527,10 +1527,9 @@ static int ntfs_dir_open(struct inode *vi, struct file *filp)
  * this problem for now.  We do write the $BITMAP attribute if it is present
  * which is the important one for a directory so things are not too bad.
  */
-static int ntfs_dir_fsync(struct file *filp, struct dentry *dentry,
-               int datasync)
+static int ntfs_dir_fsync(struct file *filp, int datasync)
 {
-       struct inode *bmp_vi, *vi = dentry->d_inode;
+       struct inode *bmp_vi, *vi = filp->f_mapping->host;
        int err, ret;
        ntfs_attr na;
 
index a1924a0..113ebd9 100644 (file)
@@ -2133,7 +2133,6 @@ static ssize_t ntfs_file_aio_write(struct kiocb *iocb, const struct iovec *iov,
 /**
  * ntfs_file_fsync - sync a file to disk
  * @filp:      file to be synced
- * @dentry:    dentry describing the file to sync
  * @datasync:  if non-zero only flush user data and not metadata
  *
  * Data integrity sync of a file to disk.  Used for fsync, fdatasync, and msync
@@ -2149,19 +2148,15 @@ static ssize_t ntfs_file_aio_write(struct kiocb *iocb, const struct iovec *iov,
  * Also, if @datasync is true, we do not wait on the inode to be written out
  * but we always wait on the page cache pages to be written out.
  *
- * Note: In the past @filp could be NULL so we ignore it as we don't need it
- * anyway.
- *
  * Locking: Caller must hold i_mutex on the inode.
  *
  * TODO: We should probably also write all attribute/index inodes associated
  * with this inode but since we have no simple way of getting to them we ignore
  * this problem for now.
  */
-static int ntfs_file_fsync(struct file *filp, struct dentry *dentry,
-               int datasync)
+static int ntfs_file_fsync(struct file *filp, int datasync)
 {
-       struct inode *vi = dentry->d_inode;
+       struct inode *vi = filp->f_mapping->host;
        int err, ret = 0;
 
        ntfs_debug("Entering for inode 0x%lx.", vi->i_ino);
index 97e54b9..6a13ea6 100644 (file)
@@ -175,13 +175,12 @@ static int ocfs2_dir_release(struct inode *inode, struct file *file)
        return 0;
 }
 
-static int ocfs2_sync_file(struct file *file,
-                          struct dentry *dentry,
-                          int datasync)
+static int ocfs2_sync_file(struct file *file, int datasync)
 {
        int err = 0;
        journal_t *journal;
-       struct inode *inode = dentry->d_inode;
+       struct dentry *dentry = file->f_path.dentry;
+       struct inode *inode = file->f_mapping->host;
        struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
 
        mlog_entry("(0x%p, 0x%p, %d, '%.*s')\n", file, dentry, datasync,
@@ -1053,7 +1052,7 @@ int ocfs2_setattr(struct dentry *dentry, struct iattr *attr)
        }
 
        /*
-        * This will intentionally not wind up calling vmtruncate(),
+        * This will intentionally not wind up calling simple_setsize(),
         * since all the work for a size change has been done above.
         * Otherwise, we could get into problems with truncate as
         * ip_alloc_sem is used there to protect against i_size
@@ -2119,9 +2118,13 @@ relock:
                         * direct write may have instantiated a few
                         * blocks outside i_size. Trim these off again.
                         * Don't need i_size_read because we hold i_mutex.
+                        *
+                        * XXX(hch): this looks buggy because ocfs2 did not
+                        * actually implement ->truncate.  Take a look at
+                        * the new truncate sequence and update this accordingly
                         */
                        if (*ppos + count > inode->i_size)
-                               vmtruncate(inode, inode->i_size);
+                               simple_setsize(inode, inode->i_size);
                        ret = written;
                        goto out_dio;
                }
index 2c26ce2..0eaa929 100644 (file)
@@ -879,13 +879,15 @@ static int ocfs2_susp_quotas(struct ocfs2_super *osb, int unsuspend)
                if (!OCFS2_HAS_RO_COMPAT_FEATURE(sb, feature[type]))
                        continue;
                if (unsuspend)
-                       status = vfs_quota_enable(
-                                       sb_dqopt(sb)->files[type],
-                                       type, QFMT_OCFS2,
-                                       DQUOT_SUSPENDED);
-               else
-                       status = vfs_quota_disable(sb, type,
-                                                  DQUOT_SUSPENDED);
+                       status = dquot_resume(sb, type);
+               else {
+                       struct ocfs2_mem_dqinfo *oinfo;
+
+                       /* Cancel periodic syncing before suspending */
+                       oinfo = sb_dqinfo(sb, type)->dqi_priv;
+                       cancel_delayed_work_sync(&oinfo->dqi_sync_work);
+                       status = dquot_suspend(sb, type);
+               }
                if (status < 0)
                        break;
        }
@@ -916,8 +918,8 @@ static int ocfs2_enable_quotas(struct ocfs2_super *osb)
                        status = -ENOENT;
                        goto out_quota_off;
                }
-               status = vfs_quota_enable(inode[type], type, QFMT_OCFS2,
-                                               DQUOT_USAGE_ENABLED);
+               status = dquot_enable(inode[type], type, QFMT_OCFS2,
+                                     DQUOT_USAGE_ENABLED);
                if (status < 0)
                        goto out_quota_off;
        }
@@ -952,8 +954,8 @@ static void ocfs2_disable_quotas(struct ocfs2_super *osb)
                /* Turn off quotas. This will remove all dquot structures from
                 * memory and so they will be automatically synced to global
                 * quota files */
-               vfs_quota_disable(sb, type, DQUOT_USAGE_ENABLED |
-                                           DQUOT_LIMITS_ENABLED);
+               dquot_disable(sb, type, DQUOT_USAGE_ENABLED |
+                                       DQUOT_LIMITS_ENABLED);
                if (!inode)
                        continue;
                iput(inode);
@@ -962,7 +964,7 @@ static void ocfs2_disable_quotas(struct ocfs2_super *osb)
 
 /* Handle quota on quotactl */
 static int ocfs2_quota_on(struct super_block *sb, int type, int format_id,
-                         char *path, int remount)
+                         char *path)
 {
        unsigned int feature[MAXQUOTAS] = { OCFS2_FEATURE_RO_COMPAT_USRQUOTA,
                                             OCFS2_FEATURE_RO_COMPAT_GRPQUOTA};
@@ -970,30 +972,24 @@ static int ocfs2_quota_on(struct super_block *sb, int type, int format_id,
        if (!OCFS2_HAS_RO_COMPAT_FEATURE(sb, feature[type]))
                return -EINVAL;
 
-       if (remount)
-               return 0;       /* Just ignore it has been handled in
-                                * ocfs2_remount() */
-       return vfs_quota_enable(sb_dqopt(sb)->files[type], type,
-                                   format_id, DQUOT_LIMITS_ENABLED);
+       return dquot_enable(sb_dqopt(sb)->files[type], type,
+                           format_id, DQUOT_LIMITS_ENABLED);
 }
 
 /* Handle quota off quotactl */
-static int ocfs2_quota_off(struct super_block *sb, int type, int remount)
+static int ocfs2_quota_off(struct super_block *sb, int type)
 {
-       if (remount)
-               return 0;       /* Ignore now and handle later in
-                                * ocfs2_remount() */
-       return vfs_quota_disable(sb, type, DQUOT_LIMITS_ENABLED);
+       return dquot_disable(sb, type, DQUOT_LIMITS_ENABLED);
 }
 
 static const struct quotactl_ops ocfs2_quotactl_ops = {
        .quota_on       = ocfs2_quota_on,
        .quota_off      = ocfs2_quota_off,
-       .quota_sync     = vfs_quota_sync,
-       .get_info       = vfs_get_dqinfo,
-       .set_info       = vfs_set_dqinfo,
-       .get_dqblk      = vfs_get_dqblk,
-       .set_dqblk      = vfs_set_dqblk,
+       .quota_sync     = dquot_quota_sync,
+       .get_info       = dquot_get_dqinfo,
+       .set_info       = dquot_set_dqinfo,
+       .get_dqblk      = dquot_get_dqblk,
+       .set_dqblk      = dquot_set_dqblk,
 };
 
 static int ocfs2_fill_super(struct super_block *sb, void *data, int silent)
index 399487c..6e7a329 100644 (file)
@@ -329,7 +329,7 @@ const struct file_operations omfs_file_operations = {
        .aio_read = generic_file_aio_read,
        .aio_write = generic_file_aio_write,
        .mmap = generic_file_mmap,
-       .fsync = simple_fsync,
+       .fsync = generic_file_fsync,
        .splice_read = generic_file_splice_read,
 };
 
index d79872e..60da077 100644 (file)
--- a/fs/pipe.c
+++ b/fs/pipe.c
@@ -1169,14 +1169,18 @@ long pipe_fcntl(struct file *file, unsigned int cmd, unsigned long arg)
 
        switch (cmd) {
        case F_SETPIPE_SZ:
-               if (!capable(CAP_SYS_ADMIN) && arg > pipe_max_pages)
-                       return -EINVAL;
+               if (!capable(CAP_SYS_ADMIN) && arg > pipe_max_pages) {
+                       ret = -EINVAL;
+                       goto out;
+               }
                /*
                 * The pipe needs to be at least 2 pages large to
                 * guarantee POSIX behaviour.
                 */
-               if (arg < 2)
-                       return -EINVAL;
+               if (arg < 2) {
+                       ret = -EINVAL;
+                       goto out;
+               }
                ret = pipe_set_size(pipe, arg);
                break;
        case F_GETPIPE_SZ:
@@ -1187,6 +1191,7 @@ long pipe_fcntl(struct file *file, unsigned int cmd, unsigned long arg)
                break;
        }
 
+out:
        mutex_unlock(&pipe->inode->i_mutex);
        return ret;
 }
index 3d3fd46..6e8fc62 100644 (file)
@@ -80,7 +80,7 @@ const struct file_operations qnx4_dir_operations =
        .llseek         = generic_file_llseek,
        .read           = generic_read_dir,
        .readdir        = qnx4_readdir,
-       .fsync          = simple_fsync,
+       .fsync          = generic_file_fsync,
 };
 
 const struct inode_operations qnx4_dir_inode_operations =
index 1ad8bf0..12c233d 100644 (file)
@@ -228,10 +228,6 @@ static struct hlist_head *dquot_hash;
 
 struct dqstats dqstats;
 EXPORT_SYMBOL(dqstats);
-#ifdef CONFIG_SMP
-struct dqstats *dqstats_pcpu;
-EXPORT_SYMBOL(dqstats_pcpu);
-#endif
 
 static qsize_t inode_get_rsv_space(struct inode *inode);
 static void __dquot_initialize(struct inode *inode, int type);
@@ -584,7 +580,7 @@ out:
 }
 EXPORT_SYMBOL(dquot_scan_active);
 
-int vfs_quota_sync(struct super_block *sb, int type, int wait)
+int dquot_quota_sync(struct super_block *sb, int type, int wait)
 {
        struct list_head *dirty;
        struct dquot *dquot;
@@ -656,7 +652,7 @@ int vfs_quota_sync(struct super_block *sb, int type, int wait)
 
        return 0;
 }
-EXPORT_SYMBOL(vfs_quota_sync);
+EXPORT_SYMBOL(dquot_quota_sync);
 
 /* Free unused dquots from cache */
 static void prune_dqcache(int count)
@@ -676,27 +672,10 @@ static void prune_dqcache(int count)
        }
 }
 
-static int dqstats_read(unsigned int type)
-{
-       int count = 0;
-#ifdef CONFIG_SMP
-       int cpu;
-       for_each_possible_cpu(cpu)
-               count += per_cpu_ptr(dqstats_pcpu, cpu)->stat[type];
-       /* Statistics reading is racy, but absolute accuracy isn't required */
-       if (count < 0)
-               count = 0;
-#else
-       count = dqstats.stat[type];
-#endif
-       return count;
-}
-
 /*
  * This is called from kswapd when we think we need some
  * more memory
  */
-
 static int shrink_dqcache_memory(int nr, gfp_t gfp_mask)
 {
        if (nr) {
@@ -704,7 +683,9 @@ static int shrink_dqcache_memory(int nr, gfp_t gfp_mask)
                prune_dqcache(nr);
                spin_unlock(&dq_list_lock);
        }
-       return (dqstats_read(DQST_FREE_DQUOTS)/100) * sysctl_vfs_cache_pressure;
+       return ((unsigned)
+               percpu_counter_read_positive(&dqstats.counter[DQST_FREE_DQUOTS])
+               /100) * sysctl_vfs_cache_pressure;
 }
 
 static struct shrinker dqcache_shrinker = {
@@ -1815,7 +1796,7 @@ int dquot_transfer(struct inode *inode, struct iattr *iattr)
        if (iattr->ia_valid & ATTR_UID && iattr->ia_uid != inode->i_uid)
                transfer_to[USRQUOTA] = dqget(sb, iattr->ia_uid, USRQUOTA);
        if (iattr->ia_valid & ATTR_GID && iattr->ia_gid != inode->i_gid)
-               transfer_to[GRPQUOTA] = dqget(sb, iattr->ia_uid, GRPQUOTA);
+               transfer_to[GRPQUOTA] = dqget(sb, iattr->ia_gid, GRPQUOTA);
 
        ret = __dquot_transfer(inode, transfer_to);
        dqput_all(transfer_to);
@@ -1850,6 +1831,7 @@ const struct dquot_operations dquot_operations = {
        .alloc_dquot    = dquot_alloc,
        .destroy_dquot  = dquot_destroy,
 };
+EXPORT_SYMBOL(dquot_operations);
 
 /*
  * Generic helper for ->open on filesystems supporting disk quotas.
@@ -1868,7 +1850,7 @@ EXPORT_SYMBOL(dquot_file_open);
 /*
  * Turn quota off on a device. type == -1 ==> quotaoff for all types (umount)
  */
-int vfs_quota_disable(struct super_block *sb, int type, unsigned int flags)
+int dquot_disable(struct super_block *sb, int type, unsigned int flags)
 {
        int cnt, ret = 0;
        struct quota_info *dqopt = sb_dqopt(sb);
@@ -1998,14 +1980,15 @@ put_inodes:
                }
        return ret;
 }
-EXPORT_SYMBOL(vfs_quota_disable);
+EXPORT_SYMBOL(dquot_disable);
 
-int vfs_quota_off(struct super_block *sb, int type, int remount)
+int dquot_quota_off(struct super_block *sb, int type)
 {
-       return vfs_quota_disable(sb, type, remount ? DQUOT_SUSPENDED :
-                                (DQUOT_USAGE_ENABLED | DQUOT_LIMITS_ENABLED));
+       return dquot_disable(sb, type,
+                            DQUOT_USAGE_ENABLED | DQUOT_LIMITS_ENABLED);
 }
-EXPORT_SYMBOL(vfs_quota_off);
+EXPORT_SYMBOL(dquot_quota_off);
+
 /*
  *     Turn quotas on on a device
  */
@@ -2123,36 +2106,43 @@ out_fmt:
 }
 
 /* Reenable quotas on remount RW */
-static int vfs_quota_on_remount(struct super_block *sb, int type)
+int dquot_resume(struct super_block *sb, int type)
 {
        struct quota_info *dqopt = sb_dqopt(sb);
        struct inode *inode;
-       int ret;
+       int ret = 0, cnt;
        unsigned int flags;
 
-       mutex_lock(&dqopt->dqonoff_mutex);
-       if (!sb_has_quota_suspended(sb, type)) {
+       for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
+               if (type != -1 && cnt != type)
+                       continue;
+
+               mutex_lock(&dqopt->dqonoff_mutex);
+               if (!sb_has_quota_suspended(sb, cnt)) {
+                       mutex_unlock(&dqopt->dqonoff_mutex);
+                       continue;
+               }
+               inode = dqopt->files[cnt];
+               dqopt->files[cnt] = NULL;
+               spin_lock(&dq_state_lock);
+               flags = dqopt->flags & dquot_state_flag(DQUOT_USAGE_ENABLED |
+                                                       DQUOT_LIMITS_ENABLED,
+                                                       cnt);
+               dqopt->flags &= ~dquot_state_flag(DQUOT_STATE_FLAGS, cnt);
+               spin_unlock(&dq_state_lock);
                mutex_unlock(&dqopt->dqonoff_mutex);
-               return 0;
-       }
-       inode = dqopt->files[type];
-       dqopt->files[type] = NULL;
-       spin_lock(&dq_state_lock);
-       flags = dqopt->flags & dquot_state_flag(DQUOT_USAGE_ENABLED |
-                                               DQUOT_LIMITS_ENABLED, type);
-       dqopt->flags &= ~dquot_state_flag(DQUOT_STATE_FLAGS, type);
-       spin_unlock(&dq_state_lock);
-       mutex_unlock(&dqopt->dqonoff_mutex);
 
-       flags = dquot_generic_flag(flags, type);
-       ret = vfs_load_quota_inode(inode, type, dqopt->info[type].dqi_fmt_id,
-                                  flags);
-       iput(inode);
+               flags = dquot_generic_flag(flags, cnt);
+               ret = vfs_load_quota_inode(inode, cnt,
+                               dqopt->info[cnt].dqi_fmt_id, flags);
+               iput(inode);
+       }
 
        return ret;
 }
+EXPORT_SYMBOL(dquot_resume);
 
-int vfs_quota_on_path(struct super_block *sb, int type, int format_id,
+int dquot_quota_on_path(struct super_block *sb, int type, int format_id,
                      struct path *path)
 {
        int error = security_quota_on(path->dentry);
@@ -2167,40 +2157,36 @@ int vfs_quota_on_path(struct super_block *sb, int type, int format_id,
                                             DQUOT_LIMITS_ENABLED);
        return error;
 }
-EXPORT_SYMBOL(vfs_quota_on_path);
+EXPORT_SYMBOL(dquot_quota_on_path);
 
-int vfs_quota_on(struct super_block *sb, int type, int format_id, char *name,
-                int remount)
+int dquot_quota_on(struct super_block *sb, int type, int format_id, char *name)
 {
        struct path path;
        int error;
 
-       if (remount)
-               return vfs_quota_on_remount(sb, type);
-
        error = kern_path(name, LOOKUP_FOLLOW, &path);
        if (!error) {
-               error = vfs_quota_on_path(sb, type, format_id, &path);
+               error = dquot_quota_on_path(sb, type, format_id, &path);
                path_put(&path);
        }
        return error;
 }
-EXPORT_SYMBOL(vfs_quota_on);
+EXPORT_SYMBOL(dquot_quota_on);
 
 /*
  * More powerful function for turning on quotas allowing setting
  * of individual quota flags
  */
-int vfs_quota_enable(struct inode *inode, int type, int format_id,
-               unsigned int flags)
+int dquot_enable(struct inode *inode, int type, int format_id,
+                unsigned int flags)
 {
        int ret = 0;
        struct super_block *sb = inode->i_sb;
        struct quota_info *dqopt = sb_dqopt(sb);
 
        /* Just unsuspend quotas? */
-       if (flags & DQUOT_SUSPENDED)
-               return vfs_quota_on_remount(sb, type);
+       BUG_ON(flags & DQUOT_SUSPENDED);
+
        if (!flags)
                return 0;
        /* Just updating flags needed? */
@@ -2232,13 +2218,13 @@ out_lock:
 load_quota:
        return vfs_load_quota_inode(inode, type, format_id, flags);
 }
-EXPORT_SYMBOL(vfs_quota_enable);
+EXPORT_SYMBOL(dquot_enable);
 
 /*
  * This function is used when filesystem needs to initialize quotas
  * during mount time.
  */
-int vfs_quota_on_mount(struct super_block *sb, char *qf_name,
+int dquot_quota_on_mount(struct super_block *sb, char *qf_name,
                int format_id, int type)
 {
        struct dentry *dentry;
@@ -2264,24 +2250,7 @@ out:
        dput(dentry);
        return error;
 }
-EXPORT_SYMBOL(vfs_quota_on_mount);
-
-/* Wrapper to turn on quotas when remounting rw */
-int vfs_dq_quota_on_remount(struct super_block *sb)
-{
-       int cnt;
-       int ret = 0, err;
-
-       if (!sb->s_qcop || !sb->s_qcop->quota_on)
-               return -ENOSYS;
-       for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
-               err = sb->s_qcop->quota_on(sb, cnt, 0, NULL, 1);
-               if (err < 0 && !ret)
-                       ret = err;
-       }
-       return ret;
-}
-EXPORT_SYMBOL(vfs_dq_quota_on_remount);
+EXPORT_SYMBOL(dquot_quota_on_mount);
 
 static inline qsize_t qbtos(qsize_t blocks)
 {
@@ -2316,8 +2285,8 @@ static void do_get_dqblk(struct dquot *dquot, struct fs_disk_quota *di)
        spin_unlock(&dq_data_lock);
 }
 
-int vfs_get_dqblk(struct super_block *sb, int type, qid_t id,
-                 struct fs_disk_quota *di)
+int dquot_get_dqblk(struct super_block *sb, int type, qid_t id,
+                   struct fs_disk_quota *di)
 {
        struct dquot *dquot;
 
@@ -2329,7 +2298,7 @@ int vfs_get_dqblk(struct super_block *sb, int type, qid_t id,
 
        return 0;
 }
-EXPORT_SYMBOL(vfs_get_dqblk);
+EXPORT_SYMBOL(dquot_get_dqblk);
 
 #define VFS_FS_DQ_MASK \
        (FS_DQ_BCOUNT | FS_DQ_BSOFT | FS_DQ_BHARD | \
@@ -2428,7 +2397,7 @@ static int do_set_dqblk(struct dquot *dquot, struct fs_disk_quota *di)
        return 0;
 }
 
-int vfs_set_dqblk(struct super_block *sb, int type, qid_t id,
+int dquot_set_dqblk(struct super_block *sb, int type, qid_t id,
                  struct fs_disk_quota *di)
 {
        struct dquot *dquot;
@@ -2444,10 +2413,10 @@ int vfs_set_dqblk(struct super_block *sb, int type, qid_t id,
 out:
        return rc;
 }
-EXPORT_SYMBOL(vfs_set_dqblk);
+EXPORT_SYMBOL(dquot_set_dqblk);
 
 /* Generic routine for getting common part of quota file information */
-int vfs_get_dqinfo(struct super_block *sb, int type, struct if_dqinfo *ii)
+int dquot_get_dqinfo(struct super_block *sb, int type, struct if_dqinfo *ii)
 {
        struct mem_dqinfo *mi;
   
@@ -2466,10 +2435,10 @@ int vfs_get_dqinfo(struct super_block *sb, int type, struct if_dqinfo *ii)
        mutex_unlock(&sb_dqopt(sb)->dqonoff_mutex);
        return 0;
 }
-EXPORT_SYMBOL(vfs_get_dqinfo);
+EXPORT_SYMBOL(dquot_get_dqinfo);
 
 /* Generic routine for setting common part of quota file information */
-int vfs_set_dqinfo(struct super_block *sb, int type, struct if_dqinfo *ii)
+int dquot_set_dqinfo(struct super_block *sb, int type, struct if_dqinfo *ii)
 {
        struct mem_dqinfo *mi;
        int err = 0;
@@ -2496,27 +2465,27 @@ out:
        mutex_unlock(&sb_dqopt(sb)->dqonoff_mutex);
        return err;
 }
-EXPORT_SYMBOL(vfs_set_dqinfo);
+EXPORT_SYMBOL(dquot_set_dqinfo);
 
-const struct quotactl_ops vfs_quotactl_ops = {
-       .quota_on       = vfs_quota_on,
-       .quota_off      = vfs_quota_off,
-       .quota_sync     = vfs_quota_sync,
-       .get_info       = vfs_get_dqinfo,
-       .set_info       = vfs_set_dqinfo,
-       .get_dqblk      = vfs_get_dqblk,
-       .set_dqblk      = vfs_set_dqblk
+const struct quotactl_ops dquot_quotactl_ops = {
+       .quota_on       = dquot_quota_on,
+       .quota_off      = dquot_quota_off,
+       .quota_sync     = dquot_quota_sync,
+       .get_info       = dquot_get_dqinfo,
+       .set_info       = dquot_set_dqinfo,
+       .get_dqblk      = dquot_get_dqblk,
+       .set_dqblk      = dquot_set_dqblk
 };
-
+EXPORT_SYMBOL(dquot_quotactl_ops);
 
 static int do_proc_dqstats(struct ctl_table *table, int write,
                     void __user *buffer, size_t *lenp, loff_t *ppos)
 {
-#ifdef CONFIG_SMP
-       /* Update global table */
        unsigned int type = (int *)table->data - dqstats.stat;
-       dqstats.stat[type] = dqstats_read(type);
-#endif
+
+       /* Update global table */
+       dqstats.stat[type] =
+                       percpu_counter_sum_positive(&dqstats.counter[type]);
        return proc_dointvec(table, write, buffer, lenp, ppos);
 }
 
@@ -2609,7 +2578,7 @@ static ctl_table sys_table[] = {
 
 static int __init dquot_init(void)
 {
-       int i;
+       int i, ret;
        unsigned long nr_hash, order;
 
        printk(KERN_NOTICE "VFS: Disk quotas %s\n", __DQUOT_VERSION__);
@@ -2627,12 +2596,11 @@ static int __init dquot_init(void)
        if (!dquot_hash)
                panic("Cannot create dquot hash table");
 
-#ifdef CONFIG_SMP
-       dqstats_pcpu = alloc_percpu(struct dqstats);
-       if (!dqstats_pcpu)
-               panic("Cannot create dquot stats table");
-#endif
-       memset(&dqstats, 0, sizeof(struct dqstats));
+       for (i = 0; i < _DQST_DQSTAT_LAST; i++) {
+               ret = percpu_counter_init(&dqstats.counter[i], 0);
+               if (ret)
+                       panic("Cannot create dquot stat counters");
+       }
 
        /* Find power-of-two hlist_heads which can fit into allocation */
        nr_hash = (1UL << order) * PAGE_SIZE / sizeof(struct hlist_head);
index ce3dfd0..b299961 100644 (file)
@@ -73,7 +73,7 @@ static int quota_quotaon(struct super_block *sb, int type, int cmd, qid_t id,
        if (IS_ERR(pathname))
                return PTR_ERR(pathname);
        if (sb->s_qcop->quota_on)
-               ret = sb->s_qcop->quota_on(sb, type, id, pathname, 0);
+               ret = sb->s_qcop->quota_on(sb, type, id, pathname);
        putname(pathname);
        return ret;
 }
@@ -260,7 +260,7 @@ static int do_quotactl(struct super_block *sb, int type, int cmd, qid_t id,
        case Q_QUOTAOFF:
                if (!sb->s_qcop->quota_off)
                        return -ENOSYS;
-               return sb->s_qcop->quota_off(sb, type, 0);
+               return sb->s_qcop->quota_off(sb, type);
        case Q_GETFMT:
                return quota_getfmt(sb, type, addr);
        case Q_GETINFO:
index 78f613c..4884ac5 100644 (file)
@@ -43,12 +43,13 @@ const struct file_operations ramfs_file_operations = {
        .write          = do_sync_write,
        .aio_write      = generic_file_aio_write,
        .mmap           = generic_file_mmap,
-       .fsync          = simple_sync_file,
+       .fsync          = noop_fsync,
        .splice_read    = generic_file_splice_read,
        .splice_write   = generic_file_splice_write,
        .llseek         = generic_file_llseek,
 };
 
 const struct inode_operations ramfs_file_inode_operations = {
+       .setattr        = simple_setattr,
        .getattr        = simple_getattr,
 };
index 5ea4ad8..d532c20 100644 (file)
@@ -42,7 +42,7 @@ const struct file_operations ramfs_file_operations = {
        .aio_read               = generic_file_aio_read,
        .write                  = do_sync_write,
        .aio_write              = generic_file_aio_write,
-       .fsync                  = simple_sync_file,
+       .fsync                  = noop_fsync,
        .splice_read            = generic_file_splice_read,
        .splice_write           = generic_file_splice_write,
        .llseek                 = generic_file_llseek,
@@ -146,7 +146,7 @@ static int ramfs_nommu_resize(struct inode *inode, loff_t newsize, loff_t size)
                        return ret;
        }
 
-       ret = vmtruncate(inode, newsize);
+       ret = simple_setsize(inode, newsize);
 
        return ret;
 }
@@ -169,7 +169,8 @@ static int ramfs_nommu_setattr(struct dentry *dentry, struct iattr *ia)
 
        /* pick out size-changing events */
        if (ia->ia_valid & ATTR_SIZE) {
-               loff_t size = i_size_read(inode);
+               loff_t size = inode->i_size;
+
                if (ia->ia_size != size) {
                        ret = ramfs_nommu_resize(inode, ia->ia_size, size);
                        if (ret < 0 || ia->ia_valid == ATTR_SIZE)
@@ -182,7 +183,7 @@ static int ramfs_nommu_setattr(struct dentry *dentry, struct iattr *ia)
                }
        }
 
-       ret = inode_setattr(inode, ia);
+       generic_setattr(inode, ia);
  out:
        ia->ia_valid = old_ia_valid;
        return ret;
index 4455fbe..198dabf 100644 (file)
@@ -14,8 +14,7 @@
 extern const struct reiserfs_key MIN_KEY;
 
 static int reiserfs_readdir(struct file *, void *, filldir_t);
-static int reiserfs_dir_fsync(struct file *filp, struct dentry *dentry,
-                             int datasync);
+static int reiserfs_dir_fsync(struct file *filp, int datasync);
 
 const struct file_operations reiserfs_dir_operations = {
        .llseek = generic_file_llseek,
@@ -28,10 +27,9 @@ const struct file_operations reiserfs_dir_operations = {
 #endif
 };
 
-static int reiserfs_dir_fsync(struct file *filp, struct dentry *dentry,
-                             int datasync)
+static int reiserfs_dir_fsync(struct file *filp, int datasync)
 {
-       struct inode *inode = dentry->d_inode;
+       struct inode *inode = filp->f_mapping->host;
        int err;
        reiserfs_write_lock(inode->i_sb);
        err = reiserfs_commit_for_inode(inode);
index 9977df9..b82cdd8 100644 (file)
@@ -134,10 +134,9 @@ static void reiserfs_vfs_truncate_file(struct inode *inode)
  * be removed...
  */
 
-static int reiserfs_sync_file(struct file *filp,
-                             struct dentry *dentry, int datasync)
+static int reiserfs_sync_file(struct file *filp, int datasync)
 {
-       struct inode *inode = dentry->d_inode;
+       struct inode *inode = filp->f_mapping->host;
        int err;
        int barrier_done;
 
index 59125fb..9822fa1 100644 (file)
@@ -158,6 +158,7 @@ static int finish_unfinished(struct super_block *s)
 #ifdef CONFIG_QUOTA
        int i;
        int ms_active_set;
+       int quota_enabled[MAXQUOTAS];
 #endif
 
        /* compose key to look for "save" links */
@@ -179,8 +180,15 @@ static int finish_unfinished(struct super_block *s)
        }
        /* Turn on quotas so that they are updated correctly */
        for (i = 0; i < MAXQUOTAS; i++) {
+               quota_enabled[i] = 1;
                if (REISERFS_SB(s)->s_qf_names[i]) {
-                       int ret = reiserfs_quota_on_mount(s, i);
+                       int ret;
+
+                       if (sb_has_quota_active(s, i)) {
+                               quota_enabled[i] = 0;
+                               continue;
+                       }
+                       ret = reiserfs_quota_on_mount(s, i);
                        if (ret < 0)
                                reiserfs_warning(s, "reiserfs-2500",
                                                 "cannot turn on journaled "
@@ -304,8 +312,8 @@ static int finish_unfinished(struct super_block *s)
 #ifdef CONFIG_QUOTA
        /* Turn quotas off */
        for (i = 0; i < MAXQUOTAS; i++) {
-               if (sb_dqopt(s)->files[i])
-                       vfs_quota_off(s, i, 0);
+               if (sb_dqopt(s)->files[i] && quota_enabled[i])
+                       dquot_quota_off(s, i);
        }
        if (ms_active_set)
                /* Restore the flag back */
@@ -466,6 +474,8 @@ static void reiserfs_put_super(struct super_block *s)
        struct reiserfs_transaction_handle th;
        th.t_trans_id = 0;
 
+       dquot_disable(s, -1, DQUOT_USAGE_ENABLED | DQUOT_LIMITS_ENABLED);
+
        reiserfs_write_lock(s);
 
        if (s->s_dirt)
@@ -620,7 +630,7 @@ static int reiserfs_acquire_dquot(struct dquot *);
 static int reiserfs_release_dquot(struct dquot *);
 static int reiserfs_mark_dquot_dirty(struct dquot *);
 static int reiserfs_write_info(struct super_block *, int);
-static int reiserfs_quota_on(struct super_block *, int, int, char *, int);
+static int reiserfs_quota_on(struct super_block *, int, int, char *);
 
 static const struct dquot_operations reiserfs_quota_operations = {
        .write_dquot = reiserfs_write_dquot,
@@ -634,12 +644,12 @@ static const struct dquot_operations reiserfs_quota_operations = {
 
 static const struct quotactl_ops reiserfs_qctl_operations = {
        .quota_on = reiserfs_quota_on,
-       .quota_off = vfs_quota_off,
-       .quota_sync = vfs_quota_sync,
-       .get_info = vfs_get_dqinfo,
-       .set_info = vfs_set_dqinfo,
-       .get_dqblk = vfs_get_dqblk,
-       .set_dqblk = vfs_set_dqblk,
+       .quota_off = dquot_quota_off,
+       .quota_sync = dquot_quota_sync,
+       .get_info = dquot_get_dqinfo,
+       .set_info = dquot_set_dqinfo,
+       .get_dqblk = dquot_get_dqblk,
+       .set_dqblk = dquot_set_dqblk,
 };
 #endif
 
@@ -1242,6 +1252,11 @@ static int reiserfs_remount(struct super_block *s, int *mount_flags, char *arg)
                if (s->s_flags & MS_RDONLY)
                        /* it is read-only already */
                        goto out_ok;
+
+               err = dquot_suspend(s, -1);
+               if (err < 0)
+                       goto out_err;
+
                /* try to remount file system with read-only permissions */
                if (sb_umount_state(rs) == REISERFS_VALID_FS
                    || REISERFS_SB(s)->s_mount_state != REISERFS_VALID_FS) {
@@ -1295,6 +1310,7 @@ static int reiserfs_remount(struct super_block *s, int *mount_flags, char *arg)
        s->s_dirt = 0;
 
        if (!(*mount_flags & MS_RDONLY)) {
+               dquot_resume(s, -1);
                finish_unfinished(s);
                reiserfs_xattr_init(s, *mount_flags);
        }
@@ -2022,15 +2038,15 @@ static int reiserfs_write_info(struct super_block *sb, int type)
  */
 static int reiserfs_quota_on_mount(struct super_block *sb, int type)
 {
-       return vfs_quota_on_mount(sb, REISERFS_SB(sb)->s_qf_names[type],
-                                 REISERFS_SB(sb)->s_jquota_fmt, type);
+       return dquot_quota_on_mount(sb, REISERFS_SB(sb)->s_qf_names[type],
+                                       REISERFS_SB(sb)->s_jquota_fmt, type);
 }
 
 /*
  * Standard function to be called on quota_on
  */
 static int reiserfs_quota_on(struct super_block *sb, int type, int format_id,
-                            char *name, int remount)
+                            char *name)
 {
        int err;
        struct path path;
@@ -2039,9 +2055,7 @@ static int reiserfs_quota_on(struct super_block *sb, int type, int format_id,
 
        if (!(REISERFS_SB(sb)->s_mount_opt & (1 << REISERFS_QUOTA)))
                return -EINVAL;
-       /* No more checks needed? Path and format_id are bogus anyway... */
-       if (remount)
-               return vfs_quota_on(sb, type, format_id, name, 1);
+
        err = kern_path(name, LOOKUP_FOLLOW, &path);
        if (err)
                return err;
@@ -2085,7 +2099,7 @@ static int reiserfs_quota_on(struct super_block *sb, int type, int format_id,
                if (err)
                        goto out;
        }
-       err = vfs_quota_on_path(sb, type, format_id, &path);
+       err = dquot_quota_on_path(sb, type, format_id, &path);
 out:
        path_put(&path);
        return err;
index 84ecf0e..8e187a0 100644 (file)
@@ -28,8 +28,9 @@
 #include "proto.h"
 
 static int
-smb_fsync(struct file *file, struct dentry * dentry, int datasync)
+smb_fsync(struct file *file, int datasync)
 {
+       struct dentry *dentry = file->f_path.dentry;
        struct smb_sb_info *server = server_from_dentry(dentry);
        int result;
 
index dfa1d67..9551cb6 100644 (file)
@@ -714,7 +714,7 @@ smb_notify_change(struct dentry *dentry, struct iattr *attr)
                error = server->ops->truncate(inode, attr->ia_size);
                if (error)
                        goto out;
-               error = vmtruncate(inode, attr->ia_size);
+               error = simple_setsize(inode, attr->ia_size);
                if (error)
                        goto out;
                refresh = 1;
index 69688b1..5c35bc7 100644 (file)
@@ -24,7 +24,6 @@
 #include <linux/slab.h>
 #include <linux/acct.h>
 #include <linux/blkdev.h>
-#include <linux/quotaops.h>
 #include <linux/mount.h>
 #include <linux/security.h>
 #include <linux/writeback.h>           /* for the emergency remount stuff */
@@ -94,8 +93,6 @@ static struct super_block *alloc_super(struct file_system_type *type)
                init_rwsem(&s->s_dquot.dqptr_sem);
                init_waitqueue_head(&s->s_wait_unfrozen);
                s->s_maxbytes = MAX_NON_LFS;
-               s->dq_op = sb_dquot_ops;
-               s->s_qcop = sb_quotactl_ops;
                s->s_op = &default_op;
                s->s_time_gran = 1000000000;
        }
@@ -160,7 +157,6 @@ void deactivate_locked_super(struct super_block *s)
 {
        struct file_system_type *fs = s->s_type;
        if (atomic_dec_and_test(&s->s_active)) {
-               vfs_dq_off(s, 0);
                fs->kill_sb(s);
                put_filesystem(fs);
                put_super(s);
@@ -524,7 +520,7 @@ rescan:
 int do_remount_sb(struct super_block *sb, int flags, void *data, int force)
 {
        int retval;
-       int remount_rw, remount_ro;
+       int remount_ro;
 
        if (sb->s_frozen != SB_UNFROZEN)
                return -EBUSY;
@@ -540,7 +536,6 @@ int do_remount_sb(struct super_block *sb, int flags, void *data, int force)
        sync_filesystem(sb);
 
        remount_ro = (flags & MS_RDONLY) && !(sb->s_flags & MS_RDONLY);
-       remount_rw = !(flags & MS_RDONLY) && (sb->s_flags & MS_RDONLY);
 
        /* If we are remounting RDONLY and current sb is read/write,
           make sure there are no rw files opened */
@@ -549,9 +544,6 @@ int do_remount_sb(struct super_block *sb, int flags, void *data, int force)
                        mark_files_ro(sb);
                else if (!fs_may_remount_ro(sb))
                        return -EBUSY;
-               retval = vfs_dq_off(sb, 1);
-               if (retval < 0 && retval != -ENOSYS)
-                       return -EBUSY;
        }
 
        if (sb->s_op->remount_fs) {
@@ -560,8 +552,7 @@ int do_remount_sb(struct super_block *sb, int flags, void *data, int force)
                        return retval;
        }
        sb->s_flags = (sb->s_flags & ~MS_RMT_MASK) | (flags & MS_RMT_MASK);
-       if (remount_rw)
-               vfs_dq_quota_on_remount(sb);
+
        /*
         * Some filesystems modify their metadata via some other path than the
         * bdev buffer cache (eg. use a private mapping, or directories in
@@ -946,8 +937,8 @@ out:
 EXPORT_SYMBOL_GPL(vfs_kern_mount);
 
 /**
- * freeze_super -- lock the filesystem and force it into a consistent state
- * @super: the super to lock
+ * freeze_super - lock the filesystem and force it into a consistent state
+ * @sb: the super to lock
  *
  * Syncs the super to make sure the filesystem is consistent and calls the fs's
  * freeze_fs.  Subsequent calls to this without first thawing the fs will return
index e8cbd41..c9f83f4 100644 (file)
--- a/fs/sync.c
+++ b/fs/sync.c
@@ -130,12 +130,10 @@ void emergency_sync(void)
 
 /*
  * Generic function to fsync a file.
- *
- * filp may be NULL if called via the msync of a vma.
  */
-int file_fsync(struct file *filp, struct dentry *dentry, int datasync)
+int file_fsync(struct file *filp, int datasync)
 {
-       struct inode * inode = dentry->d_inode;
+       struct inode *inode = filp->f_mapping->host;
        struct super_block * sb;
        int ret, err;
 
@@ -183,7 +181,7 @@ int vfs_fsync_range(struct file *file, loff_t start, loff_t end, int datasync)
         * livelocks in fsync_buffers_list().
         */
        mutex_lock(&mapping->host->i_mutex);
-       err = file->f_op->fsync(file, file->f_path.dentry, datasync);
+       err = file->f_op->fsync(file, datasync);
        if (!ret)
                ret = err;
        mutex_unlock(&mapping->host->i_mutex);
index bbd77e9..bde1a4c 100644 (file)
@@ -117,13 +117,11 @@ int sysfs_setattr(struct dentry *dentry, struct iattr *iattr)
        if (error)
                goto out;
 
-       iattr->ia_valid &= ~ATTR_SIZE; /* ignore size changes */
-
-       error = inode_setattr(inode, iattr);
-       if (error)
-               goto out;
+       /* this ignores size changes */
+       generic_setattr(inode, iattr);
 
        error = sysfs_sd_setattr(sd, iattr);
+
 out:
        mutex_unlock(&sysfs_mutex);
        return error;
index 1dabed2..79941e4 100644 (file)
@@ -24,7 +24,7 @@ const struct file_operations sysv_dir_operations = {
        .llseek         = generic_file_llseek,
        .read           = generic_read_dir,
        .readdir        = sysv_readdir,
-       .fsync          = simple_fsync,
+       .fsync          = generic_file_fsync,
 };
 
 static inline void dir_put_page(struct page *page)
index 96340c0..750cc22 100644 (file)
@@ -26,7 +26,7 @@ const struct file_operations sysv_file_operations = {
        .write          = do_sync_write,
        .aio_write      = generic_file_aio_write,
        .mmap           = generic_file_mmap,
-       .fsync          = simple_fsync,
+       .fsync          = generic_file_fsync,
        .splice_read    = generic_file_splice_read,
 };
 
index 4573734..d4a5380 100644 (file)
@@ -43,6 +43,7 @@ static int sysv_sync_fs(struct super_block *sb, int wait)
         * then attach current time stamp.
         * But if the filesystem was marked clean, keep it clean.
         */
+       sb->s_dirt = 0;
        old_time = fs32_to_cpu(sbi, *sbi->s_sb_time);
        if (sbi->s_type == FSTYPE_SYSV4) {
                if (*sbi->s_sb_state == cpu_to_fs32(sbi, 0x7c269d38 - old_time))
index 5692cf7..12f445c 100644 (file)
@@ -967,12 +967,15 @@ static int do_writepage(struct page *page, int len)
  * the page locked, and it locks @ui_mutex. However, write-back does take inode
  * @i_mutex, which means other VFS operations may be run on this inode at the
  * same time. And the problematic one is truncation to smaller size, from where
- * we have to call 'vmtruncate()', which first changes @inode->i_size, then
+ * we have to call 'simple_setsize()', which first changes @inode->i_size, then
  * drops the truncated pages. And while dropping the pages, it takes the page
- * lock. This means that 'do_truncation()' cannot call 'vmtruncate()' with
+ * lock. This means that 'do_truncation()' cannot call 'simple_setsize()' with
  * @ui_mutex locked, because it would deadlock with 'ubifs_writepage()'. This
  * means that @inode->i_size is changed while @ui_mutex is unlocked.
  *
+ * XXX: with the new truncate the above is not true anymore, the simple_setsize
+ * calls can be replaced with the individual components.
+ *
  * But in 'ubifs_writepage()' we have to guarantee that we do not write beyond
  * inode size. How do we do this if @inode->i_size may became smaller while we
  * are in the middle of 'ubifs_writepage()'? The UBIFS solution is the
@@ -1125,7 +1128,7 @@ static int do_truncation(struct ubifs_info *c, struct inode *inode,
                budgeted = 0;
        }
 
-       err = vmtruncate(inode, new_size);
+       err = simple_setsize(inode, new_size);
        if (err)
                goto out_budg;
 
@@ -1214,7 +1217,7 @@ static int do_setattr(struct ubifs_info *c, struct inode *inode,
 
        if (attr->ia_valid & ATTR_SIZE) {
                dbg_gen("size %lld -> %lld", inode->i_size, new_size);
-               err = vmtruncate(inode, new_size);
+               err = simple_setsize(inode, new_size);
                if (err)
                        goto out;
        }
@@ -1223,7 +1226,7 @@ static int do_setattr(struct ubifs_info *c, struct inode *inode,
        if (attr->ia_valid & ATTR_SIZE) {
                /* Truncation changes inode [mc]time */
                inode->i_mtime = inode->i_ctime = ubifs_current_time(inode);
-               /* 'vmtruncate()' changed @i_size, update @ui_size */
+               /* 'simple_setsize()' changed @i_size, update @ui_size */
                ui->ui_size = inode->i_size;
        }
 
@@ -1304,9 +1307,9 @@ static void *ubifs_follow_link(struct dentry *dentry, struct nameidata *nd)
        return NULL;
 }
 
-int ubifs_fsync(struct file *file, struct dentry *dentry, int datasync)
+int ubifs_fsync(struct file *file, int datasync)
 {
-       struct inode *inode = dentry->d_inode;
+       struct inode *inode = file->f_mapping->host;
        struct ubifs_info *c = inode->i_sb->s_fs_info;
        int err;
 
index bd2542d..2eef553 100644 (file)
@@ -379,7 +379,7 @@ struct ubifs_gced_idx_leb {
  * The @ui_size is a "shadow" variable for @inode->i_size and UBIFS uses
  * @ui_size instead of @inode->i_size. The reason for this is that UBIFS cannot
  * make sure @inode->i_size is always changed under @ui_mutex, because it
- * cannot call 'vmtruncate()' with @ui_mutex locked, because it would deadlock
+ * cannot call 'simple_setsize()' with @ui_mutex locked, because it would deadlock
  * with 'ubifs_writepage()' (see file.c). All the other inode fields are
  * changed under @ui_mutex, so they do not need "shadow" fields. Note, one
  * could consider to rework locking and base it on "shadow" fields.
@@ -1678,7 +1678,7 @@ const struct ubifs_lprops *ubifs_fast_find_frdi_idx(struct ubifs_info *c);
 int ubifs_calc_dark(const struct ubifs_info *c, int spc);
 
 /* file.c */
-int ubifs_fsync(struct file *file, struct dentry *dentry, int datasync);
+int ubifs_fsync(struct file *file, int datasync);
 int ubifs_setattr(struct dentry *dentry, struct iattr *attr);
 
 /* dir.c */
index 9a9378b..b608efa 100644 (file)
@@ -21,7 +21,6 @@
 
 #include "udfdecl.h"
 
-#include <linux/quotaops.h>
 #include <linux/buffer_head.h>
 #include <linux/bitops.h>
 
@@ -159,8 +158,6 @@ static void udf_bitmap_free_blocks(struct super_block *sb,
                                udf_debug("byte=%2x\n",
                                        ((char *)bh->b_data)[(bit + i) >> 3]);
                        } else {
-                               if (inode)
-                                       dquot_free_block(inode, 1);
                                udf_add_free_space(sb, sbi->s_partition, 1);
                        }
                }
@@ -210,15 +207,8 @@ static int udf_bitmap_prealloc_blocks(struct super_block *sb,
                bit = block % (sb->s_blocksize << 3);
 
                while (bit < (sb->s_blocksize << 3) && block_count > 0) {
-                       if (!udf_test_bit(bit, bh->b_data))
+                       if (!udf_clear_bit(bit, bh->b_data))
                                goto out;
-                       else if (dquot_prealloc_block(inode, 1))
-                               goto out;
-                       else if (!udf_clear_bit(bit, bh->b_data)) {
-                               udf_debug("bit already cleared for block %d\n", bit);
-                               dquot_free_block(inode, 1);
-                               goto out;
-                       }
                        block_count--;
                        alloc_count++;
                        bit++;
@@ -338,20 +328,6 @@ search_back:
        }
 
 got_block:
-
-       /*
-        * Check quota for allocation of this block.
-        */
-       if (inode) {
-               int ret = dquot_alloc_block(inode, 1);
-
-               if (ret) {
-                       mutex_unlock(&sbi->s_alloc_mutex);
-                       *err = ret;
-                       return 0;
-               }
-       }
-
        newblock = bit + (block_group << (sb->s_blocksize_bits + 3)) -
                (sizeof(struct spaceBitmapDesc) << 3);
 
@@ -401,10 +377,6 @@ static void udf_table_free_blocks(struct super_block *sb,
        }
 
        iinfo = UDF_I(table);
-       /* We do this up front - There are some error conditions that
-          could occure, but.. oh well */
-       if (inode)
-               dquot_free_block(inode, count);
        udf_add_free_space(sb, sbi->s_partition, count);
 
        start = bloc->logicalBlockNum + offset;
@@ -649,10 +621,7 @@ static int udf_table_prealloc_blocks(struct super_block *sb,
                epos.offset -= adsize;
 
                alloc_count = (elen >> sb->s_blocksize_bits);
-               if (inode && dquot_prealloc_block(inode,
-                       alloc_count > block_count ? block_count : alloc_count))
-                       alloc_count = 0;
-               else if (alloc_count > block_count) {
+               if (alloc_count > block_count) {
                        alloc_count = block_count;
                        eloc.logicalBlockNum += alloc_count;
                        elen -= (alloc_count << sb->s_blocksize_bits);
@@ -752,14 +721,6 @@ static int udf_table_new_block(struct super_block *sb,
        newblock = goal_eloc.logicalBlockNum;
        goal_eloc.logicalBlockNum++;
        goal_elen -= sb->s_blocksize;
-       if (inode) {
-               *err = dquot_alloc_block(inode, 1);
-               if (*err) {
-                       brelse(goal_epos.bh);
-                       mutex_unlock(&sbi->s_alloc_mutex);
-                       return 0;
-               }
-       }
 
        if (goal_elen)
                udf_write_aext(table, &goal_epos, &goal_eloc, goal_elen, 1);
index 1660c81..51552bf 100644 (file)
@@ -211,5 +211,5 @@ const struct file_operations udf_dir_operations = {
        .read                   = generic_read_dir,
        .readdir                = udf_readdir,
        .unlocked_ioctl         = udf_ioctl,
-       .fsync                  = simple_fsync,
+       .fsync                  = generic_file_fsync,
 };
index baae3a7..94e06d6 100644 (file)
@@ -34,7 +34,6 @@
 #include <linux/errno.h>
 #include <linux/smp_lock.h>
 #include <linux/pagemap.h>
-#include <linux/quotaops.h>
 #include <linux/buffer_head.h>
 #include <linux/aio.h>
 #include <linux/smp_lock.h>
@@ -219,39 +218,16 @@ const struct file_operations udf_file_operations = {
        .read                   = do_sync_read,
        .aio_read               = generic_file_aio_read,
        .unlocked_ioctl         = udf_ioctl,
-       .open                   = dquot_file_open,
+       .open                   = generic_file_open,
        .mmap                   = generic_file_mmap,
        .write                  = do_sync_write,
        .aio_write              = udf_file_aio_write,
        .release                = udf_release_file,
-       .fsync                  = simple_fsync,
+       .fsync                  = generic_file_fsync,
        .splice_read            = generic_file_splice_read,
        .llseek                 = generic_file_llseek,
 };
 
-int udf_setattr(struct dentry *dentry, struct iattr *iattr)
-{
-       struct inode *inode = dentry->d_inode;
-       int error;
-
-       error = inode_change_ok(inode, iattr);
-       if (error)
-               return error;
-
-       if (is_quota_modification(inode, iattr))
-               dquot_initialize(inode);
-
-       if ((iattr->ia_valid & ATTR_UID && iattr->ia_uid != inode->i_uid) ||
-            (iattr->ia_valid & ATTR_GID && iattr->ia_gid != inode->i_gid)) {
-               error = dquot_transfer(inode, iattr);
-               if (error)
-                       return error;
-       }
-
-       return inode_setattr(inode, iattr);
-}
-
 const struct inode_operations udf_file_inode_operations = {
        .truncate               = udf_truncate,
-       .setattr                = udf_setattr,
 };
index 2b5586c..18cd711 100644 (file)
@@ -20,7 +20,6 @@
 
 #include "udfdecl.h"
 #include <linux/fs.h>
-#include <linux/quotaops.h>
 #include <linux/sched.h>
 #include <linux/slab.h>
 
@@ -32,13 +31,6 @@ void udf_free_inode(struct inode *inode)
        struct super_block *sb = inode->i_sb;
        struct udf_sb_info *sbi = UDF_SB(sb);
 
-       /*
-        * Note: we must free any quota before locking the superblock,
-        * as writing the quota to disk may need the lock as well.
-        */
-       dquot_free_inode(inode);
-       dquot_drop(inode);
-
        clear_inode(inode);
 
        mutex_lock(&sbi->s_alloc_mutex);
@@ -61,7 +53,7 @@ struct inode *udf_new_inode(struct inode *dir, int mode, int *err)
        struct super_block *sb = dir->i_sb;
        struct udf_sb_info *sbi = UDF_SB(sb);
        struct inode *inode;
-       int block, ret;
+       int block;
        uint32_t start = UDF_I(dir)->i_location.logicalBlockNum;
        struct udf_inode_info *iinfo;
        struct udf_inode_info *dinfo = UDF_I(dir);
@@ -146,17 +138,6 @@ struct inode *udf_new_inode(struct inode *dir, int mode, int *err)
        insert_inode_hash(inode);
        mark_inode_dirty(inode);
 
-       dquot_initialize(inode);
-       ret = dquot_alloc_inode(inode);
-       if (ret) {
-               dquot_drop(inode);
-               inode->i_flags |= S_NOQUOTA;
-               inode->i_nlink = 0;
-               iput(inode);
-               *err = ret;
-               return NULL;
-       }
-
        *err = 0;
        return inode;
 }
index 8a3fbd1..124852b 100644 (file)
@@ -36,7 +36,6 @@
 #include <linux/pagemap.h>
 #include <linux/buffer_head.h>
 #include <linux/writeback.h>
-#include <linux/quotaops.h>
 #include <linux/slab.h>
 #include <linux/crc-itu-t.h>
 
@@ -71,9 +70,6 @@ static int udf_get_block(struct inode *, sector_t, struct buffer_head *, int);
 
 void udf_delete_inode(struct inode *inode)
 {
-       if (!is_bad_inode(inode))
-               dquot_initialize(inode);
-
        truncate_inode_pages(&inode->i_data, 0);
 
        if (is_bad_inode(inode))
@@ -113,7 +109,6 @@ void udf_clear_inode(struct inode *inode)
                        (unsigned long long)iinfo->i_lenExtents);
        }
 
-       dquot_drop(inode);
        kfree(iinfo->i_ext.i_data);
        iinfo->i_ext.i_data = NULL;
 }
index 585f733..bf5fc67 100644 (file)
@@ -27,7 +27,6 @@
 #include <linux/errno.h>
 #include <linux/mm.h>
 #include <linux/slab.h>
-#include <linux/quotaops.h>
 #include <linux/smp_lock.h>
 #include <linux/buffer_head.h>
 #include <linux/sched.h>
@@ -563,8 +562,6 @@ static int udf_create(struct inode *dir, struct dentry *dentry, int mode,
        int err;
        struct udf_inode_info *iinfo;
 
-       dquot_initialize(dir);
-
        lock_kernel();
        inode = udf_new_inode(dir, mode, &err);
        if (!inode) {
@@ -617,8 +614,6 @@ static int udf_mknod(struct inode *dir, struct dentry *dentry, int mode,
        if (!old_valid_dev(rdev))
                return -EINVAL;
 
-       dquot_initialize(dir);
-
        lock_kernel();
        err = -EIO;
        inode = udf_new_inode(dir, mode, &err);
@@ -664,8 +659,6 @@ static int udf_mkdir(struct inode *dir, struct dentry *dentry, int mode)
        struct udf_inode_info *dinfo = UDF_I(dir);
        struct udf_inode_info *iinfo;
 
-       dquot_initialize(dir);
-
        lock_kernel();
        err = -EMLINK;
        if (dir->i_nlink >= (256 << sizeof(dir->i_nlink)) - 1)
@@ -800,8 +793,6 @@ static int udf_rmdir(struct inode *dir, struct dentry *dentry)
        struct fileIdentDesc *fi, cfi;
        struct kernel_lb_addr tloc;
 
-       dquot_initialize(dir);
-
        retval = -ENOENT;
        lock_kernel();
        fi = udf_find_entry(dir, &dentry->d_name, &fibh, &cfi);
@@ -848,8 +839,6 @@ static int udf_unlink(struct inode *dir, struct dentry *dentry)
        struct fileIdentDesc cfi;
        struct kernel_lb_addr tloc;
 
-       dquot_initialize(dir);
-
        retval = -ENOENT;
        lock_kernel();
        fi = udf_find_entry(dir, &dentry->d_name, &fibh, &cfi);
@@ -904,8 +893,6 @@ static int udf_symlink(struct inode *dir, struct dentry *dentry,
        struct buffer_head *bh;
        struct udf_inode_info *iinfo;
 
-       dquot_initialize(dir);
-
        lock_kernel();
        inode = udf_new_inode(dir, S_IFLNK | S_IRWXUGO, &err);
        if (!inode)
@@ -1075,8 +1062,6 @@ static int udf_link(struct dentry *old_dentry, struct inode *dir,
        int err;
        struct buffer_head *bh;
 
-       dquot_initialize(dir);
-
        lock_kernel();
        if (inode->i_nlink >= (256 << sizeof(inode->i_nlink)) - 1) {
                unlock_kernel();
@@ -1139,9 +1124,6 @@ static int udf_rename(struct inode *old_dir, struct dentry *old_dentry,
        struct kernel_lb_addr tloc;
        struct udf_inode_info *old_iinfo = UDF_I(old_inode);
 
-       dquot_initialize(old_dir);
-       dquot_initialize(new_dir);
-
        lock_kernel();
        ofi = udf_find_entry(old_dir, &old_dentry->d_name, &ofibh, &ocfi);
        if (ofi) {
@@ -1387,7 +1369,6 @@ const struct export_operations udf_export_ops = {
 const struct inode_operations udf_dir_inode_operations = {
        .lookup                         = udf_lookup,
        .create                         = udf_create,
-       .setattr                        = udf_setattr,
        .link                           = udf_link,
        .unlink                         = udf_unlink,
        .symlink                        = udf_symlink,
@@ -1400,5 +1381,4 @@ const struct inode_operations udf_symlink_inode_operations = {
        .readlink       = generic_readlink,
        .follow_link    = page_follow_link_light,
        .put_link       = page_put_link,
-       .setattr        = udf_setattr,
 };
index 1e4543c..612d1e2 100644 (file)
@@ -557,6 +557,7 @@ static int udf_remount_fs(struct super_block *sb, int *flags, char *options)
 {
        struct udf_options uopt;
        struct udf_sb_info *sbi = UDF_SB(sb);
+       int error = 0;
 
        uopt.flags = sbi->s_flags;
        uopt.uid   = sbi->s_uid;
@@ -582,17 +583,17 @@ static int udf_remount_fs(struct super_block *sb, int *flags, char *options)
                        *flags |= MS_RDONLY;
        }
 
-       if ((*flags & MS_RDONLY) == (sb->s_flags & MS_RDONLY)) {
-               unlock_kernel();
-               return 0;
-       }
+       if ((*flags & MS_RDONLY) == (sb->s_flags & MS_RDONLY))
+               goto out_unlock;
+
        if (*flags & MS_RDONLY)
                udf_close_lvid(sb);
        else
                udf_open_lvid(sb);
 
+out_unlock:
        unlock_kernel();
-       return 0;
+       return error;
 }
 
 /* Check Volume Structure Descriptors (ECMA 167 2/9.1) */
@@ -1939,7 +1940,7 @@ static int udf_fill_super(struct super_block *sb, void *options, int silent)
        /* Fill in the rest of the superblock */
        sb->s_op = &udf_sb_ops;
        sb->s_export_op = &udf_export_ops;
-       sb->dq_op = NULL;
+
        sb->s_dirt = 0;
        sb->s_magic = UDF_SUPER_MAGIC;
        sb->s_time_gran = 1000;
index 9079ff7..2bac035 100644 (file)
@@ -131,7 +131,6 @@ extern int udf_write_fi(struct inode *inode, struct fileIdentDesc *,
 
 /* file.c */
 extern long udf_ioctl(struct file *, unsigned int, unsigned long);
-extern int udf_setattr(struct dentry *dentry, struct iattr *iattr);
 /* inode.c */
 extern struct inode *udf_iget(struct super_block *, struct kernel_lb_addr *);
 extern int udf_sync_inode(struct inode *);
index 5cfa4d8..048484f 100644 (file)
@@ -12,7 +12,6 @@
 #include <linux/stat.h>
 #include <linux/time.h>
 #include <linux/string.h>
-#include <linux/quotaops.h>
 #include <linux/buffer_head.h>
 #include <linux/capability.h>
 #include <linux/bitops.h>
@@ -85,9 +84,6 @@ void ufs_free_fragments(struct inode *inode, u64 fragment, unsigned count)
                                   "bit already cleared for fragment %u", i);
        }
        
-       dquot_free_block(inode, count);
-
-       
        fs32_add(sb, &ucg->cg_cs.cs_nffree, count);
        uspi->cs_total.cs_nffree += count;
        fs32_add(sb, &UFS_SB(sb)->fs_cs(cgno).cs_nffree, count);
@@ -195,7 +191,6 @@ do_more:
                ubh_setblock(UCPI_UBH(ucpi), ucpi->c_freeoff, blkno);
                if ((UFS_SB(sb)->s_flags & UFS_CG_MASK) == UFS_CG_44BSD)
                        ufs_clusteracct (sb, ucpi, blkno, 1);
-               dquot_free_block(inode, uspi->s_fpb);
 
                fs32_add(sb, &ucg->cg_cs.cs_nbfree, 1);
                uspi->cs_total.cs_nbfree++;
@@ -511,7 +506,6 @@ static u64 ufs_add_fragments(struct inode *inode, u64 fragment,
        struct ufs_cg_private_info * ucpi;
        struct ufs_cylinder_group * ucg;
        unsigned cgno, fragno, fragoff, count, fragsize, i;
-       int ret;
        
        UFSD("ENTER, fragment %llu, oldcount %u, newcount %u\n",
             (unsigned long long)fragment, oldcount, newcount);
@@ -557,11 +551,6 @@ static u64 ufs_add_fragments(struct inode *inode, u64 fragment,
                fs32_add(sb, &ucg->cg_frsum[fragsize - count], 1);
        for (i = oldcount; i < newcount; i++)
                ubh_clrbit (UCPI_UBH(ucpi), ucpi->c_freeoff, fragno + i);
-       ret = dquot_alloc_block(inode, count);
-       if (ret) {
-               *err = ret;
-               return 0;
-       }
 
        fs32_sub(sb, &ucg->cg_cs.cs_nffree, count);
        fs32_sub(sb, &UFS_SB(sb)->fs_cs(cgno).cs_nffree, count);
@@ -598,7 +587,6 @@ static u64 ufs_alloc_fragments(struct inode *inode, unsigned cgno,
        struct ufs_cylinder_group * ucg;
        unsigned oldcg, i, j, k, allocsize;
        u64 result;
-       int ret;
        
        UFSD("ENTER, ino %lu, cgno %u, goal %llu, count %u\n",
             inode->i_ino, cgno, (unsigned long long)goal, count);
@@ -667,7 +655,6 @@ cg_found:
                for (i = count; i < uspi->s_fpb; i++)
                        ubh_setbit (UCPI_UBH(ucpi), ucpi->c_freeoff, goal + i);
                i = uspi->s_fpb - count;
-               dquot_free_block(inode, i);
 
                fs32_add(sb, &ucg->cg_cs.cs_nffree, i);
                uspi->cs_total.cs_nffree += i;
@@ -679,11 +666,6 @@ cg_found:
        result = ufs_bitmap_search (sb, ucpi, goal, allocsize);
        if (result == INVBLOCK)
                return 0;
-       ret = dquot_alloc_block(inode, count);
-       if (ret) {
-               *err = ret;
-               return 0;
-       }
        for (i = 0; i < count; i++)
                ubh_clrbit (UCPI_UBH(ucpi), ucpi->c_freeoff, result + i);
        
@@ -718,7 +700,6 @@ static u64 ufs_alloccg_block(struct inode *inode,
        struct ufs_super_block_first * usb1;
        struct ufs_cylinder_group * ucg;
        u64 result, blkno;
-       int ret;
 
        UFSD("ENTER, goal %llu\n", (unsigned long long)goal);
 
@@ -752,11 +733,6 @@ gotit:
        ubh_clrblock (UCPI_UBH(ucpi), ucpi->c_freeoff, blkno);
        if ((UFS_SB(sb)->s_flags & UFS_CG_MASK) == UFS_CG_44BSD)
                ufs_clusteracct (sb, ucpi, blkno, -1);
-       ret = dquot_alloc_block(inode, uspi->s_fpb);
-       if (ret) {
-               *err = ret;
-               return INVBLOCK;
-       }
 
        fs32_sub(sb, &ucg->cg_cs.cs_nbfree, 1);
        uspi->cs_total.cs_nbfree--;
index 317a0d4..ec78475 100644 (file)
@@ -666,6 +666,6 @@ not_empty:
 const struct file_operations ufs_dir_operations = {
        .read           = generic_read_dir,
        .readdir        = ufs_readdir,
-       .fsync          = simple_fsync,
+       .fsync          = generic_file_fsync,
        .llseek         = generic_file_llseek,
 };
index a8962ce..33afa20 100644 (file)
@@ -24,7 +24,6 @@
  */
 
 #include <linux/fs.h>
-#include <linux/quotaops.h>
 
 #include "ufs_fs.h"
 #include "ufs.h"
@@ -41,7 +40,7 @@ const struct file_operations ufs_file_operations = {
        .write          = do_sync_write,
        .aio_write      = generic_file_aio_write,
        .mmap           = generic_file_mmap,
-       .open           = dquot_file_open,
-       .fsync          = simple_fsync,
+       .open           = generic_file_open,
+       .fsync          = generic_file_fsync,
        .splice_read    = generic_file_splice_read,
 };
index 3a959d5..594480e 100644 (file)
@@ -27,7 +27,6 @@
 #include <linux/time.h>
 #include <linux/stat.h>
 #include <linux/string.h>
-#include <linux/quotaops.h>
 #include <linux/buffer_head.h>
 #include <linux/sched.h>
 #include <linux/bitops.h>
@@ -95,9 +94,6 @@ void ufs_free_inode (struct inode * inode)
 
        is_directory = S_ISDIR(inode->i_mode);
 
-       dquot_free_inode(inode);
-       dquot_drop(inode);
-
        clear_inode (inode);
 
        if (ubh_isclr (UCPI_UBH(ucpi), ucpi->c_iusedoff, bit))
@@ -347,21 +343,12 @@ cg_found:
 
        unlock_super (sb);
 
-       dquot_initialize(inode);
-       err = dquot_alloc_inode(inode);
-       if (err) {
-               dquot_drop(inode);
-               goto fail_without_unlock;
-       }
-
        UFSD("allocating inode %lu\n", inode->i_ino);
        UFSD("EXIT\n");
        return inode;
 
 fail_remove_inode:
        unlock_super(sb);
-fail_without_unlock:
-       inode->i_flags |= S_NOQUOTA;
        inode->i_nlink = 0;
        iput(inode);
        UFSD("EXIT (FAILED): err %d\n", err);
index cffa756..73fe773 100644 (file)
@@ -37,7 +37,6 @@
 #include <linux/smp_lock.h>
 #include <linux/buffer_head.h>
 #include <linux/writeback.h>
-#include <linux/quotaops.h>
 
 #include "ufs_fs.h"
 #include "ufs.h"
@@ -910,9 +909,6 @@ void ufs_delete_inode (struct inode * inode)
 {
        loff_t old_i_size;
 
-       if (!is_bad_inode(inode))
-               dquot_initialize(inode);
-
        truncate_inode_pages(&inode->i_data, 0);
        if (is_bad_inode(inode))
                goto no_delete;
index eabc02e..b056f02 100644 (file)
@@ -30,7 +30,6 @@
 #include <linux/time.h>
 #include <linux/fs.h>
 #include <linux/smp_lock.h>
-#include <linux/quotaops.h>
 
 #include "ufs_fs.h"
 #include "ufs.h"
@@ -86,8 +85,6 @@ static int ufs_create (struct inode * dir, struct dentry * dentry, int mode,
 
        UFSD("BEGIN\n");
 
-       dquot_initialize(dir);
-
        inode = ufs_new_inode(dir, mode);
        err = PTR_ERR(inode);
 
@@ -112,8 +109,6 @@ static int ufs_mknod (struct inode * dir, struct dentry *dentry, int mode, dev_t
        if (!old_valid_dev(rdev))
                return -EINVAL;
 
-       dquot_initialize(dir);
-
        inode = ufs_new_inode(dir, mode);
        err = PTR_ERR(inode);
        if (!IS_ERR(inode)) {
@@ -138,8 +133,6 @@ static int ufs_symlink (struct inode * dir, struct dentry * dentry,
        if (l > sb->s_blocksize)
                goto out_notlocked;
 
-       dquot_initialize(dir);
-
        lock_kernel();
        inode = ufs_new_inode(dir, S_IFLNK | S_IRWXUGO);
        err = PTR_ERR(inode);
@@ -185,8 +178,6 @@ static int ufs_link (struct dentry * old_dentry, struct inode * dir,
                return -EMLINK;
        }
 
-       dquot_initialize(dir);
-
        inode->i_ctime = CURRENT_TIME_SEC;
        inode_inc_link_count(inode);
        atomic_inc(&inode->i_count);
@@ -204,8 +195,6 @@ static int ufs_mkdir(struct inode * dir, struct dentry * dentry, int mode)
        if (dir->i_nlink >= UFS_LINK_MAX)
                goto out;
 
-       dquot_initialize(dir);
-
        lock_kernel();
        inode_inc_link_count(dir);
 
@@ -250,8 +239,6 @@ static int ufs_unlink(struct inode *dir, struct dentry *dentry)
        struct page *page;
        int err = -ENOENT;
 
-       dquot_initialize(dir);
-
        de = ufs_find_entry(dir, &dentry->d_name, &page);
        if (!de)
                goto out;
@@ -296,9 +283,6 @@ static int ufs_rename(struct inode *old_dir, struct dentry *old_dentry,
        struct ufs_dir_entry *old_de;
        int err = -ENOENT;
 
-       dquot_initialize(old_dir);
-       dquot_initialize(new_dir);
-
        old_de = ufs_find_entry(old_dir, &old_dentry->d_name, &old_page);
        if (!old_de)
                goto out;
index ad9bc1e..3ec5a9e 100644 (file)
@@ -77,7 +77,6 @@
 
 #include <linux/errno.h>
 #include <linux/fs.h>
-#include <linux/quotaops.h>
 #include <linux/slab.h>
 #include <linux/time.h>
 #include <linux/stat.h>
@@ -1047,7 +1046,7 @@ magic_found:
         */
        sb->s_op = &ufs_super_ops;
        sb->s_export_op = &ufs_export_ops;
-       sb->dq_op = NULL; /***/
+
        sb->s_magic = fs32_to_cpu(sb, usb3->fs_magic);
 
        uspi->s_sblkno = fs32_to_cpu(sb, usb1->fs_sblkno);
@@ -1437,126 +1436,19 @@ static void destroy_inodecache(void)
        kmem_cache_destroy(ufs_inode_cachep);
 }
 
-static void ufs_clear_inode(struct inode *inode)
-{
-       dquot_drop(inode);
-}
-
-#ifdef CONFIG_QUOTA
-static ssize_t ufs_quota_read(struct super_block *, int, char *,size_t, loff_t);
-static ssize_t ufs_quota_write(struct super_block *, int, const char *, size_t, loff_t);
-#endif
-
 static const struct super_operations ufs_super_ops = {
        .alloc_inode    = ufs_alloc_inode,
        .destroy_inode  = ufs_destroy_inode,
        .write_inode    = ufs_write_inode,
        .delete_inode   = ufs_delete_inode,
-       .clear_inode    = ufs_clear_inode,
        .put_super      = ufs_put_super,
        .write_super    = ufs_write_super,
        .sync_fs        = ufs_sync_fs,
        .statfs         = ufs_statfs,
        .remount_fs     = ufs_remount,
        .show_options   = ufs_show_options,
-#ifdef CONFIG_QUOTA
-       .quota_read     = ufs_quota_read,
-       .quota_write    = ufs_quota_write,
-#endif
 };
 
-#ifdef CONFIG_QUOTA
-
-/* Read data from quotafile - avoid pagecache and such because we cannot afford
- * acquiring the locks... As quota files are never truncated and quota code
- * itself serializes the operations (and noone else should touch the files)
- * we don't have to be afraid of races */
-static ssize_t ufs_quota_read(struct super_block *sb, int type, char *data,
-                              size_t len, loff_t off)
-{
-       struct inode *inode = sb_dqopt(sb)->files[type];
-       sector_t blk = off >> sb->s_blocksize_bits;
-       int err = 0;
-       int offset = off & (sb->s_blocksize - 1);
-       int tocopy;
-       size_t toread;
-       struct buffer_head *bh;
-       loff_t i_size = i_size_read(inode);
-
-       if (off > i_size)
-               return 0;
-       if (off+len > i_size)
-               len = i_size-off;
-       toread = len;
-       while (toread > 0) {
-               tocopy = sb->s_blocksize - offset < toread ?
-                               sb->s_blocksize - offset : toread;
-
-               bh = ufs_bread(inode, blk, 0, &err);
-               if (err)
-                       return err;
-               if (!bh)        /* A hole? */
-                       memset(data, 0, tocopy);
-               else {
-                       memcpy(data, bh->b_data+offset, tocopy);
-                       brelse(bh);
-               }
-               offset = 0;
-               toread -= tocopy;
-               data += tocopy;
-               blk++;
-       }
-       return len;
-}
-
-/* Write to quotafile */
-static ssize_t ufs_quota_write(struct super_block *sb, int type,
-                               const char *data, size_t len, loff_t off)
-{
-       struct inode *inode = sb_dqopt(sb)->files[type];
-       sector_t blk = off >> sb->s_blocksize_bits;
-       int err = 0;
-       int offset = off & (sb->s_blocksize - 1);
-       int tocopy;
-       size_t towrite = len;
-       struct buffer_head *bh;
-
-       mutex_lock_nested(&inode->i_mutex, I_MUTEX_QUOTA);
-       while (towrite > 0) {
-               tocopy = sb->s_blocksize - offset < towrite ?
-                               sb->s_blocksize - offset : towrite;
-
-               bh = ufs_bread(inode, blk, 1, &err);
-               if (!bh)
-                       goto out;
-               lock_buffer(bh);
-               memcpy(bh->b_data+offset, data, tocopy);
-               flush_dcache_page(bh->b_page);
-               set_buffer_uptodate(bh);
-               mark_buffer_dirty(bh);
-               unlock_buffer(bh);
-               brelse(bh);
-               offset = 0;
-               towrite -= tocopy;
-               data += tocopy;
-               blk++;
-       }
-out:
-       if (len == towrite) {
-               mutex_unlock(&inode->i_mutex);
-               return err;
-       }
-       if (inode->i_size < off+len-towrite)
-               i_size_write(inode, off+len-towrite);
-       inode->i_version++;
-       inode->i_mtime = inode->i_ctime = CURRENT_TIME_SEC;
-       mark_inode_dirty(inode);
-       mutex_unlock(&inode->i_mutex);
-       return len - towrite;
-}
-
-#endif
-
 static int ufs_get_sb(struct file_system_type *fs_type,
        int flags, const char *dev_name, void *data, struct vfsmount *mnt)
 {
index f294c44..589e01a 100644 (file)
@@ -44,7 +44,6 @@
 #include <linux/buffer_head.h>
 #include <linux/blkdev.h>
 #include <linux/sched.h>
-#include <linux/quotaops.h>
 
 #include "ufs_fs.h"
 #include "ufs.h"
@@ -501,12 +500,10 @@ out:
        return err;
 }
 
-
 /*
- * We don't define our `inode->i_op->truncate', and call it here,
- * because of:
- * - there is no way to know old size
- * - there is no way inform user about error, if it happens in `truncate'
+ * TODO:
+ *     - truncate case should use proper ordering instead of using
+ *       simple_setsize
  */
 int ufs_setattr(struct dentry *dentry, struct iattr *attr)
 {
@@ -518,19 +515,10 @@ int ufs_setattr(struct dentry *dentry, struct iattr *attr)
        if (error)
                return error;
 
-       if (is_quota_modification(inode, attr))
-               dquot_initialize(inode);
-
-       if ((ia_valid & ATTR_UID && attr->ia_uid != inode->i_uid) ||
-           (ia_valid & ATTR_GID && attr->ia_gid != inode->i_gid)) {
-               error = dquot_transfer(inode, attr);
-               if (error)
-                       return error;
-       }
        if (ia_valid & ATTR_SIZE && attr->ia_size != inode->i_size) {
                loff_t old_i_size = inode->i_size;
 
-               error = vmtruncate(inode, attr->ia_size);
+               error = simple_setsize(inode, attr->ia_size);
                if (error)
                        return error;
                error = ufs_truncate(inode, old_i_size);
index d8fb1b5..257a56b 100644 (file)
@@ -100,10 +100,10 @@ xfs_iozero(
 STATIC int
 xfs_file_fsync(
        struct file             *file,
-       struct dentry           *dentry,
        int                     datasync)
 {
-       struct xfs_inode        *ip = XFS_I(dentry->d_inode);
+       struct inode            *inode = file->f_mapping->host;
+       struct xfs_inode        *ip = XFS_I(inode);
        struct xfs_trans        *tp;
        int                     error = 0;
        int                     log_flushed = 0;
@@ -140,8 +140,8 @@ xfs_file_fsync(
         * might gets cleared when the inode gets written out via the AIL
         * or xfs_iflush_cluster.
         */
-       if (((dentry->d_inode->i_state & I_DIRTY_DATASYNC) ||
-           ((dentry->d_inode->i_state & I_DIRTY_SYNC) && !datasync)) &&
+       if (((inode->i_state & I_DIRTY_DATASYNC) ||
+           ((inode->i_state & I_DIRTY_SYNC) && !datasync)) &&
            ip->i_update_core) {
                /*
                 * Kick off a transaction to log the inode core to get the
@@ -868,7 +868,7 @@ write_retry:
                        mutex_lock(&inode->i_mutex);
                xfs_ilock(ip, iolock);
 
-               error2 = -xfs_file_fsync(file, file->f_path.dentry,
+               error2 = -xfs_file_fsync(file,
                                         (file->f_flags & __O_SYNC) ? 0 : 1);
                if (!error)
                        error = error2;
index 7bf83dd..baacd98 100644 (file)
@@ -373,7 +373,7 @@ struct acpi_pci_root {
        struct acpi_pci_id id;
        struct pci_bus *bus;
        u16 segment;
-       u8 bus_nr;
+       struct resource secondary;      /* downstream bus range */
 
        u32 osc_support_set;    /* _OSC state of support bits */
        u32 osc_control_set;    /* _OSC state of control bits */
index 4f7b448..23d78b4 100644 (file)
@@ -104,8 +104,7 @@ int acpi_pci_bind_root(struct acpi_device *device);
 
 /* Arch-defined function to add a bus to the system */
 
-struct pci_bus *pci_acpi_scan_root(struct acpi_device *device, int domain,
-                                  int bus);
+struct pci_bus *pci_acpi_scan_root(struct acpi_pci_root *root);
 void pci_acpi_crs_quirks(void);
 
 /* --------------------------------------------------------------------------
diff --git a/include/acpi/acpi_hest.h b/include/acpi/acpi_hest.h
deleted file mode 100644 (file)
index 63194d0..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-#ifndef __ACPI_HEST_H
-#define __ACPI_HEST_H
-
-#include <linux/pci.h>
-
-#ifdef CONFIG_ACPI
-extern int acpi_hest_firmware_first_pci(struct pci_dev *pci);
-#else
-static inline int acpi_hest_firmware_first_pci(struct pci_dev *pci) { return 0; }
-#endif
-
-#endif
diff --git a/include/acpi/apei.h b/include/acpi/apei.h
new file mode 100644 (file)
index 0000000..b336502
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * apei.h - ACPI Platform Error Interface
+ */
+
+#ifndef ACPI_APEI_H
+#define ACPI_APEI_H
+
+#include <linux/acpi.h>
+#include <linux/cper.h>
+#include <asm/ioctls.h>
+
+#define APEI_ERST_INVALID_RECORD_ID    0xffffffffffffffffULL
+
+#define APEI_ERST_CLEAR_RECORD         _IOW('E', 1, u64)
+#define APEI_ERST_GET_RECORD_COUNT     _IOR('E', 2, u32)
+
+#ifdef __KERNEL__
+
+extern int hest_disable;
+extern int erst_disable;
+
+typedef int (*apei_hest_func_t)(struct acpi_hest_header *hest_hdr, void *data);
+int apei_hest_parse(apei_hest_func_t func, void *data);
+
+int erst_write(const struct cper_record_header *record);
+ssize_t erst_get_record_count(void);
+int erst_get_next_record_id(u64 *record_id);
+ssize_t erst_read(u64 record_id, struct cper_record_header *record,
+                 size_t buflen);
+ssize_t erst_read_next(struct cper_record_header *record, size_t buflen);
+int erst_clear(u64 record_id);
+
+#endif
+#endif
diff --git a/include/acpi/atomicio.h b/include/acpi/atomicio.h
new file mode 100644 (file)
index 0000000..8b9fb4b
--- /dev/null
@@ -0,0 +1,10 @@
+#ifndef ACPI_ATOMIC_IO_H
+#define ACPI_ATOMIC_IO_H
+
+int acpi_pre_map_gar(struct acpi_generic_address *reg);
+int acpi_post_unmap_gar(struct acpi_generic_address *reg);
+
+int acpi_atomic_read(u64 *val, struct acpi_generic_address *reg);
+int acpi_atomic_write(u64 val, struct acpi_generic_address *reg);
+
+#endif
diff --git a/include/acpi/hed.h b/include/acpi/hed.h
new file mode 100644 (file)
index 0000000..46e1249
--- /dev/null
@@ -0,0 +1,18 @@
+/*
+ * hed.h - ACPI Hardware Error Device
+ *
+ * Copyright (C) 2009, Intel Corp.
+ *     Author: Huang Ying <ying.huang@intel.com>
+ *
+ * This file is released under the GPLv2.
+ */
+
+#ifndef ACPI_HED_H
+#define ACPI_HED_H
+
+#include <linux/notifier.h>
+
+int register_acpi_hed_notifier(struct notifier_block *nb);
+void unregister_acpi_hed_notifier(struct notifier_block *nb);
+
+#endif
index 86825dd..da565a4 100644 (file)
@@ -52,17 +52,6 @@ struct acpi_power_register {
        u64 address;
 } __attribute__ ((packed));
 
-struct acpi_processor_cx_policy {
-       u32 count;
-       struct acpi_processor_cx *state;
-       struct {
-               u32 time;
-               u32 ticks;
-               u32 count;
-               u32 bm;
-       } threshold;
-};
-
 struct acpi_processor_cx {
        u8 valid;
        u8 type;
@@ -74,8 +63,6 @@ struct acpi_processor_cx {
        u32 power;
        u32 usage;
        u64 time;
-       struct acpi_processor_cx_policy promotion;
-       struct acpi_processor_cx_policy demotion;
        char desc[ACPI_CX_DESC_LEN];
 };
 
index cf7be3d..551793c 100644 (file)
@@ -1,12 +1,28 @@
 #ifndef __ACPI_VIDEO_H
 #define __ACPI_VIDEO_H
 
+#define ACPI_VIDEO_DISPLAY_CRT  1
+#define ACPI_VIDEO_DISPLAY_TV   2
+#define ACPI_VIDEO_DISPLAY_DVI  3
+#define ACPI_VIDEO_DISPLAY_LCD  4
+
+#define ACPI_VIDEO_DISPLAY_LEGACY_MONITOR 0x0100
+#define ACPI_VIDEO_DISPLAY_LEGACY_PANEL   0x0110
+#define ACPI_VIDEO_DISPLAY_LEGACY_TV      0x0200
+
 #if (defined CONFIG_ACPI_VIDEO || defined CONFIG_ACPI_VIDEO_MODULE)
 extern int acpi_video_register(void);
 extern void acpi_video_unregister(void);
+extern int acpi_video_get_edid(struct acpi_device *device, int type,
+                              int device_id, void **edid);
 #else
 static inline int acpi_video_register(void) { return 0; }
 static inline void acpi_video_unregister(void) { return; }
+static inline int acpi_video_get_edid(struct acpi_device *device, int type,
+                                     int device_id, void **edid)
+{
+       return -ENODEV;
+}
 #endif
 
 #endif
index 3da73f5..224a38c 100644 (file)
@@ -248,11 +248,12 @@ int acpi_check_region(resource_size_t start, resource_size_t n,
 int acpi_check_mem_region(resource_size_t start, resource_size_t n,
                      const char *name);
 
+int acpi_resources_are_enforced(void);
+
 #ifdef CONFIG_PM_SLEEP
 void __init acpi_no_s4_hw_signature(void);
 void __init acpi_old_suspend_ordering(void);
 void __init acpi_s4_no_nvs(void);
-void __init acpi_set_sci_en_on_resume(void);
 #endif /* CONFIG_PM_SLEEP */
 
 struct acpi_osc_context {
diff --git a/include/linux/amba/pl330.h b/include/linux/amba/pl330.h
new file mode 100644 (file)
index 0000000..cbee7de
--- /dev/null
@@ -0,0 +1,45 @@
+/* linux/include/linux/amba/pl330.h
+ *
+ * Copyright (C) 2010 Samsung Electronics Co. Ltd.
+ *     Jaswinder Singh <jassi.brar@samsung.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.
+ */
+
+#ifndef        __AMBA_PL330_H_
+#define        __AMBA_PL330_H_
+
+#include <asm/hardware/pl330.h>
+
+struct dma_pl330_peri {
+       /*
+        * Peri_Req i/f of the DMAC that is
+        * peripheral could be reached from.
+        */
+       u8 peri_id; /* {0, 31} */
+       enum pl330_reqtype rqtype;
+
+       /* For M->D and D->M Channels */
+       int burst_sz; /* in power of 2 */
+       dma_addr_t fifo_addr;
+};
+
+struct dma_pl330_platdata {
+       /*
+        * Number of valid peripherals connected to DMAC.
+        * This may be different from the value read from
+        * CR0, as the PL330 implementation might have 'holes'
+        * in the peri list or the peri could also be reached
+        * from another DMAC which the platform prefers.
+        */
+       u8 nr_valid_peri;
+       /* Array of valid peripherals */
+       struct dma_pl330_peri *peri;
+       /* Bytes to allocate for MC buffer */
+       unsigned mcbuf_sz;
+};
+
+#endif /* __AMBA_PL330_H_ */
index 6fb2720..daf8c48 100644 (file)
@@ -141,7 +141,6 @@ extern int bitmap_find_free_region(unsigned long *bitmap, int bits, int order);
 extern void bitmap_release_region(unsigned long *bitmap, int pos, int order);
 extern int bitmap_allocate_region(unsigned long *bitmap, int pos, int order);
 extern void bitmap_copy_le(void *dst, const unsigned long *src, int nbits);
-extern int bitmap_ord_to_pos(const unsigned long *bitmap, int n, int bits);
 
 #define BITMAP_LAST_WORD_MASK(nbits)                                   \
 (                                                                      \
index 16ed028..1b9ba19 100644 (file)
@@ -203,6 +203,9 @@ int block_write_full_page_endio(struct page *page, get_block_t *get_block,
 int block_read_full_page(struct page*, get_block_t*);
 int block_is_partially_uptodate(struct page *page, read_descriptor_t *desc,
                                unsigned long from);
+int block_write_begin_newtrunc(struct file *, struct address_space *,
+                               loff_t, unsigned, unsigned,
+                               struct page **, void **, get_block_t*);
 int block_write_begin(struct file *, struct address_space *,
                                loff_t, unsigned, unsigned,
                                struct page **, void **, get_block_t*);
@@ -214,6 +217,9 @@ int generic_write_end(struct file *, struct address_space *,
                                struct page *, void *);
 void page_zero_new_buffers(struct page *page, unsigned from, unsigned to);
 int block_prepare_write(struct page*, unsigned, unsigned, get_block_t*);
+int cont_write_begin_newtrunc(struct file *, struct address_space *, loff_t,
+                       unsigned, unsigned, struct page **, void **,
+                       get_block_t *, loff_t *);
 int cont_write_begin(struct file *, struct address_space *, loff_t,
                        unsigned, unsigned, struct page **, void **,
                        get_block_t *, loff_t *);
@@ -224,7 +230,10 @@ int block_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf,
 void block_sync_page(struct page *);
 sector_t generic_block_bmap(struct address_space *, sector_t, get_block_t *);
 int block_truncate_page(struct address_space *, loff_t, get_block_t *);
-int file_fsync(struct file *, struct dentry *, int);
+int file_fsync(struct file *, int);
+int nobh_write_begin_newtrunc(struct file *, struct address_space *,
+                               loff_t, unsigned, unsigned,
+                               struct page **, void **, get_block_t*);
 int nobh_write_begin(struct file *, struct address_space *,
                                loff_t, unsigned, unsigned,
                                struct page **, void **, get_block_t*);
index 4a6b604..51e3145 100644 (file)
@@ -83,6 +83,8 @@ extern unsigned long wait_for_completion_timeout(struct completion *x,
                                                   unsigned long timeout);
 extern unsigned long wait_for_completion_interruptible_timeout(
                        struct completion *x, unsigned long timeout);
+extern unsigned long wait_for_completion_killable_timeout(
+                       struct completion *x, unsigned long timeout);
 extern bool try_wait_for_completion(struct completion *x);
 extern bool completion_done(struct completion *x);
 
diff --git a/include/linux/cper.h b/include/linux/cper.h
new file mode 100644 (file)
index 0000000..4b38f90
--- /dev/null
@@ -0,0 +1,314 @@
+/*
+ * UEFI Common Platform Error Record
+ *
+ * Copyright (C) 2010, Intel Corp.
+ *     Author: Huang Ying <ying.huang@intel.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License version
+ * 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef LINUX_CPER_H
+#define LINUX_CPER_H
+
+#include <linux/uuid.h>
+
+/* CPER record signature and the size */
+#define CPER_SIG_RECORD                                "CPER"
+#define CPER_SIG_SIZE                          4
+/* Used in signature_end field in struct cper_record_header */
+#define CPER_SIG_END                           0xffffffff
+
+/*
+ * CPER record header revision, used in revision field in struct
+ * cper_record_header
+ */
+#define CPER_RECORD_REV                                0x0100
+
+/*
+ * Severity difinition for error_severity in struct cper_record_header
+ * and section_severity in struct cper_section_descriptor
+ */
+#define CPER_SER_RECOVERABLE                   0x0
+#define CPER_SER_FATAL                         0x1
+#define CPER_SER_CORRECTED                     0x2
+#define CPER_SER_INFORMATIONAL                 0x3
+
+/*
+ * Validation bits difinition for validation_bits in struct
+ * cper_record_header. If set, corresponding fields in struct
+ * cper_record_header contain valid information.
+ *
+ * corresponds platform_id
+ */
+#define CPER_VALID_PLATFORM_ID                 0x0001
+/* corresponds timestamp */
+#define CPER_VALID_TIMESTAMP                   0x0002
+/* corresponds partition_id */
+#define CPER_VALID_PARTITION_ID                        0x0004
+
+/*
+ * Notification type used to generate error record, used in
+ * notification_type in struct cper_record_header
+ *
+ * Corrected Machine Check
+ */
+#define CPER_NOTIFY_CMC                                                        \
+       UUID_LE(0x2DCE8BB1, 0xBDD7, 0x450e, 0xB9, 0xAD, 0x9C, 0xF4,     \
+               0xEB, 0xD4, 0xF8, 0x90)
+/* Corrected Platform Error */
+#define CPER_NOTIFY_CPE                                                        \
+       UUID_LE(0x4E292F96, 0xD843, 0x4a55, 0xA8, 0xC2, 0xD4, 0x81,     \
+               0xF2, 0x7E, 0xBE, 0xEE)
+/* Machine Check Exception */
+#define CPER_NOTIFY_MCE                                                        \
+       UUID_LE(0xE8F56FFE, 0x919C, 0x4cc5, 0xBA, 0x88, 0x65, 0xAB,     \
+               0xE1, 0x49, 0x13, 0xBB)
+/* PCI Express Error */
+#define CPER_NOTIFY_PCIE                                               \
+       UUID_LE(0xCF93C01F, 0x1A16, 0x4dfc, 0xB8, 0xBC, 0x9C, 0x4D,     \
+               0xAF, 0x67, 0xC1, 0x04)
+/* INIT Record (for IPF) */
+#define CPER_NOTIFY_INIT                                               \
+       UUID_LE(0xCC5263E8, 0x9308, 0x454a, 0x89, 0xD0, 0x34, 0x0B,     \
+               0xD3, 0x9B, 0xC9, 0x8E)
+/* Non-Maskable Interrupt */
+#define CPER_NOTIFY_NMI                                                        \
+       UUID_LE(0x5BAD89FF, 0xB7E6, 0x42c9, 0x81, 0x4A, 0xCF, 0x24,     \
+               0x85, 0xD6, 0xE9, 0x8A)
+/* BOOT Error Record */
+#define CPER_NOTIFY_BOOT                                               \
+       UUID_LE(0x3D61A466, 0xAB40, 0x409a, 0xA6, 0x98, 0xF3, 0x62,     \
+               0xD4, 0x64, 0xB3, 0x8F)
+/* DMA Remapping Error */
+#define CPER_NOTIFY_DMAR                                               \
+       UUID_LE(0x667DD791, 0xC6B3, 0x4c27, 0x8A, 0x6B, 0x0F, 0x8E,     \
+               0x72, 0x2D, 0xEB, 0x41)
+
+/*
+ * Flags bits definitions for flags in struct cper_record_header
+ * If set, the error has been recovered
+ */
+#define CPER_HW_ERROR_FLAGS_RECOVERED          0x1
+/* If set, the error is for previous boot */
+#define CPER_HW_ERROR_FLAGS_PREVERR            0x2
+/* If set, the error is injected for testing */
+#define CPER_HW_ERROR_FLAGS_SIMULATED          0x4
+
+/*
+ * CPER section header revision, used in revision field in struct
+ * cper_section_descriptor
+ */
+#define CPER_SEC_REV                           0x0100
+
+/*
+ * Validation bits difinition for validation_bits in struct
+ * cper_section_descriptor. If set, corresponding fields in struct
+ * cper_section_descriptor contain valid information.
+ *
+ * corresponds fru_id
+ */
+#define CPER_SEC_VALID_FRU_ID                  0x1
+/* corresponds fru_text */
+#define CPER_SEC_VALID_FRU_TEXT                        0x2
+
+/*
+ * Flags bits definitions for flags in struct cper_section_descriptor
+ *
+ * If set, the section is associated with the error condition
+ * directly, and should be focused on
+ */
+#define CPER_SEC_PRIMARY                       0x0001
+/*
+ * If set, the error was not contained within the processor or memory
+ * hierarchy and the error may have propagated to persistent storage
+ * or network
+ */
+#define CPER_SEC_CONTAINMENT_WARNING           0x0002
+/* If set, the component must be re-initialized or re-enabled prior to use */
+#define CPER_SEC_RESET                         0x0004
+/* If set, Linux may choose to discontinue use of the resource */
+#define CPER_SEC_ERROR_THRESHOLD_EXCEEDED      0x0008
+/*
+ * If set, resource could not be queried for error information due to
+ * conflicts with other system software or resources. Some fields of
+ * the section will be invalid
+ */
+#define CPER_SEC_RESOURCE_NOT_ACCESSIBLE       0x0010
+/*
+ * If set, action has been taken to ensure error containment (such as
+ * poisoning data), but the error has not been fully corrected and the
+ * data has not been consumed. Linux may choose to take further
+ * corrective action before the data is consumed
+ */
+#define CPER_SEC_LATENT_ERROR                  0x0020
+
+/*
+ * Section type definitions, used in section_type field in struct
+ * cper_section_descriptor
+ *
+ * Processor Generic
+ */
+#define CPER_SEC_PROC_GENERIC                                          \
+       UUID_LE(0x9876CCAD, 0x47B4, 0x4bdb, 0xB6, 0x5E, 0x16, 0xF1,     \
+               0x93, 0xC4, 0xF3, 0xDB)
+/* Processor Specific: X86/X86_64 */
+#define CPER_SEC_PROC_IA                                               \
+       UUID_LE(0xDC3EA0B0, 0xA144, 0x4797, 0xB9, 0x5B, 0x53, 0xFA,     \
+               0x24, 0x2B, 0x6E, 0x1D)
+/* Processor Specific: IA64 */
+#define CPER_SEC_PROC_IPF                                              \
+       UUID_LE(0xE429FAF1, 0x3CB7, 0x11D4, 0x0B, 0xCA, 0x07, 0x00,     \
+               0x80, 0xC7, 0x3C, 0x88, 0x81)
+/* Platform Memory */
+#define CPER_SEC_PLATFORM_MEM                                          \
+       UUID_LE(0xA5BC1114, 0x6F64, 0x4EDE, 0xB8, 0x63, 0x3E, 0x83,     \
+               0xED, 0x7C, 0x83, 0xB1)
+#define CPER_SEC_PCIE                                                  \
+       UUID_LE(0xD995E954, 0xBBC1, 0x430F, 0xAD, 0x91, 0xB4, 0x4D,     \
+               0xCB, 0x3C, 0x6F, 0x35)
+/* Firmware Error Record Reference */
+#define CPER_SEC_FW_ERR_REC_REF                                                \
+       UUID_LE(0x81212A96, 0x09ED, 0x4996, 0x94, 0x71, 0x8D, 0x72,     \
+               0x9C, 0x8E, 0x69, 0xED)
+/* PCI/PCI-X Bus */
+#define CPER_SEC_PCI_X_BUS                                             \
+       UUID_LE(0xC5753963, 0x3B84, 0x4095, 0xBF, 0x78, 0xED, 0xDA,     \
+               0xD3, 0xF9, 0xC9, 0xDD)
+/* PCI Component/Device */
+#define CPER_SEC_PCI_DEV                                               \
+       UUID_LE(0xEB5E4685, 0xCA66, 0x4769, 0xB6, 0xA2, 0x26, 0x06,     \
+               0x8B, 0x00, 0x13, 0x26)
+#define CPER_SEC_DMAR_GENERIC                                          \
+       UUID_LE(0x5B51FEF7, 0xC79D, 0x4434, 0x8F, 0x1B, 0xAA, 0x62,     \
+               0xDE, 0x3E, 0x2C, 0x64)
+/* Intel VT for Directed I/O specific DMAr */
+#define CPER_SEC_DMAR_VT                                               \
+       UUID_LE(0x71761D37, 0x32B2, 0x45cd, 0xA7, 0xD0, 0xB0, 0xFE,     \
+               0xDD, 0x93, 0xE8, 0xCF)
+/* IOMMU specific DMAr */
+#define CPER_SEC_DMAR_IOMMU                                            \
+       UUID_LE(0x036F84E1, 0x7F37, 0x428c, 0xA7, 0x9E, 0x57, 0x5F,     \
+               0xDF, 0xAA, 0x84, 0xEC)
+
+/*
+ * All tables and structs must be byte-packed to match CPER
+ * specification, since the tables are provided by the system BIOS
+ */
+#pragma pack(1)
+
+struct cper_record_header {
+       char    signature[CPER_SIG_SIZE];       /* must be CPER_SIG_RECORD */
+       __u16   revision;                       /* must be CPER_RECORD_REV */
+       __u32   signature_end;                  /* must be CPER_SIG_END */
+       __u16   section_count;
+       __u32   error_severity;
+       __u32   validation_bits;
+       __u32   record_length;
+       __u64   timestamp;
+       uuid_le platform_id;
+       uuid_le partition_id;
+       uuid_le creator_id;
+       uuid_le notification_type;
+       __u64   record_id;
+       __u32   flags;
+       __u64   persistence_information;
+       __u8    reserved[12];                   /* must be zero */
+};
+
+struct cper_section_descriptor {
+       __u32   section_offset;         /* Offset in bytes of the
+                                        *  section body from the base
+                                        *  of the record header */
+       __u32   section_length;
+       __u16   revision;               /* must be CPER_RECORD_REV */
+       __u8    validation_bits;
+       __u8    reserved;               /* must be zero */
+       __u32   flags;
+       uuid_le section_type;
+       uuid_le fru_id;
+       __u32   section_severity;
+       __u8    fru_text[20];
+};
+
+/* Generic Processor Error Section */
+struct cper_sec_proc_generic {
+       __u64   validation_bits;
+       __u8    proc_type;
+       __u8    proc_isa;
+       __u8    proc_error_type;
+       __u8    operation;
+       __u8    flags;
+       __u8    level;
+       __u16   reserved;
+       __u64   cpu_version;
+       char    cpu_brand[128];
+       __u64   proc_id;
+       __u64   target_addr;
+       __u64   requestor_id;
+       __u64   responder_id;
+       __u64   ip;
+};
+
+/* IA32/X64 Processor Error Section */
+struct cper_sec_proc_ia {
+       __u64   validation_bits;
+       __u8    lapic_id;
+       __u8    cpuid[48];
+};
+
+/* IA32/X64 Processor Error Infomation Structure */
+struct cper_ia_err_info {
+       uuid_le err_type;
+       __u64   validation_bits;
+       __u64   check_info;
+       __u64   target_id;
+       __u64   requestor_id;
+       __u64   responder_id;
+       __u64   ip;
+};
+
+/* IA32/X64 Processor Context Information Structure */
+struct cper_ia_proc_ctx {
+       __u16   reg_ctx_type;
+       __u16   reg_arr_size;
+       __u32   msr_addr;
+       __u64   mm_reg_addr;
+};
+
+/* Memory Error Section */
+struct cper_sec_mem_err {
+       __u64   validation_bits;
+       __u64   error_status;
+       __u64   physical_addr;
+       __u64   physical_addr_mask;
+       __u16   node;
+       __u16   card;
+       __u16   module;
+       __u16   bank;
+       __u16   device;
+       __u16   row;
+       __u16   column;
+       __u16   bit_pos;
+       __u64   requestor_id;
+       __u64   responder_id;
+       __u64   target_id;
+       __u8    error_type;
+};
+
+/* Reset to default packing */
+#pragma pack()
+
+u64 cper_next_record_id(void);
+
+#endif
index dcf77fa..55215cc 100644 (file)
@@ -125,6 +125,7 @@ struct cpuidle_driver {
 #ifdef CONFIG_CPU_IDLE
 
 extern int cpuidle_register_driver(struct cpuidle_driver *drv);
+struct cpuidle_driver *cpuidle_get_driver(void);
 extern void cpuidle_unregister_driver(struct cpuidle_driver *drv);
 extern int cpuidle_register_device(struct cpuidle_device *dev);
 extern void cpuidle_unregister_device(struct cpuidle_device *dev);
@@ -137,16 +138,17 @@ extern void cpuidle_disable_device(struct cpuidle_device *dev);
 #else
 
 static inline int cpuidle_register_driver(struct cpuidle_driver *drv)
-{return 0;}
+{return -ENODEV; }
+static inline struct cpuidle_driver *cpuidle_get_driver(void) {return NULL; }
 static inline void cpuidle_unregister_driver(struct cpuidle_driver *drv) { }
 static inline int cpuidle_register_device(struct cpuidle_device *dev)
-{return 0;}
+{return -ENODEV; }
 static inline void cpuidle_unregister_device(struct cpuidle_device *dev) { }
 
 static inline void cpuidle_pause_and_lock(void) { }
 static inline void cpuidle_resume_and_unlock(void) { }
 static inline int cpuidle_enable_device(struct cpuidle_device *dev)
-{return 0;}
+{return -ENODEV; }
 static inline void cpuidle_disable_device(struct cpuidle_device *dev) { }
 
 #endif
index fc1b930..e7d9b20 100644 (file)
@@ -63,6 +63,8 @@ struct dentry *debugfs_create_x16(const char *name, mode_t mode,
                                  struct dentry *parent, u16 *value);
 struct dentry *debugfs_create_x32(const char *name, mode_t mode,
                                  struct dentry *parent, u32 *value);
+struct dentry *debugfs_create_x64(const char *name, mode_t mode,
+                                 struct dentry *parent, u64 *value);
 struct dentry *debugfs_create_size_t(const char *name, mode_t mode,
                                     struct dentry *parent, size_t *value);
 struct dentry *debugfs_create_bool(const char *name, mode_t mode,
index 5f494b4..7fc62d4 100644 (file)
@@ -868,7 +868,7 @@ extern int ext3_htree_store_dirent(struct file *dir_file, __u32 hash,
 extern void ext3_htree_free_dir_info(struct dir_private_info *p);
 
 /* fsync.c */
-extern int ext3_sync_file (struct file *, struct dentry *, int);
+extern int ext3_sync_file(struct file *, int);
 
 /* hash.c */
 extern int ext3fs_dirhash(const char *name, int len, struct
index f3793eb..907ace3 100644 (file)
@@ -4,8 +4,6 @@
 #include <linux/types.h>
 #include <linux/i2c.h>
 
-struct dentry;
-
 /* Definitions of frame buffers                                                */
 
 #define FB_MAX                 32      /* sufficient for now */
@@ -1017,8 +1015,7 @@ extern void fb_deferred_io_open(struct fb_info *info,
                                struct inode *inode,
                                struct file *file);
 extern void fb_deferred_io_cleanup(struct fb_info *info);
-extern int fb_deferred_io_fsync(struct file *file, struct dentry *dentry,
-                               int datasync);
+extern int fb_deferred_io_fsync(struct file *file, int datasync);
 
 static inline bool fb_be_math(struct fb_info *info)
 {
index 5555508..b1e1297 100644 (file)
@@ -11,7 +11,6 @@
 
 struct file;
 
-extern void __fput(struct file *);
 extern void fput(struct file *);
 extern void drop_file_write_access(struct file *file);
 
index 85e823a..3428393 100644 (file)
@@ -954,6 +954,7 @@ extern spinlock_t files_lock;
 #define file_list_unlock() spin_unlock(&files_lock);
 
 #define get_file(x)    atomic_long_inc(&(x)->f_count)
+#define fput_atomic(x) atomic_long_add_unless(&(x)->f_count, -1, 1)
 #define file_count(x)  atomic_long_read(&(x)->f_count)
 
 #ifdef CONFIG_DEBUG_WRITECOUNT
@@ -1497,7 +1498,7 @@ struct file_operations {
        int (*open) (struct inode *, struct file *);
        int (*flush) (struct file *, fl_owner_t id);
        int (*release) (struct inode *, struct file *);
-       int (*fsync) (struct file *, struct dentry *, int datasync);
+       int (*fsync) (struct file *, int datasync);
        int (*aio_fsync) (struct kiocb *, int datasync);
        int (*fasync) (int, struct file *, int);
        int (*lock) (struct file *, int, struct file_lock *);
@@ -2212,7 +2213,7 @@ extern int generic_segment_checks(const struct iovec *iov,
 /* fs/block_dev.c */
 extern ssize_t blkdev_aio_write(struct kiocb *iocb, const struct iovec *iov,
                                unsigned long nr_segs, loff_t pos);
-extern int blkdev_fsync(struct file *filp, struct dentry *dentry, int datasync);
+extern int blkdev_fsync(struct file *filp, int datasync);
 
 /* fs/splice.c */
 extern ssize_t generic_file_splice_read(struct file *, loff_t *,
@@ -2256,6 +2257,10 @@ typedef void (dio_submit_t)(int rw, struct bio *bio, struct inode *inode,
                            loff_t file_offset);
 void dio_end_io(struct bio *bio, int error);
 
+ssize_t __blockdev_direct_IO_newtrunc(int rw, struct kiocb *iocb, struct inode *inode,
+       struct block_device *bdev, const struct iovec *iov, loff_t offset,
+       unsigned long nr_segs, get_block_t get_block, dio_iodone_t end_io,
+       dio_submit_t submit_io, int lock_type);
 ssize_t __blockdev_direct_IO(int rw, struct kiocb *iocb, struct inode *inode,
        struct block_device *bdev, const struct iovec *iov, loff_t offset,
        unsigned long nr_segs, get_block_t get_block, dio_iodone_t end_io,
@@ -2269,6 +2274,24 @@ enum {
        DIO_SKIP_HOLES  = 0x02,
 };
 
+static inline ssize_t blockdev_direct_IO_newtrunc(int rw, struct kiocb *iocb,
+       struct inode *inode, struct block_device *bdev, const struct iovec *iov,
+       loff_t offset, unsigned long nr_segs, get_block_t get_block,
+       dio_iodone_t end_io)
+{
+       return __blockdev_direct_IO_newtrunc(rw, iocb, inode, bdev, iov, offset,
+                                   nr_segs, get_block, end_io, NULL,
+                                   DIO_LOCKING | DIO_SKIP_HOLES);
+}
+
+static inline ssize_t blockdev_direct_IO_no_locking_newtrunc(int rw, struct kiocb *iocb,
+       struct inode *inode, struct block_device *bdev, const struct iovec *iov,
+       loff_t offset, unsigned long nr_segs, get_block_t get_block,
+       dio_iodone_t end_io)
+{
+       return __blockdev_direct_IO_newtrunc(rw, iocb, inode, bdev, iov, offset,
+                               nr_segs, get_block, end_io, NULL, 0);
+}
 static inline ssize_t blockdev_direct_IO(int rw, struct kiocb *iocb,
        struct inode *inode, struct block_device *bdev, const struct iovec *iov,
        loff_t offset, unsigned long nr_segs, get_block_t get_block,
@@ -2341,13 +2364,15 @@ extern int dcache_dir_open(struct inode *, struct file *);
 extern int dcache_dir_close(struct inode *, struct file *);
 extern loff_t dcache_dir_lseek(struct file *, loff_t, int);
 extern int dcache_readdir(struct file *, void *, filldir_t);
+extern int simple_setattr(struct dentry *, struct iattr *);
 extern int simple_getattr(struct vfsmount *, struct dentry *, struct kstat *);
 extern int simple_statfs(struct dentry *, struct kstatfs *);
 extern int simple_link(struct dentry *, struct inode *, struct dentry *);
 extern int simple_unlink(struct inode *, struct dentry *);
 extern int simple_rmdir(struct inode *, struct dentry *);
 extern int simple_rename(struct inode *, struct dentry *, struct inode *, struct dentry *);
-extern int simple_sync_file(struct file *, struct dentry *, int);
+extern int simple_setsize(struct inode *, loff_t);
+extern int noop_fsync(struct file *, int);
 extern int simple_empty(struct dentry *);
 extern int simple_readpage(struct file *file, struct page *page);
 extern int simple_write_begin(struct file *file, struct address_space *mapping,
@@ -2372,7 +2397,7 @@ extern ssize_t simple_read_from_buffer(void __user *to, size_t count,
 extern ssize_t simple_write_to_buffer(void *to, size_t available, loff_t *ppos,
                const void __user *from, size_t count);
 
-extern int simple_fsync(struct file *, struct dentry *, int);
+extern int generic_file_fsync(struct file *, int);
 
 #ifdef CONFIG_MIGRATION
 extern int buffer_migrate_page(struct address_space *,
@@ -2383,7 +2408,8 @@ extern int buffer_migrate_page(struct address_space *,
 
 extern int inode_change_ok(const struct inode *, struct iattr *);
 extern int inode_newsize_ok(const struct inode *, loff_t offset);
-extern int __must_check inode_setattr(struct inode *, struct iattr *);
+extern int __must_check inode_setattr(struct inode *, const struct iattr *);
+extern void generic_setattr(struct inode *inode, const struct iattr *attr);
 
 extern void file_update_time(struct file *file);
 
index c082f22..3167f2d 100644 (file)
@@ -73,18 +73,25 @@ struct trace_iterator {
 };
 
 
+struct trace_event;
+
 typedef enum print_line_t (*trace_print_func)(struct trace_iterator *iter,
-                                             int flags);
-struct trace_event {
-       struct hlist_node       node;
-       struct list_head        list;
-       int                     type;
+                                     int flags, struct trace_event *event);
+
+struct trace_event_functions {
        trace_print_func        trace;
        trace_print_func        raw;
        trace_print_func        hex;
        trace_print_func        binary;
 };
 
+struct trace_event {
+       struct hlist_node               node;
+       struct list_head                list;
+       int                             type;
+       struct trace_event_functions    *funcs;
+};
+
 extern int register_ftrace_event(struct trace_event *event);
 extern int unregister_ftrace_event(struct trace_event *event);
 
@@ -116,28 +123,70 @@ void tracing_record_cmdline(struct task_struct *tsk);
 
 struct event_filter;
 
+enum trace_reg {
+       TRACE_REG_REGISTER,
+       TRACE_REG_UNREGISTER,
+       TRACE_REG_PERF_REGISTER,
+       TRACE_REG_PERF_UNREGISTER,
+};
+
+struct ftrace_event_call;
+
+struct ftrace_event_class {
+       char                    *system;
+       void                    *probe;
+#ifdef CONFIG_PERF_EVENTS
+       void                    *perf_probe;
+#endif
+       int                     (*reg)(struct ftrace_event_call *event,
+                                      enum trace_reg type);
+       int                     (*define_fields)(struct ftrace_event_call *);
+       struct list_head        *(*get_fields)(struct ftrace_event_call *);
+       struct list_head        fields;
+       int                     (*raw_init)(struct ftrace_event_call *);
+};
+
+enum {
+       TRACE_EVENT_FL_ENABLED_BIT,
+       TRACE_EVENT_FL_FILTERED_BIT,
+};
+
+enum {
+       TRACE_EVENT_FL_ENABLED  = (1 << TRACE_EVENT_FL_ENABLED_BIT),
+       TRACE_EVENT_FL_FILTERED = (1 << TRACE_EVENT_FL_FILTERED_BIT),
+};
+
 struct ftrace_event_call {
        struct list_head        list;
+       struct ftrace_event_class *class;
        char                    *name;
-       char                    *system;
        struct dentry           *dir;
-       struct trace_event      *event;
-       int                     enabled;
-       int                     (*regfunc)(struct ftrace_event_call *);
-       void                    (*unregfunc)(struct ftrace_event_call *);
-       int                     id;
+       struct trace_event      event;
        const char              *print_fmt;
-       int                     (*raw_init)(struct ftrace_event_call *);
-       int                     (*define_fields)(struct ftrace_event_call *);
-       struct list_head        fields;
-       int                     filter_active;
        struct event_filter     *filter;
        void                    *mod;
        void                    *data;
 
+       /*
+        * 32 bit flags:
+        *   bit 1:             enabled
+        *   bit 2:             filter_active
+        *
+        * Changes to flags must hold the event_mutex.
+        *
+        * Note: Reads of flags do not hold the event_mutex since
+        * they occur in critical sections. But the way flags
+        * is currently used, these changes do no affect the code
+        * except that when a change is made, it may have a slight
+        * delay in propagating the changes to other CPUs due to
+        * caching and such.
+        */
+       unsigned int            flags;
+
+#ifdef CONFIG_PERF_EVENTS
        int                     perf_refcount;
-       int                     (*perf_event_enable)(struct ftrace_event_call *);
-       void                    (*perf_event_disable)(struct ftrace_event_call *);
+       struct hlist_head       *perf_events;
+#endif
 };
 
 #define PERF_MAX_TRACE_SIZE    2048
@@ -194,24 +243,22 @@ struct perf_event;
 
 DECLARE_PER_CPU(struct pt_regs, perf_trace_regs);
 
-extern int perf_trace_enable(int event_id);
-extern void perf_trace_disable(int event_id);
-extern int ftrace_profile_set_filter(struct perf_event *event, int event_id,
+extern int  perf_trace_init(struct perf_event *event);
+extern void perf_trace_destroy(struct perf_event *event);
+extern int  perf_trace_enable(struct perf_event *event);
+extern void perf_trace_disable(struct perf_event *event);
+extern int  ftrace_profile_set_filter(struct perf_event *event, int event_id,
                                     char *filter_str);
 extern void ftrace_profile_free_filter(struct perf_event *event);
-extern void *
-perf_trace_buf_prepare(int size, unsigned short type, int *rctxp,
-                        unsigned long *irq_flags);
+extern void *perf_trace_buf_prepare(int size, unsigned short type,
+                                   struct pt_regs *regs, int *rctxp);
 
 static inline void
 perf_trace_buf_submit(void *raw_data, int size, int rctx, u64 addr,
-                      u64 count, unsigned long irq_flags, struct pt_regs *regs)
+                      u64 count, struct pt_regs *regs, void *head)
 {
-       struct trace_entry *entry = raw_data;
-
-       perf_tp_event(entry->type, addr, count, raw_data, size, regs);
+       perf_tp_event(addr, count, raw_data, size, regs, head);
        perf_swevent_put_recursion_context(rctx);
-       local_irq_restore(irq_flags);
 }
 #endif
 
diff --git a/include/linux/i2c/adp8860.h b/include/linux/i2c/adp8860.h
new file mode 100644 (file)
index 0000000..0b4d398
--- /dev/null
@@ -0,0 +1,154 @@
+/*
+ * Definitions and platform data for Analog Devices
+ * Backlight drivers ADP8860
+ *
+ * Copyright 2009-2010 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#ifndef __LINUX_I2C_ADP8860_H
+#define __LINUX_I2C_ADP8860_H
+
+#include <linux/leds.h>
+#include <linux/types.h>
+
+#define ID_ADP8860             8860
+
+#define ADP8860_MAX_BRIGHTNESS 0x7F
+#define FLAG_OFFT_SHIFT 8
+
+/*
+ * LEDs subdevice platform data
+ */
+
+#define ADP8860_LED_DIS_BLINK  (0 << FLAG_OFFT_SHIFT)
+#define ADP8860_LED_OFFT_600ms (1 << FLAG_OFFT_SHIFT)
+#define ADP8860_LED_OFFT_1200ms        (2 << FLAG_OFFT_SHIFT)
+#define ADP8860_LED_OFFT_1800ms        (3 << FLAG_OFFT_SHIFT)
+
+#define ADP8860_LED_ONT_200ms  0
+#define ADP8860_LED_ONT_600ms  1
+#define ADP8860_LED_ONT_800ms  2
+#define ADP8860_LED_ONT_1200ms 3
+
+#define ADP8860_LED_D7         (7)
+#define ADP8860_LED_D6         (6)
+#define ADP8860_LED_D5         (5)
+#define ADP8860_LED_D4         (4)
+#define ADP8860_LED_D3         (3)
+#define ADP8860_LED_D2         (2)
+#define ADP8860_LED_D1         (1)
+
+/*
+ * Backlight subdevice platform data
+ */
+
+#define ADP8860_BL_D7          (1 << 6)
+#define ADP8860_BL_D6          (1 << 5)
+#define ADP8860_BL_D5          (1 << 4)
+#define ADP8860_BL_D4          (1 << 3)
+#define ADP8860_BL_D3          (1 << 2)
+#define ADP8860_BL_D2          (1 << 1)
+#define ADP8860_BL_D1          (1 << 0)
+
+#define ADP8860_FADE_T_DIS     0       /* Fade Timer Disabled */
+#define ADP8860_FADE_T_300ms   1       /* 0.3 Sec */
+#define ADP8860_FADE_T_600ms   2
+#define ADP8860_FADE_T_900ms   3
+#define ADP8860_FADE_T_1200ms  4
+#define ADP8860_FADE_T_1500ms  5
+#define ADP8860_FADE_T_1800ms  6
+#define ADP8860_FADE_T_2100ms  7
+#define ADP8860_FADE_T_2400ms  8
+#define ADP8860_FADE_T_2700ms  9
+#define ADP8860_FADE_T_3000ms  10
+#define ADP8860_FADE_T_3500ms  11
+#define ADP8860_FADE_T_4000ms  12
+#define ADP8860_FADE_T_4500ms  13
+#define ADP8860_FADE_T_5000ms  14
+#define ADP8860_FADE_T_5500ms  15      /* 5.5 Sec */
+
+#define ADP8860_FADE_LAW_LINEAR        0
+#define ADP8860_FADE_LAW_SQUARE        1
+#define ADP8860_FADE_LAW_CUBIC1        2
+#define ADP8860_FADE_LAW_CUBIC2        3
+
+#define ADP8860_BL_AMBL_FILT_80ms      0       /* Light sensor filter time */
+#define ADP8860_BL_AMBL_FILT_160ms     1
+#define ADP8860_BL_AMBL_FILT_320ms     2
+#define ADP8860_BL_AMBL_FILT_640ms     3
+#define ADP8860_BL_AMBL_FILT_1280ms    4
+#define ADP8860_BL_AMBL_FILT_2560ms    5
+#define ADP8860_BL_AMBL_FILT_5120ms    6
+#define ADP8860_BL_AMBL_FILT_10240ms   7       /* 10.24 sec */
+
+/*
+ * Blacklight current 0..30mA
+ */
+#define ADP8860_BL_CUR_mA(I)           ((I * 127) / 30)
+
+/*
+ * L2 comparator current 0..1106uA
+ */
+#define ADP8860_L2_COMP_CURR_uA(I)     ((I * 255) / 1106)
+
+/*
+ * L3 comparator current 0..138uA
+ */
+#define ADP8860_L3_COMP_CURR_uA(I)     ((I * 255) / 138)
+
+struct adp8860_backlight_platform_data {
+       u8 bl_led_assign;       /* 1 = Backlight 0 = Individual LED */
+
+       u8 bl_fade_in;          /* Backlight Fade-In Timer */
+       u8 bl_fade_out;         /* Backlight Fade-Out Timer */
+       u8 bl_fade_law;         /* fade-on/fade-off transfer characteristic */
+
+       u8 en_ambl_sens;        /* 1 = enable ambient light sensor */
+       u8 abml_filt;           /* Light sensor filter time */
+
+       u8 l1_daylight_max;     /* use BL_CUR_mA(I) 0 <= I <= 30 mA */
+       u8 l1_daylight_dim;     /* typ = 0, use BL_CUR_mA(I) 0 <= I <= 30 mA */
+       u8 l2_office_max;       /* use BL_CUR_mA(I) 0 <= I <= 30 mA */
+       u8 l2_office_dim;       /* typ = 0, use BL_CUR_mA(I) 0 <= I <= 30 mA */
+       u8 l3_dark_max;         /* use BL_CUR_mA(I) 0 <= I <= 30 mA */
+       u8 l3_dark_dim;         /* typ = 0, use BL_CUR_mA(I) 0 <= I <= 30 mA */
+
+       u8 l2_trip;             /* use L2_COMP_CURR_uA(I) 0 <= I <= 1106 uA */
+       u8 l2_hyst;             /* use L2_COMP_CURR_uA(I) 0 <= I <= 1106 uA */
+       u8 l3_trip;             /* use L3_COMP_CURR_uA(I) 0 <= I <= 551 uA */
+       u8 l3_hyst;             /* use L3_COMP_CURR_uA(I) 0 <= I <= 551 uA */
+
+       /**
+        * Independent Current Sinks / LEDS
+        * Sinks not assigned to the Backlight can be exposed to
+        * user space using the LEDS CLASS interface
+        */
+
+       int num_leds;
+       struct led_info *leds;
+       u8 led_fade_in;         /* LED Fade-In Timer */
+       u8 led_fade_out;        /* LED Fade-Out Timer */
+       u8 led_fade_law;        /* fade-on/fade-off transfer characteristic */
+       u8 led_on_time;
+
+       /**
+        * Gain down disable. Setting this option does not allow the
+        * charge pump to switch to lower gains. NOT AVAILABLE on ADP8860
+        * 1 = the charge pump doesn't switch down in gain until all LEDs are 0.
+        *  The charge pump switches up in gain as needed. This feature is
+        *  useful if the ADP8863 charge pump is used to drive an external load.
+        *  This feature must be used when utilizing small fly capacitors
+        *  (0402 or smaller).
+        * 0 = the charge pump automatically switches up and down in gain.
+        *  This provides optimal efficiency, but is not suitable for driving
+        *  loads that are not connected through the ADP8863 diode drivers.
+        *  Additionally, the charge pump fly capacitors should be low ESR
+        * and sized 0603 or greater.
+        */
+
+       u8 gdwn_dis;
+};
+
+#endif /* __LINUX_I2C_ADP8860_H */
diff --git a/include/linux/input/tps6507x-ts.h b/include/linux/input/tps6507x-ts.h
new file mode 100644 (file)
index 0000000..ab14403
--- /dev/null
@@ -0,0 +1,24 @@
+/* linux/i2c/tps6507x-ts.h
+ *
+ * Functions to access TPS65070 touch screen chip.
+ *
+ * Copyright (c) 2009 RidgeRun (todd.fischer@ridgerun.com)
+ *
+ *
+ *  For licencing details see kernel-base/COPYING
+ */
+
+#ifndef __LINUX_I2C_TPS6507X_TS_H
+#define __LINUX_I2C_TPS6507X_TS_H
+
+/* Board specific touch screen initial values */
+struct touchscreen_init_data {
+       int     poll_period;    /* ms */
+       int     vref;           /* non-zero to leave vref on */
+       __u16   min_pressure;   /* min reading to be treated as a touch */
+       __u16   vendor;
+       __u16   product;
+       __u16   version;
+};
+
+#endif /*  __LINUX_I2C_TPS6507X_TS_H */
index c67feca..8877123 100644 (file)
@@ -69,6 +69,29 @@ struct lcd_device {
        struct device dev;
 };
 
+struct lcd_platform_data {
+       /* reset lcd panel device. */
+       int (*reset)(struct lcd_device *ld);
+       /* on or off to lcd panel. if 'enable' is 0 then
+          lcd power off and 1, lcd power on. */
+       int (*power_on)(struct lcd_device *ld, int enable);
+
+       /* it indicates whether lcd panel was enabled
+          from bootloader or not. */
+       int lcd_enabled;
+       /* it means delay for stable time when it becomes low to high
+          or high to low that is dependent on whether reset gpio is
+          low active or high active. */
+       unsigned int reset_delay;
+       /* stable time needing to become lcd power on. */
+       unsigned int power_on_delay;
+       /* stable time needing to become lcd power off. */
+       unsigned int power_off_delay;
+
+       /* it could be used for any purpose. */
+       void *pdata;
+};
+
 static inline void lcd_set_power(struct lcd_device *ld, int power)
 {
        mutex_lock(&ld->update_lock);
index d8bf966..ba6986a 100644 (file)
@@ -149,14 +149,18 @@ struct gpio_led {
        unsigned        default_state : 2;
        /* default_state should be one of LEDS_GPIO_DEFSTATE_(ON|OFF|KEEP) */
 };
-#define LEDS_GPIO_DEFSTATE_OFF 0
-#define LEDS_GPIO_DEFSTATE_ON  1
-#define LEDS_GPIO_DEFSTATE_KEEP        2
+#define LEDS_GPIO_DEFSTATE_OFF         0
+#define LEDS_GPIO_DEFSTATE_ON          1
+#define LEDS_GPIO_DEFSTATE_KEEP                2
 
 struct gpio_led_platform_data {
        int             num_leds;
        struct gpio_led *leds;
-       int             (*gpio_blink_set)(unsigned gpio,
+
+#define GPIO_LED_NO_BLINK_LOW  0       /* No blink GPIO state low */
+#define GPIO_LED_NO_BLINK_HIGH 1       /* No blink GPIO state high */
+#define GPIO_LED_BLINK         2       /* Plase, blink */
+       int             (*gpio_blink_set)(unsigned gpio, int state,
                                        unsigned long *delay_on,
                                        unsigned long *delay_off);
 };
index ee84e7e..3bad270 100644 (file)
@@ -386,6 +386,7 @@ enum {
        ATA_HORKAGE_1_5_GBPS    = (1 << 13),    /* force 1.5 Gbps */
        ATA_HORKAGE_NOSETXFER   = (1 << 14),    /* skip SETXFER, SATA only */
        ATA_HORKAGE_BROKEN_FPDMA_AA     = (1 << 15),    /* skip AA */
+       ATA_HORKAGE_DUMP_ID     = (1 << 16),    /* dump IDENTIFY data */
 
         /* DMA mask for user DMA control: User visible values; DO NOT
            renumber */
@@ -513,7 +514,9 @@ struct ata_ioports {
        void __iomem            *command_addr;
        void __iomem            *altstatus_addr;
        void __iomem            *ctl_addr;
+#ifdef CONFIG_ATA_BMDMA
        void __iomem            *bmdma_addr;
+#endif /* CONFIG_ATA_BMDMA */
        void __iomem            *scr_addr;
 };
 #endif /* CONFIG_ATA_SFF */
@@ -721,8 +724,10 @@ struct ata_port {
        u8                      ctl;    /* cache of ATA control register */
        u8                      last_ctl;       /* Cache last written value */
        struct delayed_work     sff_pio_task;
+#ifdef CONFIG_ATA_BMDMA
        struct ata_bmdma_prd    *bmdma_prd;     /* BMDMA SG list */
        dma_addr_t              bmdma_prd_dma;  /* and its DMA mapping */
+#endif /* CONFIG_ATA_BMDMA */
 #endif /* CONFIG_ATA_SFF */
 
        unsigned int            pio_mask;
@@ -856,10 +861,12 @@ struct ata_port_operations {
        void (*sff_irq_clear)(struct ata_port *);
        void (*sff_drain_fifo)(struct ata_queued_cmd *qc);
 
+#ifdef CONFIG_ATA_BMDMA
        void (*bmdma_setup)(struct ata_queued_cmd *qc);
        void (*bmdma_start)(struct ata_queued_cmd *qc);
        void (*bmdma_stop)(struct ata_queued_cmd *qc);
        u8   (*bmdma_status)(struct ata_port *ap);
+#endif /* CONFIG_ATA_BMDMA */
 #endif /* CONFIG_ATA_SFF */
 
        ssize_t (*em_show)(struct ata_port *ap, char *buf);
@@ -1555,7 +1562,6 @@ extern void sata_pmp_error_handler(struct ata_port *ap);
 #ifdef CONFIG_ATA_SFF
 
 extern const struct ata_port_operations ata_sff_port_ops;
-extern const struct ata_port_operations ata_bmdma_port_ops;
 extern const struct ata_port_operations ata_bmdma32_port_ops;
 
 /* PIO only, sg_tablesize and dma_boundary limits can be removed */
@@ -1564,11 +1570,6 @@ extern const struct ata_port_operations ata_bmdma32_port_ops;
        .sg_tablesize           = LIBATA_MAX_PRD,               \
        .dma_boundary           = ATA_DMA_BOUNDARY
 
-#define ATA_BMDMA_SHT(drv_name)                                        \
-       ATA_BASE_SHT(drv_name),                                 \
-       .sg_tablesize           = LIBATA_MAX_PRD,               \
-       .dma_boundary           = ATA_DMA_BOUNDARY
-
 extern void ata_sff_dev_select(struct ata_port *ap, unsigned int device);
 extern u8 ata_sff_check_status(struct ata_port *ap);
 extern void ata_sff_pause(struct ata_port *ap);
@@ -1593,7 +1594,7 @@ extern int ata_sff_hsm_move(struct ata_port *ap, struct ata_queued_cmd *qc,
 extern void ata_sff_queue_pio_task(struct ata_port *ap, unsigned long delay);
 extern unsigned int ata_sff_qc_issue(struct ata_queued_cmd *qc);
 extern bool ata_sff_qc_fill_rtf(struct ata_queued_cmd *qc);
-extern unsigned int ata_sff_host_intr(struct ata_port *ap,
+extern unsigned int ata_sff_port_intr(struct ata_port *ap,
                                      struct ata_queued_cmd *qc);
 extern irqreturn_t ata_sff_interrupt(int irq, void *dev_instance);
 extern void ata_sff_lost_interrupt(struct ata_port *ap);
@@ -1625,11 +1626,24 @@ extern int ata_pci_sff_init_one(struct pci_dev *pdev,
                struct scsi_host_template *sht, void *host_priv, int hflags);
 #endif /* CONFIG_PCI */
 
+#ifdef CONFIG_ATA_BMDMA
+
+extern const struct ata_port_operations ata_bmdma_port_ops;
+
+#define ATA_BMDMA_SHT(drv_name)                                        \
+       ATA_BASE_SHT(drv_name),                                 \
+       .sg_tablesize           = LIBATA_MAX_PRD,               \
+       .dma_boundary           = ATA_DMA_BOUNDARY
+
 extern void ata_bmdma_qc_prep(struct ata_queued_cmd *qc);
 extern unsigned int ata_bmdma_qc_issue(struct ata_queued_cmd *qc);
 extern void ata_bmdma_dumb_qc_prep(struct ata_queued_cmd *qc);
+extern unsigned int ata_bmdma_port_intr(struct ata_port *ap,
+                                     struct ata_queued_cmd *qc);
+extern irqreturn_t ata_bmdma_interrupt(int irq, void *dev_instance);
 extern void ata_bmdma_error_handler(struct ata_port *ap);
 extern void ata_bmdma_post_internal_cmd(struct ata_queued_cmd *qc);
+extern void ata_bmdma_irq_clear(struct ata_port *ap);
 extern void ata_bmdma_setup(struct ata_queued_cmd *qc);
 extern void ata_bmdma_start(struct ata_queued_cmd *qc);
 extern void ata_bmdma_stop(struct ata_queued_cmd *qc);
@@ -1640,7 +1654,15 @@ extern int ata_bmdma_port_start32(struct ata_port *ap);
 #ifdef CONFIG_PCI
 extern int ata_pci_bmdma_clear_simplex(struct pci_dev *pdev);
 extern void ata_pci_bmdma_init(struct ata_host *host);
+extern int ata_pci_bmdma_prepare_host(struct pci_dev *pdev,
+                                     const struct ata_port_info * const * ppi,
+                                     struct ata_host **r_host);
+extern int ata_pci_bmdma_init_one(struct pci_dev *pdev,
+                                 const struct ata_port_info * const * ppi,
+                                 struct scsi_host_template *sht,
+                                 void *host_priv, int hflags);
 #endif /* CONFIG_PCI */
+#endif /* CONFIG_ATA_BMDMA */
 
 /**
  *     ata_sff_busy_wait - Wait for a port status register
index e3c4ff8..bfd23be 100644 (file)
@@ -370,7 +370,7 @@ extern int pm860x_set_bits(struct i2c_client *, int, unsigned char,
                           unsigned char);
 
 extern int pm860x_device_init(struct pm860x_chip *chip,
-                             struct pm860x_platform_data *pdata);
-extern void pm860x_device_exit(struct pm860x_chip *chip);
+                             struct pm860x_platform_data *pdata) __devinit ;
+extern void pm860x_device_exit(struct pm860x_chip *chip) __devexit ;
 
 #endif /* __LINUX_MFD_88PM860X_H */
diff --git a/include/linux/mfd/ab4500.h b/include/linux/mfd/ab4500.h
deleted file mode 100644 (file)
index a42a703..0000000
+++ /dev/null
@@ -1,262 +0,0 @@
-/*
- * Copyright (C) 2009 ST-Ericsson
- *
- * Author: Srinidhi KASAGAR <srinidhi.kasagar@stericsson.com>
- *
- * 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.
- *
- * AB4500 device core funtions, for client access
- */
-#ifndef MFD_AB4500_H
-#define MFD_AB4500_H
-
-#include <linux/device.h>
-
-/*
- * AB4500 bank addresses
- */
-#define AB4500_SYS_CTRL1_BLOCK 0x1
-#define AB4500_SYS_CTRL2_BLOCK 0x2
-#define AB4500_REGU_CTRL1      0x3
-#define AB4500_REGU_CTRL2      0x4
-#define AB4500_USB             0x5
-#define AB4500_TVOUT           0x6
-#define AB4500_DBI             0x7
-#define AB4500_ECI_AV_ACC      0x8
-#define AB4500_RESERVED                0x9
-#define AB4500_GPADC           0xA
-#define AB4500_CHARGER         0xB
-#define AB4500_GAS_GAUGE       0xC
-#define AB4500_AUDIO           0xD
-#define AB4500_INTERRUPT       0xE
-#define AB4500_RTC             0xF
-#define AB4500_MISC            0x10
-#define AB4500_DEBUG           0x12
-#define AB4500_PROD_TEST       0x13
-#define AB4500_OTP_EMUL                0x15
-
-/*
- * System control 1 register offsets.
- * Bank = 0x01
- */
-#define AB4500_TURNON_STAT_REG         0x0100
-#define AB4500_RESET_STAT_REG          0x0101
-#define AB4500_PONKEY1_PRESS_STAT_REG  0x0102
-
-#define AB4500_FSM_STAT1_REG           0x0140
-#define AB4500_FSM_STAT2_REG           0x0141
-#define AB4500_SYSCLK_REQ_STAT_REG     0x0142
-#define AB4500_USB_STAT1_REG           0x0143
-#define AB4500_USB_STAT2_REG           0x0144
-#define AB4500_STATUS_SPARE1_REG       0x0145
-#define AB4500_STATUS_SPARE2_REG       0x0146
-
-#define AB4500_CTRL1_REG               0x0180
-#define AB4500_CTRL2_REG               0x0181
-
-/*
- * System control 2 register offsets.
- * bank = 0x02
- */
-#define AB4500_CTRL3_REG               0x0200
-#define AB4500_MAIN_WDOG_CTRL_REG      0x0201
-#define AB4500_MAIN_WDOG_TIMER_REG     0x0202
-#define AB4500_LOW_BAT_REG             0x0203
-#define AB4500_BATT_OK_REG             0x0204
-#define AB4500_SYSCLK_TIMER_REG                0x0205
-#define AB4500_SMPSCLK_CTRL_REG                0x0206
-#define AB4500_SMPSCLK_SEL1_REG                0x0207
-#define AB4500_SMPSCLK_SEL2_REG                0x0208
-#define AB4500_SMPSCLK_SEL3_REG                0x0209
-#define AB4500_SYSULPCLK_CONF_REG      0x020A
-#define AB4500_SYSULPCLK_CTRL1_REG     0x020B
-#define AB4500_SYSCLK_CTRL_REG         0x020C
-#define AB4500_SYSCLK_REQ1_VALID_REG   0x020D
-#define AB4500_SYSCLK_REQ_VALID_REG    0x020E
-#define AB4500_SYSCTRL_SPARE_REG       0x020F
-#define AB4500_PAD_CONF_REG            0x0210
-
-/*
- * Regu control1 register offsets
- * Bank = 0x03
- */
-#define AB4500_REGU_SERIAL_CTRL1_REG   0x0300
-#define AB4500_REGU_SERIAL_CTRL2_REG   0x0301
-#define AB4500_REGU_SERIAL_CTRL3_REG   0x0302
-#define AB4500_REGU_REQ_CTRL1_REG      0x0303
-#define AB4500_REGU_REQ_CTRL2_REG      0x0304
-#define AB4500_REGU_REQ_CTRL3_REG      0x0305
-#define AB4500_REGU_REQ_CTRL4_REG      0x0306
-#define AB4500_REGU_MISC1_REG          0x0380
-#define AB4500_REGU_OTGSUPPLY_CTRL_REG 0x0381
-#define AB4500_REGU_VUSB_CTRL_REG      0x0382
-#define AB4500_REGU_VAUDIO_SUPPLY_REG  0x0383
-#define AB4500_REGU_CTRL1_SPARE_REG    0x0384
-
-/*
- * Regu control2 Vmod register offsets
- */
-#define AB4500_REGU_VMOD_REGU_REG      0x0440
-#define AB4500_REGU_VMOD_SEL1_REG      0x0441
-#define AB4500_REGU_VMOD_SEL2_REG      0x0442
-#define AB4500_REGU_CTRL_DISCH_REG     0x0443
-#define AB4500_REGU_CTRL_DISCH2_REG    0x0444
-
-/*
- * USB/ULPI register offsets
- * Bank : 0x5
- */
-#define AB4500_USB_LINE_STAT_REG       0x0580
-#define AB4500_USB_LINE_CTRL1_REG      0x0581
-#define AB4500_USB_LINE_CTRL2_REG      0x0582
-#define AB4500_USB_LINE_CTRL3_REG      0x0583
-#define AB4500_USB_LINE_CTRL4_REG      0x0584
-#define AB4500_USB_LINE_CTRL5_REG      0x0585
-#define AB4500_USB_OTG_CTRL_REG                0x0587
-#define AB4500_USB_OTG_STAT_REG                0x0588
-#define AB4500_USB_OTG_STAT_REG                0x0588
-#define AB4500_USB_CTRL_SPARE_REG      0x0589
-#define AB4500_USB_PHY_CTRL_REG                0x058A
-
-/*
- * TVOUT / CTRL register offsets
- * Bank : 0x06
- */
-#define AB4500_TVOUT_CTRL_REG          0x0680
-
-/*
- * DBI register offsets
- * Bank : 0x07
- */
-#define AB4500_DBI_REG1_REG            0x0700
-#define AB4500_DBI_REG2_REG            0x0701
-
-/*
- * ECI regsiter offsets
- * Bank : 0x08
- */
-#define AB4500_ECI_CTRL_REG            0x0800
-#define AB4500_ECI_HOOKLEVEL_REG       0x0801
-#define AB4500_ECI_DATAOUT_REG         0x0802
-#define AB4500_ECI_DATAIN_REG          0x0803
-
-/*
- * AV Connector register offsets
- * Bank : 0x08
- */
-#define AB4500_AV_CONN_REG             0x0840
-
-/*
- * Accessory detection register offsets
- * Bank : 0x08
- */
-#define AB4500_ACC_DET_DB1_REG         0x0880
-#define AB4500_ACC_DET_DB2_REG         0x0881
-
-/*
- * GPADC register offsets
- * Bank : 0x0A
- */
-#define AB4500_GPADC_CTRL1_REG         0x0A00
-#define AB4500_GPADC_CTRL2_REG         0x0A01
-#define AB4500_GPADC_CTRL3_REG         0x0A02
-#define AB4500_GPADC_AUTO_TIMER_REG    0x0A03
-#define AB4500_GPADC_STAT_REG          0x0A04
-#define AB4500_GPADC_MANDATAL_REG      0x0A05
-#define AB4500_GPADC_MANDATAH_REG      0x0A06
-#define AB4500_GPADC_AUTODATAL_REG     0x0A07
-#define AB4500_GPADC_AUTODATAH_REG     0x0A08
-#define AB4500_GPADC_MUX_CTRL_REG      0x0A09
-
-/*
- * Charger / status register offfsets
- * Bank : 0x0B
- */
-#define AB4500_CH_STATUS1_REG          0x0B00
-#define AB4500_CH_STATUS2_REG          0x0B01
-#define AB4500_CH_USBCH_STAT1_REG      0x0B02
-#define AB4500_CH_USBCH_STAT2_REG      0x0B03
-#define AB4500_CH_FSM_STAT_REG         0x0B04
-#define AB4500_CH_STAT_REG             0x0B05
-
-/*
- * Charger / control register offfsets
- * Bank : 0x0B
- */
-#define AB4500_CH_VOLT_LVL_REG         0x0B40
-
-/*
- * Charger / main control register offfsets
- * Bank : 0x0B
- */
-#define AB4500_MCH_CTRL1               0x0B80
-#define AB4500_MCH_CTRL2               0x0B81
-#define AB4500_MCH_IPT_CURLVL_REG      0x0B82
-#define AB4500_CH_WD_REG               0x0B83
-
-/*
- * Charger / USB control register offsets
- * Bank : 0x0B
- */
-#define AB4500_USBCH_CTRL1_REG         0x0BC0
-#define AB4500_USBCH_CTRL2_REG         0x0BC1
-#define AB4500_USBCH_IPT_CRNTLVL_REG   0x0BC2
-
-/*
- * RTC bank register offsets
- * Bank : 0xF
- */
-#define AB4500_RTC_SOFF_STAT_REG       0x0F00
-#define AB4500_RTC_CC_CONF_REG         0x0F01
-#define AB4500_RTC_READ_REQ_REG                0x0F02
-#define AB4500_RTC_WATCH_TSECMID_REG   0x0F03
-#define AB4500_RTC_WATCH_TSECHI_REG    0x0F04
-#define AB4500_RTC_WATCH_TMIN_LOW_REG  0x0F05
-#define AB4500_RTC_WATCH_TMIN_MID_REG  0x0F06
-#define AB4500_RTC_WATCH_TMIN_HI_REG   0x0F07
-#define AB4500_RTC_ALRM_MIN_LOW_REG    0x0F08
-#define AB4500_RTC_ALRM_MIN_MID_REG    0x0F09
-#define AB4500_RTC_ALRM_MIN_HI_REG     0x0F0A
-#define AB4500_RTC_STAT_REG            0x0F0B
-#define AB4500_RTC_BKUP_CHG_REG                0x0F0C
-#define AB4500_RTC_FORCE_BKUP_REG      0x0F0D
-#define AB4500_RTC_CALIB_REG           0x0F0E
-#define AB4500_RTC_SWITCH_STAT_REG     0x0F0F
-
-/*
- * PWM Out generators
- * Bank: 0x10
- */
-#define AB4500_PWM_OUT_CTRL1_REG       0x1060
-#define AB4500_PWM_OUT_CTRL2_REG       0x1061
-#define AB4500_PWM_OUT_CTRL3_REG       0x1062
-#define AB4500_PWM_OUT_CTRL4_REG       0x1063
-#define AB4500_PWM_OUT_CTRL5_REG       0x1064
-#define AB4500_PWM_OUT_CTRL6_REG       0x1065
-#define AB4500_PWM_OUT_CTRL7_REG       0x1066
-
-#define AB4500_I2C_PAD_CTRL_REG                0x1067
-#define AB4500_REV_REG                 0x1080
-
-/**
- * struct ab4500
- * @spi: spi device structure
- * @tx_buf: transmit buffer
- * @rx_buf: receive buffer
- * @lock: sync primitive
- */
-struct ab4500 {
-       struct spi_device       *spi;
-       unsigned long           tx_buf[4];
-       unsigned long           rx_buf[4];
-       struct mutex            lock;
-};
-
-int ab4500_write(struct ab4500 *ab4500, unsigned char block,
-               unsigned long addr, unsigned char data);
-int ab4500_read(struct ab4500 *ab4500, unsigned char block,
-               unsigned long addr);
-
-#endif /* MFD_AB4500_H */
diff --git a/include/linux/mfd/ab8500.h b/include/linux/mfd/ab8500.h
new file mode 100644 (file)
index 0000000..b63ff3b
--- /dev/null
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ *
+ * License Terms: GNU General Public License v2
+ * Author: Srinidhi Kasagar <srinidhi.kasagar@stericsson.com>
+ */
+#ifndef MFD_AB8500_H
+#define MFD_AB8500_H
+
+#include <linux/device.h>
+
+/*
+ * Interrupts
+ */
+
+#define AB8500_INT_MAIN_EXT_CH_NOT_OK  0
+#define AB8500_INT_UN_PLUG_TV_DET      1
+#define AB8500_INT_PLUG_TV_DET         2
+#define AB8500_INT_TEMP_WARM           3
+#define AB8500_INT_PON_KEY2DB_F                4
+#define AB8500_INT_PON_KEY2DB_R                5
+#define AB8500_INT_PON_KEY1DB_F                6
+#define AB8500_INT_PON_KEY1DB_R                7
+#define AB8500_INT_BATT_OVV            8
+#define AB8500_INT_MAIN_CH_UNPLUG_DET  10
+#define AB8500_INT_MAIN_CH_PLUG_DET    11
+#define AB8500_INT_USB_ID_DET_F                12
+#define AB8500_INT_USB_ID_DET_R                13
+#define AB8500_INT_VBUS_DET_F          14
+#define AB8500_INT_VBUS_DET_R          15
+#define AB8500_INT_VBUS_CH_DROP_END    16
+#define AB8500_INT_RTC_60S             17
+#define AB8500_INT_RTC_ALARM           18
+#define AB8500_INT_BAT_CTRL_INDB       20
+#define AB8500_INT_CH_WD_EXP           21
+#define AB8500_INT_VBUS_OVV            22
+#define AB8500_INT_MAIN_CH_DROP_END    23
+#define AB8500_INT_CCN_CONV_ACC                24
+#define AB8500_INT_INT_AUD             25
+#define AB8500_INT_CCEOC               26
+#define AB8500_INT_CC_INT_CALIB                27
+#define AB8500_INT_LOW_BAT_F           28
+#define AB8500_INT_LOW_BAT_R           29
+#define AB8500_INT_BUP_CHG_NOT_OK      30
+#define AB8500_INT_BUP_CHG_OK          31
+#define AB8500_INT_GP_HW_ADC_CONV_END  32
+#define AB8500_INT_ACC_DETECT_1DB_F    33
+#define AB8500_INT_ACC_DETECT_1DB_R    34
+#define AB8500_INT_ACC_DETECT_22DB_F   35
+#define AB8500_INT_ACC_DETECT_22DB_R   36
+#define AB8500_INT_ACC_DETECT_21DB_F   37
+#define AB8500_INT_ACC_DETECT_21DB_R   38
+#define AB8500_INT_GP_SW_ADC_CONV_END  39
+#define AB8500_INT_BTEMP_LOW           72
+#define AB8500_INT_BTEMP_LOW_MEDIUM    73
+#define AB8500_INT_BTEMP_MEDIUM_HIGH   74
+#define AB8500_INT_BTEMP_HIGH          75
+#define AB8500_INT_USB_CHARGER_NOT_OK  81
+#define AB8500_INT_ID_WAKEUP_R         82
+#define AB8500_INT_ID_DET_R1R          84
+#define AB8500_INT_ID_DET_R2R          85
+#define AB8500_INT_ID_DET_R3R          86
+#define AB8500_INT_ID_DET_R4R          87
+#define AB8500_INT_ID_WAKEUP_F         88
+#define AB8500_INT_ID_DET_R1F          90
+#define AB8500_INT_ID_DET_R2F          91
+#define AB8500_INT_ID_DET_R3F          92
+#define AB8500_INT_ID_DET_R4F          93
+#define AB8500_INT_USB_CHG_DET_DONE    94
+#define AB8500_INT_USB_CH_TH_PROT_F    96
+#define AB8500_INT_USB_CH_TH_PROP_R    97
+#define AB8500_INT_MAIN_CH_TH_PROP_F   98
+#define AB8500_INT_MAIN_CH_TH_PROT_R   99
+#define AB8500_INT_USB_CHARGER_NOT_OKF 103
+
+#define AB8500_NR_IRQS                 104
+#define AB8500_NUM_IRQ_REGS            13
+
+/**
+ * struct ab8500 - ab8500 internal structure
+ * @dev: parent device
+ * @lock: read/write operations lock
+ * @irq_lock: genirq bus lock
+ * @revision: chip revision
+ * @irq: irq line
+ * @write: register write
+ * @read: register read
+ * @rx_buf: rx buf for SPI
+ * @tx_buf: tx buf for SPI
+ * @mask: cache of IRQ regs for bus lock
+ * @oldmask: cache of previous IRQ regs for bus lock
+ */
+struct ab8500 {
+       struct device   *dev;
+       struct mutex    lock;
+       struct mutex    irq_lock;
+       int             revision;
+       int             irq_base;
+       int             irq;
+
+       int (*write) (struct ab8500 *a8500, u16 addr, u8 data);
+       int (*read) (struct ab8500 *a8500, u16 addr);
+
+       unsigned long   tx_buf[4];
+       unsigned long   rx_buf[4];
+
+       u8 mask[AB8500_NUM_IRQ_REGS];
+       u8 oldmask[AB8500_NUM_IRQ_REGS];
+};
+
+/**
+ * struct ab8500_platform_data - AB8500 platform data
+ * @irq_base: start of AB8500 IRQs, AB8500_NR_IRQS will be used
+ * @init: board-specific initialization after detection of ab8500
+ */
+struct ab8500_platform_data {
+       int irq_base;
+       void (*init) (struct ab8500 *);
+};
+
+extern int ab8500_write(struct ab8500 *a8500, u16 addr, u8 data);
+extern int ab8500_read(struct ab8500 *a8500, u16 addr);
+extern int ab8500_set_bits(struct ab8500 *a8500, u16 addr, u8 mask, u8 data);
+
+extern int __devinit ab8500_init(struct ab8500 *ab8500);
+extern int __devexit ab8500_exit(struct ab8500 *ab8500);
+
+#endif /* MFD_AB8500_H */
similarity index 51%
rename from include/linux/mfd/ab3100.h
rename to include/linux/mfd/abx500.h
index 9a881c3..390726f 100644 (file)
@@ -3,17 +3,37 @@
  * License terms: GNU General Public License (GPL) version 2
  * AB3100 core access functions
  * Author: Linus Walleij <linus.walleij@stericsson.com>
+ *
+ * ABX500 core access functions.
+ * The abx500 interface is used for the Analog Baseband chip
+ * ab3100, ab3550, ab5500 and possibly comming. It is not used for
+ * ab4500 and ab8500 since they are another family of chip.
+ *
+ * Author: Mattias Wallin <mattias.wallin@stericsson.com>
+ * Author: Mattias Nilsson <mattias.i.nilsson@stericsson.com>
+ * Author: Bengt Jonsson <bengt.g.jonsson@stericsson.com>
+ * Author: Rickard Andersson <rickard.andersson@stericsson.com>
  */
 
 #include <linux/device.h>
 #include <linux/regulator/machine.h>
 
-#ifndef MFD_AB3100_H
-#define MFD_AB3100_H
+#ifndef MFD_ABX500_H
+#define MFD_ABX500_H
 
-#define ABUNKNOWN      0
-#define        AB3000          1
-#define        AB3100          2
+#define AB3100_P1A     0xc0
+#define AB3100_P1B     0xc1
+#define AB3100_P1C     0xc2
+#define AB3100_P1D     0xc3
+#define AB3100_P1E     0xc4
+#define AB3100_P1F     0xc5
+#define AB3100_P1G     0xc6
+#define AB3100_R2A     0xc7
+#define AB3100_R2B     0xc8
+#define AB3550_P1A     0x10
+#define AB5500_1_0     0x20
+#define AB5500_2_0     0x21
+#define AB5500_2_1     0x22
 
 /*
  * AB3100, EVENTA1, A2 and A3 event register flags
@@ -89,7 +109,7 @@ struct ab3100 {
        char chip_name[32];
        u8 chip_id;
        struct blocking_notifier_head event_subscribers;
-       u32 startup_events;
+       u8 startup_events[3];
        bool startup_events_read;
 };
 
@@ -112,18 +132,102 @@ struct ab3100_platform_data {
        int external_voltage;
 };
 
-int ab3100_set_register_interruptible(struct ab3100 *ab3100, u8 reg, u8 regval);
-int ab3100_get_register_interruptible(struct ab3100 *ab3100, u8 reg, u8 *regval);
-int ab3100_get_register_page_interruptible(struct ab3100 *ab3100,
-                            u8 first_reg, u8 *regvals, u8 numregs);
-int ab3100_mask_and_set_register_interruptible(struct ab3100 *ab3100,
-                                u8 reg, u8 andmask, u8 ormask);
-u8 ab3100_get_chip_type(struct ab3100 *ab3100);
 int ab3100_event_register(struct ab3100 *ab3100,
                          struct notifier_block *nb);
 int ab3100_event_unregister(struct ab3100 *ab3100,
                            struct notifier_block *nb);
-int ab3100_event_registers_startup_state_get(struct ab3100 *ab3100,
-                                            u32 *fatevent);
 
+/* AB3550, STR register flags */
+#define AB3550_STR_ONSWA                               (0x01)
+#define AB3550_STR_ONSWB                               (0x02)
+#define AB3550_STR_ONSWC                               (0x04)
+#define AB3550_STR_DCIO                                        (0x08)
+#define AB3550_STR_BOOT_MODE                           (0x10)
+#define AB3550_STR_SIM_OFF                             (0x20)
+#define AB3550_STR_BATT_REMOVAL                                (0x40)
+#define AB3550_STR_VBUS                                        (0x80)
+
+/* Interrupt mask registers */
+#define AB3550_IMR1 0x29
+#define AB3550_IMR2 0x2a
+#define AB3550_IMR3 0x2b
+#define AB3550_IMR4 0x2c
+#define AB3550_IMR5 0x2d
+
+enum ab3550_devid {
+       AB3550_DEVID_ADC,
+       AB3550_DEVID_DAC,
+       AB3550_DEVID_LEDS,
+       AB3550_DEVID_POWER,
+       AB3550_DEVID_REGULATORS,
+       AB3550_DEVID_SIM,
+       AB3550_DEVID_UART,
+       AB3550_DEVID_RTC,
+       AB3550_DEVID_CHARGER,
+       AB3550_DEVID_FUELGAUGE,
+       AB3550_DEVID_VIBRATOR,
+       AB3550_DEVID_CODEC,
+       AB3550_NUM_DEVICES,
+};
+
+/**
+ * struct abx500_init_setting
+ * Initial value of the registers for driver to use during setup.
+ */
+struct abx500_init_settings {
+       u8 bank;
+       u8 reg;
+       u8 setting;
+};
+
+/**
+ * struct ab3550_platform_data
+ * Data supplied to initialize board connections to the AB3550
+ */
+struct ab3550_platform_data {
+       struct {unsigned int base; unsigned int count; } irq;
+       void *dev_data[AB3550_NUM_DEVICES];
+       size_t dev_data_sz[AB3550_NUM_DEVICES];
+       struct abx500_init_settings *init_settings;
+       unsigned int init_settings_sz;
+};
+
+int abx500_set_register_interruptible(struct device *dev, u8 bank, u8 reg,
+       u8 value);
+int abx500_get_register_interruptible(struct device *dev, u8 bank, u8 reg,
+       u8 *value);
+int abx500_get_register_page_interruptible(struct device *dev, u8 bank,
+       u8 first_reg, u8 *regvals, u8 numregs);
+int abx500_set_register_page_interruptible(struct device *dev, u8 bank,
+       u8 first_reg, u8 *regvals, u8 numregs);
+/**
+ * abx500_mask_and_set_register_inerruptible() - Modifies selected bits of a
+ *     target register
+ *
+ * @dev: The AB sub device.
+ * @bank: The i2c bank number.
+ * @bitmask: The bit mask to use.
+ * @bitvalues: The new bit values.
+ *
+ * Updates the value of an AB register:
+ * value -> ((value & ~bitmask) | (bitvalues & bitmask))
+ */
+int abx500_mask_and_set_register_interruptible(struct device *dev, u8 bank,
+       u8 reg, u8 bitmask, u8 bitvalues);
+int abx500_get_chip_id(struct device *dev);
+int abx500_event_registers_startup_state_get(struct device *dev, u8 *event);
+int abx500_startup_irq_enabled(struct device *dev, unsigned int irq);
+
+struct abx500_ops {
+       int (*get_chip_id) (struct device *);
+       int (*get_register) (struct device *, u8, u8, u8 *);
+       int (*set_register) (struct device *, u8, u8, u8);
+       int (*get_register_page) (struct device *, u8, u8, u8 *, u8);
+       int (*set_register_page) (struct device *, u8, u8, u8 *, u8);
+       int (*mask_and_set_register) (struct device *, u8, u8, u8, u8);
+       int (*event_registers_startup_state_get) (struct device *, u8 *);
+       int (*startup_irq_enabled) (struct device *, unsigned int);
+};
+
+int abx500_register_ops(struct device *core_dev, struct abx500_ops *ops);
 #endif
diff --git a/include/linux/mfd/janz.h b/include/linux/mfd/janz.h
new file mode 100644 (file)
index 0000000..e9994c4
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * Common Definitions for Janz MODULbus devices
+ *
+ * Copyright (c) 2010 Ira W. Snyder <iws@ovro.caltech.edu>
+ *
+ * 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 JANZ_H
+#define JANZ_H
+
+struct janz_platform_data {
+       /* MODULbus Module Number */
+       unsigned int modno;
+};
+
+/* PLX bridge chip onboard registers */
+struct janz_cmodio_onboard_regs {
+       u8 unused1;
+
+       /*
+        * Read access: interrupt status
+        * Write access: interrupt disable
+        */
+       u8 int_disable;
+       u8 unused2;
+
+       /*
+        * Read access: MODULbus number (hex switch)
+        * Write access: interrupt enable
+        */
+       u8 int_enable;
+       u8 unused3;
+
+       /* write-only */
+       u8 reset_assert;
+       u8 unused4;
+
+       /* write-only */
+       u8 reset_deassert;
+       u8 unused5;
+
+       /* read-write access to serial EEPROM */
+       u8 eep;
+       u8 unused6;
+
+       /* write-only access to EEPROM chip select */
+       u8 enid;
+};
+
+#endif /* JANZ_H */
index 8895d9d..4a894f6 100644 (file)
@@ -64,6 +64,70 @@ static inline int mc13783_ackirq(struct mc13783 *mc13783, int irq)
                                        MC13783_ADC0_TSMOD1 | \
                                        MC13783_ADC0_TSMOD2)
 
+struct mc13783_led_platform_data {
+#define MC13783_LED_MD         0
+#define MC13783_LED_AD         1
+#define MC13783_LED_KP         2
+#define MC13783_LED_R1         3
+#define MC13783_LED_G1         4
+#define MC13783_LED_B1         5
+#define MC13783_LED_R2         6
+#define MC13783_LED_G2         7
+#define MC13783_LED_B2         8
+#define MC13783_LED_R3         9
+#define MC13783_LED_G3         10
+#define MC13783_LED_B3         11
+#define MC13783_LED_MAX MC13783_LED_B3
+       int id;
+       const char *name;
+       const char *default_trigger;
+
+/* Three or two bits current selection depending on the led */
+       char max_current;
+};
+
+struct mc13783_leds_platform_data {
+       int num_leds;
+       struct mc13783_led_platform_data *led;
+
+#define MC13783_LED_TRIODE_MD  (1 << 0)
+#define MC13783_LED_TRIODE_AD  (1 << 1)
+#define MC13783_LED_TRIODE_KP  (1 << 2)
+#define MC13783_LED_BOOST_EN   (1 << 3)
+#define MC13783_LED_TC1HALF    (1 << 4)
+#define MC13783_LED_SLEWLIMTC  (1 << 5)
+#define MC13783_LED_SLEWLIMBL  (1 << 6)
+#define MC13783_LED_TRIODE_TC1 (1 << 7)
+#define MC13783_LED_TRIODE_TC2 (1 << 8)
+#define MC13783_LED_TRIODE_TC3 (1 << 9)
+       int flags;
+
+#define MC13783_LED_AB_DISABLED                0
+#define MC13783_LED_AB_MD1             1
+#define MC13783_LED_AB_MD12            2
+#define MC13783_LED_AB_MD123           3
+#define MC13783_LED_AB_MD1234          4
+#define MC13783_LED_AB_MD1234_AD1      5
+#define MC13783_LED_AB_MD1234_AD12     6
+#define MC13783_LED_AB_MD1_AD          7
+       char abmode;
+
+#define MC13783_LED_ABREF_200MV        0
+#define MC13783_LED_ABREF_400MV        1
+#define MC13783_LED_ABREF_600MV        2
+#define MC13783_LED_ABREF_800MV        3
+       char abref;
+
+#define MC13783_LED_PERIOD_10MS                0
+#define MC13783_LED_PERIOD_100MS       1
+#define MC13783_LED_PERIOD_500MS       2
+#define MC13783_LED_PERIOD_2S          3
+       char bl_period;
+       char tc1_period;
+       char tc2_period;
+       char tc3_period;
+};
+
 /* to be cleaned up */
 struct regulator_init_data;
 
@@ -80,12 +144,14 @@ struct mc13783_regulator_platform_data {
 struct mc13783_platform_data {
        int num_regulators;
        struct mc13783_regulator_init_data *regulators;
+       struct mc13783_leds_platform_data *leds;
 
 #define MC13783_USE_TOUCHSCREEN (1 << 0)
 #define MC13783_USE_CODEC      (1 << 1)
 #define MC13783_USE_ADC                (1 << 2)
 #define MC13783_USE_RTC                (1 << 3)
 #define MC13783_USE_REGULATOR  (1 << 4)
+#define MC13783_USE_LED                (1 << 5)
        unsigned int flags;
 };
 
diff --git a/include/linux/mfd/pcf50633/backlight.h b/include/linux/mfd/pcf50633/backlight.h
new file mode 100644 (file)
index 0000000..83747e2
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ *  Copyright (C) 2009-2010, Lars-Peter Clausen <lars@metafoo.de>
+ *      PCF50633 backlight device 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.
+ *
+ *  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_PCF50633_BACKLIGHT
+#define __LINUX_MFD_PCF50633_BACKLIGHT
+
+/*
+* @default_brightness: Backlight brightness is initialized to this value
+*
+* Brightness to be used after the driver has been probed.
+* Valid range 0-63.
+*
+* @default_brightness_limit: The actual brightness is limited by this value
+*
+* Brightness limit to be used after the driver has been probed. This is useful
+* when it is not known how much power is available for the backlight during
+* probe.
+* Valid range 0-63. Can be changed later with pcf50633_bl_set_brightness_limit.
+*
+* @ramp_time: Display ramp time when changing brightness
+*
+* When changing the backlights brightness the change is not instant, instead
+* it fades smooth from one state to another. This value specifies how long
+* the fade should take. The lower the value the higher the fade time.
+* Valid range 0-255
+*/
+struct pcf50633_bl_platform_data {
+       unsigned int    default_brightness;
+       unsigned int    default_brightness_limit;
+       uint8_t         ramp_time;
+};
+
+
+struct pcf50633;
+
+int pcf50633_bl_set_brightness_limit(struct pcf50633 *pcf, unsigned int limit);
+
+#endif
+
index 3398bd9..ad411a7 100644 (file)
@@ -18,6 +18,7 @@
 #include <linux/regulator/driver.h>
 #include <linux/regulator/machine.h>
 #include <linux/power_supply.h>
+#include <linux/mfd/pcf50633/backlight.h>
 
 struct pcf50633;
 
@@ -43,6 +44,8 @@ struct pcf50633_platform_data {
        void (*force_shutdown)(struct pcf50633 *);
 
        u8 resumers[5];
+
+       struct pcf50633_bl_platform_data *backlight_data;
 };
 
 struct pcf50633_irq {
@@ -152,6 +155,7 @@ struct pcf50633 {
        struct platform_device *mbc_pdev;
        struct platform_device *adc_pdev;
        struct platform_device *input_pdev;
+       struct platform_device *bl_pdev;
        struct platform_device *regulator_pdev[PCF50633_NUM_REGULATORS];
 };
 
diff --git a/include/linux/mfd/rdc321x.h b/include/linux/mfd/rdc321x.h
new file mode 100644 (file)
index 0000000..4bdf19c
--- /dev/null
@@ -0,0 +1,26 @@
+#ifndef __RDC321X_MFD_H
+#define __RDC321X_MFD_H
+
+#include <linux/types.h>
+#include <linux/pci.h>
+
+/* Offsets to be accessed in the southbridge PCI
+ * device configuration register */
+#define RDC321X_WDT_CTRL       0x44
+#define RDC321X_GPIO_CTRL_REG1 0x48
+#define RDC321X_GPIO_DATA_REG1 0x4c
+#define RDC321X_GPIO_CTRL_REG2 0x84
+#define RDC321X_GPIO_DATA_REG2 0x88
+
+#define RDC321X_MAX_GPIO       58
+
+struct rdc321x_gpio_pdata {
+       struct pci_dev *sb_pdev;
+       unsigned max_gpios;
+};
+
+struct rdc321x_wdt_pdata {
+       struct pci_dev *sb_pdev;
+};
+
+#endif /* __RDC321X_MFD_H */
diff --git a/include/linux/mfd/tc35892.h b/include/linux/mfd/tc35892.h
new file mode 100644 (file)
index 0000000..e47f770
--- /dev/null
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ *
+ * License Terms: GNU General Public License, version 2
+ */
+
+#ifndef __LINUX_MFD_TC35892_H
+#define __LINUX_MFD_TC35892_H
+
+#include <linux/device.h>
+
+#define TC35892_RSTCTRL_IRQRST (1 << 4)
+#define TC35892_RSTCTRL_TIMRST (1 << 3)
+#define TC35892_RSTCTRL_ROTRST (1 << 2)
+#define TC35892_RSTCTRL_KBDRST (1 << 1)
+#define TC35892_RSTCTRL_GPIRST (1 << 0)
+
+#define TC35892_IRQST          0x91
+
+#define TC35892_MANFCODE_MAGIC 0x03
+#define TC35892_MANFCODE       0x80
+#define TC35892_VERSION                0x81
+#define TC35892_IOCFG          0xA7
+
+#define TC35892_CLKMODE                0x88
+#define TC35892_CLKCFG         0x89
+#define TC35892_CLKEN          0x8A
+
+#define TC35892_RSTCTRL                0x82
+#define TC35892_EXTRSTN                0x83
+#define TC35892_RSTINTCLR      0x84
+
+#define TC35892_GPIOIS0                0xC9
+#define TC35892_GPIOIS1                0xCA
+#define TC35892_GPIOIS2                0xCB
+#define TC35892_GPIOIBE0       0xCC
+#define TC35892_GPIOIBE1       0xCD
+#define TC35892_GPIOIBE2       0xCE
+#define TC35892_GPIOIEV0       0xCF
+#define TC35892_GPIOIEV1       0xD0
+#define TC35892_GPIOIEV2       0xD1
+#define TC35892_GPIOIE0                0xD2
+#define TC35892_GPIOIE1                0xD3
+#define TC35892_GPIOIE2                0xD4
+#define TC35892_GPIORIS0       0xD6
+#define TC35892_GPIORIS1       0xD7
+#define TC35892_GPIORIS2       0xD8
+#define TC35892_GPIOMIS0       0xD9
+#define TC35892_GPIOMIS1       0xDA
+#define TC35892_GPIOMIS2       0xDB
+#define TC35892_GPIOIC0                0xDC
+#define TC35892_GPIOIC1                0xDD
+#define TC35892_GPIOIC2                0xDE
+
+#define TC35892_GPIODATA0      0xC0
+#define TC35892_GPIOMASK0      0xc1
+#define TC35892_GPIODATA1      0xC2
+#define TC35892_GPIOMASK1      0xc3
+#define TC35892_GPIODATA2      0xC4
+#define TC35892_GPIOMASK2      0xC5
+
+#define TC35892_GPIODIR0       0xC6
+#define TC35892_GPIODIR1       0xC7
+#define TC35892_GPIODIR2       0xC8
+
+#define TC35892_GPIOSYNC0      0xE6
+#define TC35892_GPIOSYNC1      0xE7
+#define TC35892_GPIOSYNC2      0xE8
+
+#define TC35892_GPIOWAKE0      0xE9
+#define TC35892_GPIOWAKE1      0xEA
+#define TC35892_GPIOWAKE2      0xEB
+
+#define TC35892_GPIOODM0       0xE0
+#define TC35892_GPIOODE0       0xE1
+#define TC35892_GPIOODM1       0xE2
+#define TC35892_GPIOODE1       0xE3
+#define TC35892_GPIOODM2       0xE4
+#define TC35892_GPIOODE2       0xE5
+
+#define TC35892_INT_GPIIRQ     0
+#define TC35892_INT_TI0IRQ     1
+#define TC35892_INT_TI1IRQ     2
+#define TC35892_INT_TI2IRQ     3
+#define TC35892_INT_ROTIRQ     5
+#define TC35892_INT_KBDIRQ     6
+#define TC35892_INT_PORIRQ     7
+
+#define TC35892_NR_INTERNAL_IRQS       8
+#define TC35892_INT_GPIO(x)    (TC35892_NR_INTERNAL_IRQS + (x))
+
+struct tc35892 {
+       struct mutex lock;
+       struct device *dev;
+       struct i2c_client *i2c;
+
+       int irq_base;
+       int num_gpio;
+       struct tc35892_platform_data *pdata;
+};
+
+extern int tc35892_reg_write(struct tc35892 *tc35892, u8 reg, u8 data);
+extern int tc35892_reg_read(struct tc35892 *tc35892, u8 reg);
+extern int tc35892_block_read(struct tc35892 *tc35892, u8 reg, u8 length,
+                             u8 *values);
+extern int tc35892_block_write(struct tc35892 *tc35892, u8 reg, u8 length,
+                              const u8 *values);
+extern int tc35892_set_bits(struct tc35892 *tc35892, u8 reg, u8 mask, u8 val);
+
+/**
+ * struct tc35892_gpio_platform_data - TC35892 GPIO platform data
+ * @gpio_base: first gpio number assigned to TC35892.  A maximum of
+ *            %TC35892_NR_GPIOS GPIOs will be allocated.
+ */
+struct tc35892_gpio_platform_data {
+       int gpio_base;
+};
+
+/**
+ * struct tc35892_platform_data - TC35892 platform data
+ * @irq_base: base IRQ number.  %TC35892_NR_IRQS irqs will be used.
+ * @gpio: GPIO-specific platform data
+ */
+struct tc35892_platform_data {
+       int irq_base;
+       struct tc35892_gpio_platform_data *gpio;
+};
+
+#define TC35892_NR_GPIOS       24
+#define TC35892_NR_IRQS                TC35892_INT_GPIO(TC35892_NR_GPIOS)
+
+#endif
diff --git a/include/linux/mfd/tps6507x.h b/include/linux/mfd/tps6507x.h
new file mode 100644 (file)
index 0000000..c923e48
--- /dev/null
@@ -0,0 +1,169 @@
+/* linux/mfd/tps6507x.h
+ *
+ * Functions to access TPS65070 power management chip.
+ *
+ * Copyright (c) 2009 RidgeRun (todd.fischer@ridgerun.com)
+ *
+ *
+ *  For licencing details see kernel-base/COPYING
+ */
+
+#ifndef __LINUX_MFD_TPS6507X_H
+#define __LINUX_MFD_TPS6507X_H
+
+/*
+ * ----------------------------------------------------------------------------
+ * Registers, all 8 bits
+ * ----------------------------------------------------------------------------
+ */
+
+
+/* Register definitions */
+#define        TPS6507X_REG_PPATH1                     0X01
+#define        TPS6507X_CHG_USB                        BIT(7)
+#define        TPS6507X_CHG_AC                         BIT(6)
+#define        TPS6507X_CHG_USB_PW_ENABLE              BIT(5)
+#define        TPS6507X_CHG_AC_PW_ENABLE               BIT(4)
+#define        TPS6507X_CHG_AC_CURRENT                 BIT(2)
+#define        TPS6507X_CHG_USB_CURRENT                BIT(0)
+
+#define        TPS6507X_REG_INT                        0X02
+#define        TPS6507X_REG_MASK_AC_USB                BIT(7)
+#define        TPS6507X_REG_MASK_TSC                   BIT(6)
+#define        TPS6507X_REG_MASK_PB_IN                 BIT(5)
+#define        TPS6507X_REG_TSC_INT                    BIT(3)
+#define        TPS6507X_REG_PB_IN_INT                  BIT(2)
+#define        TPS6507X_REG_AC_USB_APPLIED             BIT(1)
+#define        TPS6507X_REG_AC_USB_REMOVED             BIT(0)
+
+#define        TPS6507X_REG_CHGCONFIG0                 0X03
+
+#define        TPS6507X_REG_CHGCONFIG1                 0X04
+#define        TPS6507X_CON_CTRL1_DCDC1_ENABLE         BIT(4)
+#define        TPS6507X_CON_CTRL1_DCDC2_ENABLE         BIT(3)
+#define        TPS6507X_CON_CTRL1_DCDC3_ENABLE         BIT(2)
+#define        TPS6507X_CON_CTRL1_LDO1_ENABLE          BIT(1)
+#define        TPS6507X_CON_CTRL1_LDO2_ENABLE          BIT(0)
+
+#define        TPS6507X_REG_CHGCONFIG2                 0X05
+
+#define        TPS6507X_REG_CHGCONFIG3                 0X06
+
+#define        TPS6507X_REG_ADCONFIG                   0X07
+#define        TPS6507X_ADCONFIG_AD_ENABLE             BIT(7)
+#define        TPS6507X_ADCONFIG_START_CONVERSION      BIT(6)
+#define        TPS6507X_ADCONFIG_CONVERSION_DONE       BIT(5)
+#define        TPS6507X_ADCONFIG_VREF_ENABLE           BIT(4)
+#define        TPS6507X_ADCONFIG_INPUT_AD_IN1          0
+#define        TPS6507X_ADCONFIG_INPUT_AD_IN2          1
+#define        TPS6507X_ADCONFIG_INPUT_AD_IN3          2
+#define        TPS6507X_ADCONFIG_INPUT_AD_IN4          3
+#define        TPS6507X_ADCONFIG_INPUT_TS_PIN          4
+#define        TPS6507X_ADCONFIG_INPUT_BAT_CURRENT     5
+#define        TPS6507X_ADCONFIG_INPUT_AC_VOLTAGE      6
+#define        TPS6507X_ADCONFIG_INPUT_SYS_VOLTAGE     7
+#define        TPS6507X_ADCONFIG_INPUT_CHARGER_VOLTAGE 8
+#define        TPS6507X_ADCONFIG_INPUT_BAT_VOLTAGE     9
+#define        TPS6507X_ADCONFIG_INPUT_THRESHOLD_VOLTAGE 10
+#define        TPS6507X_ADCONFIG_INPUT_ISET1_VOLTAGE   11
+#define        TPS6507X_ADCONFIG_INPUT_ISET2_VOLTAGE   12
+#define        TPS6507X_ADCONFIG_INPUT_REAL_TSC        14
+#define        TPS6507X_ADCONFIG_INPUT_TSC             15
+
+#define        TPS6507X_REG_TSCMODE                    0X08
+#define        TPS6507X_TSCMODE_X_POSITION             0
+#define        TPS6507X_TSCMODE_Y_POSITION             1
+#define        TPS6507X_TSCMODE_PRESSURE               2
+#define        TPS6507X_TSCMODE_X_PLATE                3
+#define        TPS6507X_TSCMODE_Y_PLATE                4
+#define        TPS6507X_TSCMODE_STANDBY                5
+#define        TPS6507X_TSCMODE_ADC_INPUT              6
+#define        TPS6507X_TSCMODE_DISABLE                7
+
+#define        TPS6507X_REG_ADRESULT_1                 0X09
+
+#define        TPS6507X_REG_ADRESULT_2                 0X0A
+#define        TPS6507X_REG_ADRESULT_2_MASK            (BIT(1) | BIT(0))
+
+#define        TPS6507X_REG_PGOOD                      0X0B
+
+#define        TPS6507X_REG_PGOODMASK                  0X0C
+
+#define        TPS6507X_REG_CON_CTRL1                  0X0D
+#define        TPS6507X_CON_CTRL1_DCDC1_ENABLE         BIT(4)
+#define        TPS6507X_CON_CTRL1_DCDC2_ENABLE         BIT(3)
+#define        TPS6507X_CON_CTRL1_DCDC3_ENABLE         BIT(2)
+#define        TPS6507X_CON_CTRL1_LDO1_ENABLE          BIT(1)
+#define        TPS6507X_CON_CTRL1_LDO2_ENABLE          BIT(0)
+
+#define        TPS6507X_REG_CON_CTRL2                  0X0E
+
+#define        TPS6507X_REG_CON_CTRL3                  0X0F
+
+#define        TPS6507X_REG_DEFDCDC1                   0X10
+#define TPS6507X_DEFDCDC1_DCDC1_EXT_ADJ_EN     BIT(7)
+#define TPS6507X_DEFDCDC1_DCDC1_MASK           0X3F
+
+#define        TPS6507X_REG_DEFDCDC2_LOW               0X11
+#define TPS6507X_DEFDCDC2_LOW_DCDC2_MASK       0X3F
+
+#define        TPS6507X_REG_DEFDCDC2_HIGH              0X12
+#define TPS6507X_DEFDCDC2_HIGH_DCDC2_MASK      0X3F
+
+#define        TPS6507X_REG_DEFDCDC3_LOW               0X13
+#define TPS6507X_DEFDCDC3_LOW_DCDC3_MASK       0X3F
+
+#define        TPS6507X_REG_DEFDCDC3_HIGH              0X14
+#define TPS6507X_DEFDCDC3_HIGH_DCDC3_MASK      0X3F
+
+#define        TPS6507X_REG_DEFSLEW                    0X15
+
+#define        TPS6507X_REG_LDO_CTRL1                  0X16
+#define TPS6507X_REG_LDO_CTRL1_LDO1_MASK       0X0F
+
+#define        TPS6507X_REG_DEFLDO2                    0X17
+#define TPS6507X_REG_DEFLDO2_LDO2_MASK         0X3F
+
+#define        TPS6507X_REG_WLED_CTRL1                 0X18
+
+#define        TPS6507X_REG_WLED_CTRL2                 0X19
+
+/* VDCDC MASK */
+#define TPS6507X_DEFDCDCX_DCDC_MASK            0X3F
+
+#define TPS6507X_MAX_REGISTER                  0X19
+
+/**
+ * struct tps6507x_board - packages regulator and touchscreen init data
+ * @tps6507x_regulator_data: regulator initialization values
+ *
+ * Board data may be used to initialize regulator and touchscreen.
+ */
+
+struct tps6507x_board {
+       struct regulator_init_data *tps6507x_pmic_init_data;
+       struct touchscreen_init_data *tps6507x_ts_init_data;
+};
+
+/**
+ * struct tps6507x_dev - tps6507x sub-driver chip access routines
+ * @read_dev() - I2C register read function
+ * @write_dev() - I2C register write function
+ *
+ * Device data may be used to access the TPS6507x chip
+ */
+
+struct tps6507x_dev {
+       struct device *dev;
+       struct i2c_client *i2c_client;
+       int (*read_dev)(struct tps6507x_dev *tps6507x, char reg, int size,
+                       void *dest);
+       int (*write_dev)(struct tps6507x_dev *tps6507x, char reg, int size,
+                        void *src);
+
+       /* Client devices */
+       struct tps6507x_pmic *pmic;
+       struct tps6507x_ts *ts;
+};
+
+#endif /*  __LINUX_MFD_TPS6507X_H */
index 5915f6e..eb5bd4e 100644 (file)
@@ -256,8 +256,9 @@ struct wm831x {
        int irq_masks_cache[WM831X_NUM_IRQ_REGS]; /* Cached hardware value */
 
        /* Chip revision based flags */
-       unsigned has_gpio_ena:1;  /* Has GPIO enable bit */
-       unsigned has_cs_sts:1;    /* Has current sink status bit */
+       unsigned has_gpio_ena:1;         /* Has GPIO enable bit */
+       unsigned has_cs_sts:1;           /* Has current sink status bit */
+       unsigned charger_irq_wake:1;     /* Are charger IRQs a wake source? */
 
        int num_gpio;
 
index 8a8f1d0..dba35e4 100644 (file)
@@ -66,8 +66,6 @@
  * int num_online_nodes()              Number of online Nodes
  * int num_possible_nodes()            Number of all possible Nodes
  *
- * int node_random(mask)               Random node with set bit in mask
- *
  * int node_online(node)               Is some node online?
  * int node_possible(node)             Is some node possible?
  *
@@ -432,10 +430,6 @@ static inline void node_set_offline(int nid)
        node_clear_state(nid, N_ONLINE);
        nr_online_nodes = num_node_state(N_ONLINE);
 }
-
-#define node_random(mask) __node_random(&(mask))
-extern int __node_random(const nodemask_t *maskp);
-
 #else
 
 static inline int node_state(int node, enum node_states state)
@@ -466,8 +460,6 @@ static inline int num_node_state(enum node_states state)
 
 #define node_set_online(node)     node_set_state((node), N_ONLINE)
 #define node_set_offline(node)    node_clear_state((node), N_ONLINE)
-
-static inline int node_random(const nodemask_t mask) { return 0; }
 #endif
 
 #define node_online_map        node_states[N_ONLINE]
index a327322..6a471ab 100644 (file)
@@ -311,7 +311,8 @@ struct pci_dev {
        unsigned int    is_virtfn:1;
        unsigned int    reset_fn:1;
        unsigned int    is_hotplug_bridge:1;
-       unsigned int    aer_firmware_first:1;
+       unsigned int    __aer_firmware_first_valid:1;
+       unsigned int    __aer_firmware_first:1;
        pci_dev_flags_t dev_flags;
        atomic_t        enable_cnt;     /* pci_enable_device has been called */
 
index 3fd5c82..fb6c91e 100644 (file)
@@ -485,6 +485,7 @@ struct perf_guest_info_callbacks {
 #include <linux/ftrace.h>
 #include <linux/cpu.h>
 #include <asm/atomic.h>
+#include <asm/local.h>
 
 #define PERF_MAX_STACK_DEPTH           255
 
@@ -587,21 +588,19 @@ struct perf_mmap_data {
        struct rcu_head                 rcu_head;
 #ifdef CONFIG_PERF_USE_VMALLOC
        struct work_struct              work;
+       int                             page_order;     /* allocation order  */
 #endif
-       int                             data_order;
        int                             nr_pages;       /* nr of data pages  */
        int                             writable;       /* are we writable   */
        int                             nr_locked;      /* nr pages mlocked  */
 
        atomic_t                        poll;           /* POLL_ for wakeups */
-       atomic_t                        events;         /* event_id limit       */
 
-       atomic_long_t                   head;           /* write position    */
-       atomic_long_t                   done_head;      /* completed head    */
-
-       atomic_t                        lock;           /* concurrent writes */
-       atomic_t                        wakeup;         /* needs a wakeup    */
-       atomic_t                        lost;           /* nr records lost   */
+       local_t                         head;           /* write position    */
+       local_t                         nest;           /* nested writers    */
+       local_t                         events;         /* event limit       */
+       local_t                         wakeup;         /* wakeup stamp      */
+       local_t                         lost;           /* nr records lost   */
 
        long                            watermark;      /* wakeup watermark  */
 
@@ -728,6 +727,7 @@ struct perf_event {
        perf_overflow_handler_t         overflow_handler;
 
 #ifdef CONFIG_EVENT_TRACING
+       struct ftrace_event_call        *tp_event;
        struct event_filter             *filter;
 #endif
 
@@ -803,11 +803,12 @@ struct perf_cpu_context {
 struct perf_output_handle {
        struct perf_event               *event;
        struct perf_mmap_data           *data;
-       unsigned long                   head;
-       unsigned long                   offset;
+       unsigned long                   wakeup;
+       unsigned long                   size;
+       void                            *addr;
+       int                             page;
        int                             nmi;
        int                             sample;
-       int                             locked;
 };
 
 #ifdef CONFIG_PERF_EVENTS
@@ -993,8 +994,9 @@ static inline bool perf_paranoid_kernel(void)
 }
 
 extern void perf_event_init(void);
-extern void perf_tp_event(int event_id, u64 addr, u64 count, void *record,
-                         int entry_size, struct pt_regs *regs);
+extern void perf_tp_event(u64 addr, u64 count, void *record,
+                         int entry_size, struct pt_regs *regs,
+                         struct hlist_head *head);
 extern void perf_bp_event(struct perf_event *event, void *data);
 
 #ifndef perf_misc_flags
index 7126a15..94c1f03 100644 (file)
@@ -174,8 +174,7 @@ enum {
 #include <linux/rwsem.h>
 #include <linux/spinlock.h>
 #include <linux/wait.h>
-#include <linux/percpu.h>
-#include <linux/smp.h>
+#include <linux/percpu_counter.h>
 
 #include <linux/dqblk_xfs.h>
 #include <linux/dqblk_v1.h>
@@ -254,6 +253,7 @@ enum {
 
 struct dqstats {
        int stat[_DQST_DQSTAT_LAST];
+       struct percpu_counter counter[_DQST_DQSTAT_LAST];
 };
 
 extern struct dqstats *dqstats_pcpu;
@@ -261,20 +261,12 @@ extern struct dqstats dqstats;
 
 static inline void dqstats_inc(unsigned int type)
 {
-#ifdef CONFIG_SMP
-       per_cpu_ptr(dqstats_pcpu, smp_processor_id())->stat[type]++;
-#else
-       dqstats.stat[type]++;
-#endif
+       percpu_counter_inc(&dqstats.counter[type]);
 }
 
 static inline void dqstats_dec(unsigned int type)
 {
-#ifdef CONFIG_SMP
-       per_cpu_ptr(dqstats_pcpu, smp_processor_id())->stat[type]--;
-#else
-       dqstats.stat[type]--;
-#endif
+       percpu_counter_dec(&dqstats.counter[type]);
 }
 
 #define DQ_MOD_B       0       /* dquot modified since read */
@@ -332,8 +324,8 @@ struct dquot_operations {
 
 /* Operations handling requests from userspace */
 struct quotactl_ops {
-       int (*quota_on)(struct super_block *, int, int, char *, int);
-       int (*quota_off)(struct super_block *, int, int);
+       int (*quota_on)(struct super_block *, int, int, char *);
+       int (*quota_off)(struct super_block *, int);
        int (*quota_sync)(struct super_block *, int, int);
        int (*get_info)(struct super_block *, int, struct if_dqinfo *);
        int (*set_info)(struct super_block *, int, struct if_dqinfo *);
index e38ae53..aa36793 100644 (file)
@@ -53,6 +53,14 @@ int dquot_alloc_inode(const struct inode *inode);
 int dquot_claim_space_nodirty(struct inode *inode, qsize_t number);
 void dquot_free_inode(const struct inode *inode);
 
+int dquot_disable(struct super_block *sb, int type, unsigned int flags);
+/* Suspend quotas on remount RO */
+static inline int dquot_suspend(struct super_block *sb, int type)
+{
+       return dquot_disable(sb, type, DQUOT_SUSPENDED);
+}
+int dquot_resume(struct super_block *sb, int type);
+
 int dquot_commit(struct dquot *dquot);
 int dquot_acquire(struct dquot *dquot);
 int dquot_release(struct dquot *dquot);
@@ -61,27 +69,25 @@ int dquot_mark_dquot_dirty(struct dquot *dquot);
 
 int dquot_file_open(struct inode *inode, struct file *file);
 
-int vfs_quota_on(struct super_block *sb, int type, int format_id,
-       char *path, int remount);
-int vfs_quota_enable(struct inode *inode, int type, int format_id,
+int dquot_quota_on(struct super_block *sb, int type, int format_id,
+       char *path);
+int dquot_enable(struct inode *inode, int type, int format_id,
        unsigned int flags);
-int vfs_quota_on_path(struct super_block *sb, int type, int format_id,
+int dquot_quota_on_path(struct super_block *sb, int type, int format_id,
        struct path *path);
-int vfs_quota_on_mount(struct super_block *sb, char *qf_name,
+int dquot_quota_on_mount(struct super_block *sb, char *qf_name,
        int format_id, int type);
-int vfs_quota_off(struct super_block *sb, int type, int remount);
-int vfs_quota_disable(struct super_block *sb, int type, unsigned int flags);
-int vfs_quota_sync(struct super_block *sb, int type, int wait);
-int vfs_get_dqinfo(struct super_block *sb, int type, struct if_dqinfo *ii);
-int vfs_set_dqinfo(struct super_block *sb, int type, struct if_dqinfo *ii);
-int vfs_get_dqblk(struct super_block *sb, int type, qid_t id,
+int dquot_quota_off(struct super_block *sb, int type);
+int dquot_quota_sync(struct super_block *sb, int type, int wait);
+int dquot_get_dqinfo(struct super_block *sb, int type, struct if_dqinfo *ii);
+int dquot_set_dqinfo(struct super_block *sb, int type, struct if_dqinfo *ii);
+int dquot_get_dqblk(struct super_block *sb, int type, qid_t id,
                struct fs_disk_quota *di);
-int vfs_set_dqblk(struct super_block *sb, int type, qid_t id,
+int dquot_set_dqblk(struct super_block *sb, int type, qid_t id,
                struct fs_disk_quota *di);
 
 int __dquot_transfer(struct inode *inode, struct dquot **transfer_to);
 int dquot_transfer(struct inode *inode, struct iattr *iattr);
-int vfs_dq_quota_on_remount(struct super_block *sb);
 
 static inline struct mem_dqinfo *sb_dqinfo(struct super_block *sb, int type)
 {
@@ -148,20 +154,7 @@ static inline unsigned sb_any_quota_active(struct super_block *sb)
  * Operations supported for diskquotas.
  */
 extern const struct dquot_operations dquot_operations;
-extern const struct quotactl_ops vfs_quotactl_ops;
-
-#define sb_dquot_ops (&dquot_operations)
-#define sb_quotactl_ops (&vfs_quotactl_ops)
-
-/* Cannot be called inside a transaction */
-static inline int vfs_dq_off(struct super_block *sb, int remount)
-{
-       int ret = -ENOSYS;
-
-       if (sb->s_qcop && sb->s_qcop->quota_off)
-               ret = sb->s_qcop->quota_off(sb, -1, remount);
-       return ret;
-}
+extern const struct quotactl_ops dquot_quotactl_ops;
 
 #else
 
@@ -206,12 +199,6 @@ static inline int sb_any_quota_active(struct super_block *sb)
        return 0;
 }
 
-/*
- * NO-OP when quota not configured.
- */
-#define sb_dquot_ops                           (NULL)
-#define sb_quotactl_ops                                (NULL)
-
 static inline void dquot_initialize(struct inode *inode)
 {
 }
@@ -229,16 +216,6 @@ static inline void dquot_free_inode(const struct inode *inode)
 {
 }
 
-static inline int vfs_dq_off(struct super_block *sb, int remount)
-{
-       return 0;
-}
-
-static inline int vfs_dq_quota_on_remount(struct super_block *sb)
-{
-       return 0;
-}
-
 static inline int dquot_transfer(struct inode *inode, struct iattr *iattr)
 {
        return 0;
@@ -265,6 +242,22 @@ static inline int dquot_claim_space_nodirty(struct inode *inode, qsize_t number)
        return 0;
 }
 
+static inline int dquot_disable(struct super_block *sb, int type,
+               unsigned int flags)
+{
+       return 0;
+}
+
+static inline int dquot_suspend(struct super_block *sb, int type)
+{
+       return 0;
+}
+
+static inline int dquot_resume(struct super_block *sb, int type)
+{
+       return 0;
+}
+
 #define dquot_file_open                generic_file_open
 
 #endif /* CONFIG_QUOTA */
index 19b5f22..bd6eb0e 100644 (file)
@@ -88,11 +88,15 @@ union rio_pw_msg;
  * @swpinfo: Switch port info
  * @src_ops: Source operation capabilities
  * @dst_ops: Destination operation capabilities
+ * @comp_tag: RIO component tag
+ * @phys_efptr: RIO device extended features pointer
+ * @em_efptr: RIO Error Management features pointer
  * @dma_mask: Mask of bits of RIO address this device implements
  * @rswitch: Pointer to &struct rio_switch if valid for this device
  * @driver: Driver claiming this device
  * @dev: Device model device
  * @riores: RIO resources this device owns
+ * @pwcback: port-write callback function for this device
  * @destid: Network destination ID
  */
 struct rio_dev {
@@ -222,6 +226,8 @@ struct rio_net {
  * @add_entry: Callback for switch-specific route add function
  * @get_entry: Callback for switch-specific route get function
  * @clr_table: Callback for switch-specific clear route table function
+ * @set_domain: Callback for switch-specific domain setting function
+ * @get_domain: Callback for switch-specific domain get function
  * @em_init: Callback for switch-specific error management initialization function
  * @em_handle: Callback for switch-specific error management handler function
  */
index 057929b..a1a86a5 100644 (file)
@@ -103,22 +103,6 @@ struct perf_event_attr;
 #define __SC_TEST5(t5, a5, ...)        __SC_TEST(t5); __SC_TEST4(__VA_ARGS__)
 #define __SC_TEST6(t6, a6, ...)        __SC_TEST(t6); __SC_TEST5(__VA_ARGS__)
 
-#ifdef CONFIG_PERF_EVENTS
-
-#define TRACE_SYS_ENTER_PERF_INIT(sname)                                      \
-       .perf_event_enable = perf_sysenter_enable,                             \
-       .perf_event_disable = perf_sysenter_disable,
-
-#define TRACE_SYS_EXIT_PERF_INIT(sname)                                               \
-       .perf_event_enable = perf_sysexit_enable,                              \
-       .perf_event_disable = perf_sysexit_disable,
-#else
-#define TRACE_SYS_ENTER_PERF(sname)
-#define TRACE_SYS_ENTER_PERF_INIT(sname)
-#define TRACE_SYS_EXIT_PERF(sname)
-#define TRACE_SYS_EXIT_PERF_INIT(sname)
-#endif /* CONFIG_PERF_EVENTS */
-
 #ifdef CONFIG_FTRACE_SYSCALLS
 #define __SC_STR_ADECL1(t, a)          #a
 #define __SC_STR_ADECL2(t, a, ...)     #a, __SC_STR_ADECL1(__VA_ARGS__)
@@ -134,54 +118,43 @@ struct perf_event_attr;
 #define __SC_STR_TDECL5(t, a, ...)     #t, __SC_STR_TDECL4(__VA_ARGS__)
 #define __SC_STR_TDECL6(t, a, ...)     #t, __SC_STR_TDECL5(__VA_ARGS__)
 
+extern struct ftrace_event_class event_class_syscall_enter;
+extern struct ftrace_event_class event_class_syscall_exit;
+extern struct trace_event_functions enter_syscall_print_funcs;
+extern struct trace_event_functions exit_syscall_print_funcs;
+
 #define SYSCALL_TRACE_ENTER_EVENT(sname)                               \
-       static const struct syscall_metadata __syscall_meta_##sname;    \
+       static struct syscall_metadata __syscall_meta_##sname;          \
        static struct ftrace_event_call                                 \
        __attribute__((__aligned__(4))) event_enter_##sname;            \
-       static struct trace_event enter_syscall_print_##sname = {       \
-               .trace                  = print_syscall_enter,          \
-       };                                                              \
        static struct ftrace_event_call __used                          \
          __attribute__((__aligned__(4)))                               \
          __attribute__((section("_ftrace_events")))                    \
          event_enter_##sname = {                                       \
                .name                   = "sys_enter"#sname,            \
-               .system                 = "syscalls",                   \
-               .event                  = &enter_syscall_print_##sname, \
-               .raw_init               = init_syscall_trace,           \
-               .define_fields          = syscall_enter_define_fields,  \
-               .regfunc                = reg_event_syscall_enter,      \
-               .unregfunc              = unreg_event_syscall_enter,    \
+               .class                  = &event_class_syscall_enter,   \
+               .event.funcs            = &enter_syscall_print_funcs,   \
                .data                   = (void *)&__syscall_meta_##sname,\
-               TRACE_SYS_ENTER_PERF_INIT(sname)                        \
        }
 
 #define SYSCALL_TRACE_EXIT_EVENT(sname)                                        \
-       static const struct syscall_metadata __syscall_meta_##sname;    \
+       static struct syscall_metadata __syscall_meta_##sname;          \
        static struct ftrace_event_call                                 \
        __attribute__((__aligned__(4))) event_exit_##sname;             \
-       static struct trace_event exit_syscall_print_##sname = {        \
-               .trace                  = print_syscall_exit,           \
-       };                                                              \
        static struct ftrace_event_call __used                          \
          __attribute__((__aligned__(4)))                               \
          __attribute__((section("_ftrace_events")))                    \
          event_exit_##sname = {                                        \
                .name                   = "sys_exit"#sname,             \
-               .system                 = "syscalls",                   \
-               .event                  = &exit_syscall_print_##sname,  \
-               .raw_init               = init_syscall_trace,           \
-               .define_fields          = syscall_exit_define_fields,   \
-               .regfunc                = reg_event_syscall_exit,       \
-               .unregfunc              = unreg_event_syscall_exit,     \
+               .class                  = &event_class_syscall_exit,    \
+               .event.funcs            = &exit_syscall_print_funcs,    \
                .data                   = (void *)&__syscall_meta_##sname,\
-               TRACE_SYS_EXIT_PERF_INIT(sname)                 \
        }
 
 #define SYSCALL_METADATA(sname, nb)                            \
        SYSCALL_TRACE_ENTER_EVENT(sname);                       \
        SYSCALL_TRACE_EXIT_EVENT(sname);                        \
-       static const struct syscall_metadata __used             \
+       static struct syscall_metadata __used                   \
          __attribute__((__aligned__(4)))                       \
          __attribute__((section("__syscalls_metadata")))       \
          __syscall_meta_##sname = {                            \
@@ -191,12 +164,14 @@ struct perf_event_attr;
                .args           = args_##sname,                 \
                .enter_event    = &event_enter_##sname,         \
                .exit_event     = &event_exit_##sname,          \
+               .enter_fields   = LIST_HEAD_INIT(__syscall_meta_##sname.enter_fields), \
+               .exit_fields    = LIST_HEAD_INIT(__syscall_meta_##sname.exit_fields), \
        };
 
 #define SYSCALL_DEFINE0(sname)                                 \
        SYSCALL_TRACE_ENTER_EVENT(_##sname);                    \
        SYSCALL_TRACE_EXIT_EVENT(_##sname);                     \
-       static const struct syscall_metadata __used             \
+       static struct syscall_metadata __used                   \
          __attribute__((__aligned__(4)))                       \
          __attribute__((section("__syscalls_metadata")))       \
          __syscall_meta__##sname = {                           \
@@ -204,6 +179,8 @@ struct perf_event_attr;
                .nb_args        = 0,                            \
                .enter_event    = &event_enter__##sname,        \
                .exit_event     = &event_exit__##sname,         \
+               .enter_fields   = LIST_HEAD_INIT(__syscall_meta__##sname.enter_fields), \
+               .exit_fields    = LIST_HEAD_INIT(__syscall_meta__##sname.exit_fields), \
        };                                                      \
        asmlinkage long sys_##sname(void)
 #else
index 1d85f9a..9a59d1f 100644 (file)
 struct module;
 struct tracepoint;
 
+struct tracepoint_func {
+       void *func;
+       void *data;
+};
+
 struct tracepoint {
        const char *name;               /* Tracepoint name */
        int state;                      /* State. */
        void (*regfunc)(void);
        void (*unregfunc)(void);
-       void **funcs;
+       struct tracepoint_func *funcs;
 } __attribute__((aligned(32)));                /*
                                         * Aligned on 32 bytes because it is
                                         * globally visible and gcc happily
@@ -37,16 +42,19 @@ struct tracepoint {
  * Connect a probe to a tracepoint.
  * Internal API, should not be used directly.
  */
-extern int tracepoint_probe_register(const char *name, void *probe);
+extern int tracepoint_probe_register(const char *name, void *probe, void *data);
 
 /*
  * Disconnect a probe from a tracepoint.
  * Internal API, should not be used directly.
  */
-extern int tracepoint_probe_unregister(const char *name, void *probe);
+extern int
+tracepoint_probe_unregister(const char *name, void *probe, void *data);
 
-extern int tracepoint_probe_register_noupdate(const char *name, void *probe);
-extern int tracepoint_probe_unregister_noupdate(const char *name, void *probe);
+extern int tracepoint_probe_register_noupdate(const char *name, void *probe,
+                                             void *data);
+extern int tracepoint_probe_unregister_noupdate(const char *name, void *probe,
+                                               void *data);
 extern void tracepoint_probe_update_all(void);
 
 struct tracepoint_iter {
@@ -102,17 +110,27 @@ static inline void tracepoint_update_probe_range(struct tracepoint *begin,
 /*
  * it_func[0] is never NULL because there is at least one element in the array
  * when the array itself is non NULL.
+ *
+ * Note, the proto and args passed in includes "__data" as the first parameter.
+ * The reason for this is to handle the "void" prototype. If a tracepoint
+ * has a "void" prototype, then it is invalid to declare a function
+ * as "(void *, void)". The DECLARE_TRACE_NOARGS() will pass in just
+ * "void *data", where as the DECLARE_TRACE() will pass in "void *data, proto".
  */
 #define __DO_TRACE(tp, proto, args)                                    \
        do {                                                            \
-               void **it_func;                                         \
+               struct tracepoint_func *it_func_ptr;                    \
+               void *it_func;                                          \
+               void *__data;                                           \
                                                                        \
                rcu_read_lock_sched_notrace();                          \
-               it_func = rcu_dereference_sched((tp)->funcs);           \
-               if (it_func) {                                          \
+               it_func_ptr = rcu_dereference_sched((tp)->funcs);       \
+               if (it_func_ptr) {                                      \
                        do {                                            \
-                               ((void(*)(proto))(*it_func))(args);     \
-                       } while (*(++it_func));                         \
+                               it_func = (it_func_ptr)->func;          \
+                               __data = (it_func_ptr)->data;           \
+                               ((void(*)(proto))(it_func))(args);      \
+                       } while ((++it_func_ptr)->func);                \
                }                                                       \
                rcu_read_unlock_sched_notrace();                        \
        } while (0)
@@ -122,24 +140,32 @@ static inline void tracepoint_update_probe_range(struct tracepoint *begin,
  * not add unwanted padding between the beginning of the section and the
  * structure. Force alignment to the same alignment as the section start.
  */
-#define DECLARE_TRACE(name, proto, args)                               \
+#define __DECLARE_TRACE(name, proto, args, data_proto, data_args)      \
        extern struct tracepoint __tracepoint_##name;                   \
        static inline void trace_##name(proto)                          \
        {                                                               \
                if (unlikely(__tracepoint_##name.state))                \
                        __DO_TRACE(&__tracepoint_##name,                \
-                               TP_PROTO(proto), TP_ARGS(args));        \
+                               TP_PROTO(data_proto),                   \
+                               TP_ARGS(data_args));                    \
+       }                                                               \
+       static inline int                                               \
+       register_trace_##name(void (*probe)(data_proto), void *data)    \
+       {                                                               \
+               return tracepoint_probe_register(#name, (void *)probe,  \
+                                                data);                 \
        }                                                               \
-       static inline int register_trace_##name(void (*probe)(proto))   \
+       static inline int                                               \
+       unregister_trace_##name(void (*probe)(data_proto), void *data)  \
        {                                                               \
-               return tracepoint_probe_register(#name, (void *)probe); \
+               return tracepoint_probe_unregister(#name, (void *)probe, \
+                                                  data);               \
        }                                                               \
-       static inline int unregister_trace_##name(void (*probe)(proto)) \
+       static inline void                                              \
+       check_trace_callback_type_##name(void (*cb)(data_proto))        \
        {                                                               \
-               return tracepoint_probe_unregister(#name, (void *)probe);\
        }
 
-
 #define DEFINE_TRACE_FN(name, reg, unreg)                              \
        static const char __tpstrtab_##name[]                           \
        __attribute__((section("__tracepoints_strings"))) = #name;      \
@@ -156,18 +182,23 @@ static inline void tracepoint_update_probe_range(struct tracepoint *begin,
        EXPORT_SYMBOL(__tracepoint_##name)
 
 #else /* !CONFIG_TRACEPOINTS */
-#define DECLARE_TRACE(name, proto, args)                               \
-       static inline void _do_trace_##name(struct tracepoint *tp, proto) \
-       { }                                                             \
+#define __DECLARE_TRACE(name, proto, args, data_proto, data_args)      \
        static inline void trace_##name(proto)                          \
        { }                                                             \
-       static inline int register_trace_##name(void (*probe)(proto))   \
+       static inline int                                               \
+       register_trace_##name(void (*probe)(data_proto),                \
+                             void *data)                               \
        {                                                               \
                return -ENOSYS;                                         \
        }                                                               \
-       static inline int unregister_trace_##name(void (*probe)(proto)) \
+       static inline int                                               \
+       unregister_trace_##name(void (*probe)(data_proto),              \
+                               void *data)                             \
        {                                                               \
                return -ENOSYS;                                         \
+       }                                                               \
+       static inline void check_trace_callback_type_##name(void (*cb)(data_proto)) \
+       {                                                               \
        }
 
 #define DEFINE_TRACE_FN(name, reg, unreg)
@@ -176,6 +207,29 @@ static inline void tracepoint_update_probe_range(struct tracepoint *begin,
 #define EXPORT_TRACEPOINT_SYMBOL(name)
 
 #endif /* CONFIG_TRACEPOINTS */
+
+/*
+ * The need for the DECLARE_TRACE_NOARGS() is to handle the prototype
+ * (void). "void" is a special value in a function prototype and can
+ * not be combined with other arguments. Since the DECLARE_TRACE()
+ * macro adds a data element at the beginning of the prototype,
+ * we need a way to differentiate "(void *data, proto)" from
+ * "(void *data, void)". The second prototype is invalid.
+ *
+ * DECLARE_TRACE_NOARGS() passes "void" as the tracepoint prototype
+ * and "void *__data" as the callback prototype.
+ *
+ * DECLARE_TRACE() passes "proto" as the tracepoint protoype and
+ * "void *__data, proto" as the callback prototype.
+ */
+#define DECLARE_TRACE_NOARGS(name)                                     \
+               __DECLARE_TRACE(name, void, , void *__data, __data)
+
+#define DECLARE_TRACE(name, proto, args)                               \
+               __DECLARE_TRACE(name, PARAMS(proto), PARAMS(args),      \
+                               PARAMS(void *__data, proto),            \
+                               PARAMS(__data, args))
+
 #endif /* DECLARE_TRACE */
 
 #ifndef TRACE_EVENT
index 2389f93..92f1d99 100644 (file)
@@ -105,6 +105,22 @@ struct uac_as_header_descriptor_v2 {
        __u8 iChannelNames;
 } __attribute__((packed));
 
+/* 4.10.1.2 Class-Specific AS Isochronous Audio Data Endpoint Descriptor */
+
+struct uac2_iso_endpoint_descriptor {
+       __u8  bLength;                  /* in bytes: 8 */
+       __u8  bDescriptorType;          /* USB_DT_CS_ENDPOINT */
+       __u8  bDescriptorSubtype;       /* EP_GENERAL */
+       __u8  bmAttributes;
+       __u8  bmControls;
+       __u8  bLockDelayUnits;
+       __le16 wLockDelay;
+} __attribute__((packed));
+
+#define UAC2_CONTROL_PITCH             (3 << 0)
+#define UAC2_CONTROL_DATA_OVERRUN      (3 << 2)
+#define UAC2_CONTROL_DATA_UNDERRUN     (3 << 4)
+
 /* 6.1 Interrupt Data Message */
 
 #define UAC2_INTERRUPT_DATA_MSG_VENDOR (1 << 0)
diff --git a/include/linux/uuid.h b/include/linux/uuid.h
new file mode 100644 (file)
index 0000000..5b7efbf
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+ * UUID/GUID definition
+ *
+ * Copyright (C) 2010, Intel Corp.
+ *     Huang Ying <ying.huang@intel.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License version
+ * 2 as published by the Free Software Foundation;
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef _LINUX_UUID_H_
+#define _LINUX_UUID_H_
+
+#include <linux/types.h>
+#include <linux/string.h>
+
+typedef struct {
+       __u8 b[16];
+} uuid_le;
+
+typedef struct {
+       __u8 b[16];
+} uuid_be;
+
+#define UUID_LE(a, b, c, d0, d1, d2, d3, d4, d5, d6, d7)               \
+((uuid_le)                                                             \
+{{ (a) & 0xff, ((a) >> 8) & 0xff, ((a) >> 16) & 0xff, ((a) >> 24) & 0xff, \
+   (b) & 0xff, ((b) >> 8) & 0xff,                                      \
+   (c) & 0xff, ((c) >> 8) & 0xff,                                      \
+   (d0), (d1), (d2), (d3), (d4), (d5), (d6), (d7) }})
+
+#define UUID_BE(a, b, c, d0, d1, d2, d3, d4, d5, d6, d7)               \
+((uuid_be)                                                             \
+{{ ((a) >> 24) & 0xff, ((a) >> 16) & 0xff, ((a) >> 8) & 0xff, (a) & 0xff, \
+   ((b) >> 8) & 0xff, (b) & 0xff,                                      \
+   ((c) >> 8) & 0xff, (c) & 0xff,                                      \
+   (d0), (d1), (d2), (d3), (d4), (d5), (d6), (d7) }})
+
+#define NULL_UUID_LE                                                   \
+       UUID_LE(0x00000000, 0x0000, 0x0000, 0x00, 0x00, 0x00, 0x00,     \
+               0x00, 0x00, 0x00, 0x00)
+
+#define NULL_UUID_BE                                                   \
+       UUID_BE(0x00000000, 0x0000, 0x0000, 0x00, 0x00, 0x00, 0x00,     \
+               0x00, 0x00, 0x00, 0x00)
+
+static inline int uuid_le_cmp(const uuid_le u1, const uuid_le u2)
+{
+       return memcmp(&u1, &u2, sizeof(uuid_le));
+}
+
+static inline int uuid_be_cmp(const uuid_be u1, const uuid_be u2)
+{
+       return memcmp(&u1, &u2, sizeof(uuid_be));
+}
+
+extern void uuid_le_gen(uuid_le *u);
+extern void uuid_be_gen(uuid_be *u);
+
+#endif
index 6cf4486..726cc35 100644 (file)
@@ -39,7 +39,7 @@ extern int net_cls_subsys_id;
 static inline u32 task_cls_classid(struct task_struct *p)
 {
        int id;
-       u32 classid;
+       u32 classid = 0;
 
        if (in_interrupt())
                return 0;
index 6173c61..4b86011 100644 (file)
@@ -876,7 +876,7 @@ struct sctp_transport {
 
        /* Reference counting. */
        atomic_t refcnt;
-       int      dead:1,
+       __u32    dead:1,
                /* RTO-Pending : A flag used to track if one of the DATA
                 *              chunks sent to this address is currently being
                 *              used to compute a RTT. If this flag is 0,
index d2a71b0..ca241ea 100644 (file)
@@ -1026,15 +1026,23 @@ extern void release_sock(struct sock *sk);
                                SINGLE_DEPTH_NESTING)
 #define bh_unlock_sock(__sk)   spin_unlock(&((__sk)->sk_lock.slock))
 
-static inline void lock_sock_bh(struct sock *sk)
+extern bool lock_sock_fast(struct sock *sk);
+/**
+ * unlock_sock_fast - complement of lock_sock_fast
+ * @sk: socket
+ * @slow: slow mode
+ *
+ * fast unlock socket for user context.
+ * If slow mode is on, we call regular release_sock()
+ */
+static inline void unlock_sock_fast(struct sock *sk, bool slow)
 {
-       spin_lock_bh(&sk->sk_lock.slock);
+       if (slow)
+               release_sock(sk);
+       else
+               spin_unlock_bh(&sk->sk_lock.slock);
 }
 
-static inline void unlock_sock_bh(struct sock *sk)
-{
-       spin_unlock_bh(&sk->sk_lock.slock);
-}
 
 extern struct sock             *sk_alloc(struct net *net, int family,
                                          gfp_t priority,
index 5d60ad4..f5b1ba9 100644 (file)
@@ -606,9 +606,9 @@ TRACE_EVENT(ext4_free_blocks,
 );
 
 TRACE_EVENT(ext4_sync_file,
-       TP_PROTO(struct file *file, struct dentry *dentry, int datasync),
+       TP_PROTO(struct file *file, int datasync),
 
-       TP_ARGS(file, dentry, datasync),
+       TP_ARGS(file, datasync),
 
        TP_STRUCT__entry(
                __field(        dev_t,  dev                     )
@@ -618,6 +618,8 @@ TRACE_EVENT(ext4_sync_file,
        ),
 
        TP_fast_assign(
+               struct dentry *dentry = file->f_path.dentry;
+
                __entry->dev            = dentry->d_inode->i_sb->s_dev;
                __entry->ino            = dentry->d_inode->i_ino;
                __entry->datasync       = datasync;
index 88c59c1..3d685d1 100644 (file)
                struct trace_entry      ent;                            \
                tstruct                                                 \
                char                    __data[0];                      \
-       };
+       };                                                              \
+                                                                       \
+       static struct ftrace_event_class event_class_##name;
+
 #undef DEFINE_EVENT
 #define DEFINE_EVENT(template, name, proto, args)      \
-       static struct ftrace_event_call                 \
+       static struct ftrace_event_call __used          \
        __attribute__((__aligned__(4))) event_##name
 
 #undef DEFINE_EVENT_PRINT
  *
  *     entry = iter->ent;
  *
- *     if (entry->type != event_<call>.id) {
+ *     if (entry->type != event_<call>->event.type) {
  *             WARN_ON_ONCE(1);
  *             return TRACE_TYPE_UNHANDLED;
  *     }
 #undef DECLARE_EVENT_CLASS
 #define DECLARE_EVENT_CLASS(call, proto, args, tstruct, assign, print) \
 static notrace enum print_line_t                                       \
-ftrace_raw_output_id_##call(int event_id, const char *name,            \
-                           struct trace_iterator *iter, int flags)     \
+ftrace_raw_output_##call(struct trace_iterator *iter, int flags,       \
+                        struct trace_event *trace_event)               \
 {                                                                      \
+       struct ftrace_event_call *event;                                \
        struct trace_seq *s = &iter->seq;                               \
        struct ftrace_raw_##call *field;                                \
        struct trace_entry *entry;                                      \
        struct trace_seq *p;                                            \
        int ret;                                                        \
                                                                        \
+       event = container_of(trace_event, struct ftrace_event_call,     \
+                            event);                                    \
+                                                                       \
        entry = iter->ent;                                              \
                                                                        \
-       if (entry->type != event_id) {                                  \
+       if (entry->type != event->event.type) {                         \
                WARN_ON_ONCE(1);                                        \
                return TRACE_TYPE_UNHANDLED;                            \
        }                                                               \
@@ -226,7 +233,7 @@ ftrace_raw_output_id_##call(int event_id, const char *name,         \
                                                                        \
        p = &get_cpu_var(ftrace_event_seq);                             \
        trace_seq_init(p);                                              \
-       ret = trace_seq_printf(s, "%s: ", name);                        \
+       ret = trace_seq_printf(s, "%s: ", event->name);                 \
        if (ret)                                                        \
                ret = trace_seq_printf(s, print);                       \
        put_cpu();                                                      \
@@ -234,21 +241,16 @@ ftrace_raw_output_id_##call(int event_id, const char *name,               \
                return TRACE_TYPE_PARTIAL_LINE;                         \
                                                                        \
        return TRACE_TYPE_HANDLED;                                      \
-}
-
-#undef DEFINE_EVENT
-#define DEFINE_EVENT(template, name, proto, args)                      \
-static notrace enum print_line_t                                       \
-ftrace_raw_output_##name(struct trace_iterator *iter, int flags)       \
-{                                                                      \
-       return ftrace_raw_output_id_##template(event_##name.id,         \
-                                              #name, iter, flags);     \
-}
+}                                                                      \
+static struct trace_event_functions ftrace_event_type_funcs_##call = { \
+       .trace                  = ftrace_raw_output_##call,             \
+};
 
 #undef DEFINE_EVENT_PRINT
 #define DEFINE_EVENT_PRINT(template, call, proto, args, print)         \
 static notrace enum print_line_t                                       \
-ftrace_raw_output_##call(struct trace_iterator *iter, int flags)       \
+ftrace_raw_output_##call(struct trace_iterator *iter, int flags,       \
+                        struct trace_event *event)                     \
 {                                                                      \
        struct trace_seq *s = &iter->seq;                               \
        struct ftrace_raw_##template *field;                            \
@@ -258,7 +260,7 @@ ftrace_raw_output_##call(struct trace_iterator *iter, int flags)    \
                                                                        \
        entry = iter->ent;                                              \
                                                                        \
-       if (entry->type != event_##call.id) {                           \
+       if (entry->type != event_##call.event.type) {                   \
                WARN_ON_ONCE(1);                                        \
                return TRACE_TYPE_UNHANDLED;                            \
        }                                                               \
@@ -275,7 +277,10 @@ ftrace_raw_output_##call(struct trace_iterator *iter, int flags)   \
                return TRACE_TYPE_PARTIAL_LINE;                         \
                                                                        \
        return TRACE_TYPE_HANDLED;                                      \
-}
+}                                                                      \
+static struct trace_event_functions ftrace_event_type_funcs_##call = { \
+       .trace                  = ftrace_raw_output_##call,             \
+};
 
 #include TRACE_INCLUDE(TRACE_INCLUDE_FILE)
 
@@ -381,80 +386,18 @@ static inline notrace int ftrace_get_offsets_##call(                      \
 
 #include TRACE_INCLUDE(TRACE_INCLUDE_FILE)
 
-#ifdef CONFIG_PERF_EVENTS
-
-/*
- * Generate the functions needed for tracepoint perf_event support.
- *
- * NOTE: The insertion profile callback (ftrace_profile_<call>) is defined later
- *
- * static int ftrace_profile_enable_<call>(void)
- * {
- *     return register_trace_<call>(ftrace_profile_<call>);
- * }
- *
- * static void ftrace_profile_disable_<call>(void)
- * {
- *     unregister_trace_<call>(ftrace_profile_<call>);
- * }
- *
- */
-
-#undef DECLARE_EVENT_CLASS
-#define DECLARE_EVENT_CLASS(call, proto, args, tstruct, assign, print)
-
-#undef DEFINE_EVENT
-#define DEFINE_EVENT(template, name, proto, args)                      \
-                                                                       \
-static void perf_trace_##name(proto);                                  \
-                                                                       \
-static notrace int                                                     \
-perf_trace_enable_##name(struct ftrace_event_call *unused)             \
-{                                                                      \
-       return register_trace_##name(perf_trace_##name);                \
-}                                                                      \
-                                                                       \
-static notrace void                                                    \
-perf_trace_disable_##name(struct ftrace_event_call *unused)            \
-{                                                                      \
-       unregister_trace_##name(perf_trace_##name);                     \
-}
-
-#undef DEFINE_EVENT_PRINT
-#define DEFINE_EVENT_PRINT(template, name, proto, args, print) \
-       DEFINE_EVENT(template, name, PARAMS(proto), PARAMS(args))
-
-#include TRACE_INCLUDE(TRACE_INCLUDE_FILE)
-
-#endif /* CONFIG_PERF_EVENTS */
-
 /*
  * Stage 4 of the trace events.
  *
  * Override the macros in <trace/trace_events.h> to include the following:
  *
- * static void ftrace_event_<call>(proto)
- * {
- *     event_trace_printk(_RET_IP_, "<call>: " <fmt>);
- * }
- *
- * static int ftrace_reg_event_<call>(struct ftrace_event_call *unused)
- * {
- *     return register_trace_<call>(ftrace_event_<call>);
- * }
- *
- * static void ftrace_unreg_event_<call>(struct ftrace_event_call *unused)
- * {
- *     unregister_trace_<call>(ftrace_event_<call>);
- * }
- *
- *
  * For those macros defined with TRACE_EVENT:
  *
  * static struct ftrace_event_call event_<call>;
  *
- * static void ftrace_raw_event_<call>(proto)
+ * static void ftrace_raw_event_<call>(void *__data, proto)
  * {
+ *     struct ftrace_event_call *event_call = __data;
  *     struct ftrace_data_offsets_<call> __maybe_unused __data_offsets;
  *     struct ring_buffer_event *event;
  *     struct ftrace_raw_<call> *entry; <-- defined in stage 1
@@ -469,7 +412,7 @@ perf_trace_disable_##name(struct ftrace_event_call *unused)         \
  *     __data_size = ftrace_get_offsets_<call>(&__data_offsets, args);
  *
  *     event = trace_current_buffer_lock_reserve(&buffer,
- *                               event_<call>.id,
+ *                               event_<call>->event.type,
  *                               sizeof(*entry) + __data_size,
  *                               irq_flags, pc);
  *     if (!event)
@@ -484,43 +427,42 @@ perf_trace_disable_##name(struct ftrace_event_call *unused)               \
  *                                                event, irq_flags, pc);
  * }
  *
- * static int ftrace_raw_reg_event_<call>(struct ftrace_event_call *unused)
- * {
- *     return register_trace_<call>(ftrace_raw_event_<call>);
- * }
- *
- * static void ftrace_unreg_event_<call>(struct ftrace_event_call *unused)
- * {
- *     unregister_trace_<call>(ftrace_raw_event_<call>);
- * }
- *
  * static struct trace_event ftrace_event_type_<call> = {
  *     .trace                  = ftrace_raw_output_<call>, <-- stage 2
  * };
  *
  * static const char print_fmt_<call>[] = <TP_printk>;
  *
+ * static struct ftrace_event_class __used event_class_<template> = {
+ *     .system                 = "<system>",
+ *     .define_fields          = ftrace_define_fields_<call>,
+ *     .fields                 = LIST_HEAD_INIT(event_class_##call.fields),
+ *     .raw_init               = trace_event_raw_init,
+ *     .probe                  = ftrace_raw_event_##call,
+ * };
+ *
  * static struct ftrace_event_call __used
  * __attribute__((__aligned__(4)))
  * __attribute__((section("_ftrace_events"))) event_<call> = {
  *     .name                   = "<call>",
- *     .system                 = "<system>",
- *     .raw_init               = trace_event_raw_init,
- *     .regfunc                = ftrace_reg_event_<call>,
- *     .unregfunc              = ftrace_unreg_event_<call>,
+ *     .class                  = event_class_<template>,
+ *     .event                  = &ftrace_event_type_<call>,
  *     .print_fmt              = print_fmt_<call>,
- *     .define_fields          = ftrace_define_fields_<call>,
- * }
+ * };
  *
  */
 
 #ifdef CONFIG_PERF_EVENTS
 
+#define _TRACE_PERF_PROTO(call, proto)                                 \
+       static notrace void                                             \
+       perf_trace_##call(void *__data, proto);
+
 #define _TRACE_PERF_INIT(call)                                         \
-       .perf_event_enable = perf_trace_enable_##call,                  \
-       .perf_event_disable = perf_trace_disable_##call,
+       .perf_probe             = perf_trace_##call,
 
 #else
+#define _TRACE_PERF_PROTO(call, proto)
 #define _TRACE_PERF_INIT(call)
 #endif /* CONFIG_PERF_EVENTS */
 
@@ -554,9 +496,9 @@ perf_trace_disable_##name(struct ftrace_event_call *unused)         \
 #define DECLARE_EVENT_CLASS(call, proto, args, tstruct, assign, print) \
                                                                        \
 static notrace void                                                    \
-ftrace_raw_event_id_##call(struct ftrace_event_call *event_call,       \
-                                      proto)                           \
+ftrace_raw_event_##call(void *__data, proto)                           \
 {                                                                      \
+       struct ftrace_event_call *event_call = __data;                  \
        struct ftrace_data_offsets_##call __maybe_unused __data_offsets;\
        struct ring_buffer_event *event;                                \
        struct ftrace_raw_##call *entry;                                \
@@ -571,7 +513,7 @@ ftrace_raw_event_id_##call(struct ftrace_event_call *event_call,    \
        __data_size = ftrace_get_offsets_##call(&__data_offsets, args); \
                                                                        \
        event = trace_current_buffer_lock_reserve(&buffer,              \
-                                event_call->id,                        \
+                                event_call->event.type,                \
                                 sizeof(*entry) + __data_size,          \
                                 irq_flags, pc);                        \
        if (!event)                                                     \
@@ -586,34 +528,21 @@ ftrace_raw_event_id_##call(struct ftrace_event_call *event_call,  \
                trace_nowake_buffer_unlock_commit(buffer,               \
                                                  event, irq_flags, pc); \
 }
+/*
+ * The ftrace_test_probe is compiled out, it is only here as a build time check
+ * to make sure that if the tracepoint handling changes, the ftrace probe will
+ * fail to compile unless it too is updated.
+ */
 
 #undef DEFINE_EVENT
 #define DEFINE_EVENT(template, call, proto, args)                      \
-                                                                       \
-static notrace void ftrace_raw_event_##call(proto)                     \
-{                                                                      \
-       ftrace_raw_event_id_##template(&event_##call, args);            \
-}                                                                      \
-                                                                       \
-static notrace int                                                     \
-ftrace_raw_reg_event_##call(struct ftrace_event_call *unused)          \
+static inline void ftrace_test_probe_##call(void)                      \
 {                                                                      \
-       return register_trace_##call(ftrace_raw_event_##call);          \
-}                                                                      \
-                                                                       \
-static notrace void                                                    \
-ftrace_raw_unreg_event_##call(struct ftrace_event_call *unused)                \
-{                                                                      \
-       unregister_trace_##call(ftrace_raw_event_##call);               \
-}                                                                      \
-                                                                       \
-static struct trace_event ftrace_event_type_##call = {                 \
-       .trace                  = ftrace_raw_output_##call,             \
-};
+       check_trace_callback_type_##call(ftrace_raw_event_##template);  \
+}
 
 #undef DEFINE_EVENT_PRINT
-#define DEFINE_EVENT_PRINT(template, name, proto, args, print) \
-       DEFINE_EVENT(template, name, PARAMS(proto), PARAMS(args))
+#define DEFINE_EVENT_PRINT(template, name, proto, args, print)
 
 #include TRACE_INCLUDE(TRACE_INCLUDE_FILE)
 
@@ -630,7 +559,16 @@ static struct trace_event ftrace_event_type_##call = {                     \
 
 #undef DECLARE_EVENT_CLASS
 #define DECLARE_EVENT_CLASS(call, proto, args, tstruct, assign, print) \
-static const char print_fmt_##call[] = print;
+_TRACE_PERF_PROTO(call, PARAMS(proto));                                        \
+static const char print_fmt_##call[] = print;                          \
+static struct ftrace_event_class __used event_class_##call = {         \
+       .system                 = __stringify(TRACE_SYSTEM),            \
+       .define_fields          = ftrace_define_fields_##call,          \
+       .fields                 = LIST_HEAD_INIT(event_class_##call.fields),\
+       .raw_init               = trace_event_raw_init,                 \
+       .probe                  = ftrace_raw_event_##call,              \
+       _TRACE_PERF_INIT(call)                                          \
+};
 
 #undef DEFINE_EVENT
 #define DEFINE_EVENT(template, call, proto, args)                      \
@@ -639,15 +577,10 @@ static struct ftrace_event_call __used                                    \
 __attribute__((__aligned__(4)))                                                \
 __attribute__((section("_ftrace_events"))) event_##call = {            \
        .name                   = #call,                                \
-       .system                 = __stringify(TRACE_SYSTEM),            \
-       .event                  = &ftrace_event_type_##call,            \
-       .raw_init               = trace_event_raw_init,                 \
-       .regfunc                = ftrace_raw_reg_event_##call,          \
-       .unregfunc              = ftrace_raw_unreg_event_##call,        \
+       .class                  = &event_class_##template,              \
+       .event.funcs            = &ftrace_event_type_funcs_##template,  \
        .print_fmt              = print_fmt_##template,                 \
-       .define_fields          = ftrace_define_fields_##template,      \
-       _TRACE_PERF_INIT(call)                                  \
-}
+};
 
 #undef DEFINE_EVENT_PRINT
 #define DEFINE_EVENT_PRINT(template, call, proto, args, print)         \
@@ -658,14 +591,9 @@ static struct ftrace_event_call __used                                     \
 __attribute__((__aligned__(4)))                                                \
 __attribute__((section("_ftrace_events"))) event_##call = {            \
        .name                   = #call,                                \
-       .system                 = __stringify(TRACE_SYSTEM),            \
-       .event                  = &ftrace_event_type_##call,            \
-       .raw_init               = trace_event_raw_init,                 \
-       .regfunc                = ftrace_raw_reg_event_##call,          \
-       .unregfunc              = ftrace_raw_unreg_event_##call,        \
+       .class                  = &event_class_##template,              \
+       .event.funcs            = &ftrace_event_type_funcs_##call,      \
        .print_fmt              = print_fmt_##call,                     \
-       .define_fields          = ftrace_define_fields_##template,      \
-       _TRACE_PERF_INIT(call)                                  \
 }
 
 #include TRACE_INCLUDE(TRACE_INCLUDE_FILE)
@@ -765,17 +693,20 @@ __attribute__((section("_ftrace_events"))) event_##call = {               \
 #undef DECLARE_EVENT_CLASS
 #define DECLARE_EVENT_CLASS(call, proto, args, tstruct, assign, print) \
 static notrace void                                                    \
-perf_trace_templ_##call(struct ftrace_event_call *event_call,          \
-                       struct pt_regs *__regs, proto)                  \
+perf_trace_##call(void *__data, proto)                                 \
 {                                                                      \
+       struct ftrace_event_call *event_call = __data;                  \
        struct ftrace_data_offsets_##call __maybe_unused __data_offsets;\
        struct ftrace_raw_##call *entry;                                \
+       struct pt_regs __regs;                                          \
        u64 __addr = 0, __count = 1;                                    \
-       unsigned long irq_flags;                                        \
+       struct hlist_head *head;                                        \
        int __entry_size;                                               \
        int __data_size;                                                \
        int rctx;                                                       \
                                                                        \
+       perf_fetch_caller_regs(&__regs, 1);                             \
+                                                                       \
        __data_size = ftrace_get_offsets_##call(&__data_offsets, args); \
        __entry_size = ALIGN(__data_size + sizeof(*entry) + sizeof(u32),\
                             sizeof(u64));                              \
@@ -784,32 +715,34 @@ perf_trace_templ_##call(struct ftrace_event_call *event_call,             \
        if (WARN_ONCE(__entry_size > PERF_MAX_TRACE_SIZE,               \
                      "profile buffer not large enough"))               \
                return;                                                 \
+                                                                       \
        entry = (struct ftrace_raw_##call *)perf_trace_buf_prepare(     \
-               __entry_size, event_call->id, &rctx, &irq_flags);       \
+               __entry_size, event_call->event.type, &__regs, &rctx);  \
        if (!entry)                                                     \
                return;                                                 \
+                                                                       \
        tstruct                                                         \
                                                                        \
        { assign; }                                                     \
                                                                        \
+       head = per_cpu_ptr(event_call->perf_events, smp_processor_id());\
        perf_trace_buf_submit(entry, __entry_size, rctx, __addr,        \
-                              __count, irq_flags, __regs);             \
+               __count, &__regs, head);                                \
 }
 
+/*
+ * This part is compiled out, it is only here as a build time check
+ * to make sure that if the tracepoint handling changes, the
+ * perf probe will fail to compile unless it too is updated.
+ */
 #undef DEFINE_EVENT
 #define DEFINE_EVENT(template, call, proto, args)                      \
-static notrace void perf_trace_##call(proto)                           \
+static inline void perf_test_probe_##call(void)                                \
 {                                                                      \
-       struct ftrace_event_call *event_call = &event_##call;           \
-       struct pt_regs *__regs = &get_cpu_var(perf_trace_regs);         \
-                                                                       \
-       perf_fetch_caller_regs(__regs, 1);                              \
-                                                                       \
-       perf_trace_templ_##template(event_call, __regs, args);          \
-                                                                       \
-       put_cpu_var(perf_trace_regs);                                   \
+       check_trace_callback_type_##call(perf_trace_##template);        \
 }
 
+
 #undef DEFINE_EVENT_PRINT
 #define DEFINE_EVENT_PRINT(template, name, proto, args, print) \
        DEFINE_EVENT(template, name, PARAMS(proto), PARAMS(args))
index e5e5f48..257e089 100644 (file)
@@ -25,6 +25,8 @@ struct syscall_metadata {
        int             nb_args;
        const char      **types;
        const char      **args;
+       struct list_head enter_fields;
+       struct list_head exit_fields;
 
        struct ftrace_event_call *enter_event;
        struct ftrace_event_call *exit_event;
@@ -34,16 +36,16 @@ struct syscall_metadata {
 extern unsigned long arch_syscall_addr(int nr);
 extern int init_syscall_trace(struct ftrace_event_call *call);
 
-extern int syscall_enter_define_fields(struct ftrace_event_call *call);
-extern int syscall_exit_define_fields(struct ftrace_event_call *call);
 extern int reg_event_syscall_enter(struct ftrace_event_call *call);
 extern void unreg_event_syscall_enter(struct ftrace_event_call *call);
 extern int reg_event_syscall_exit(struct ftrace_event_call *call);
 extern void unreg_event_syscall_exit(struct ftrace_event_call *call);
 extern int
 ftrace_format_syscall(struct ftrace_event_call *call, struct trace_seq *s);
-enum print_line_t print_syscall_enter(struct trace_iterator *iter, int flags);
-enum print_line_t print_syscall_exit(struct trace_iterator *iter, int flags);
+enum print_line_t print_syscall_enter(struct trace_iterator *iter, int flags,
+                                     struct trace_event *event);
+enum print_line_t print_syscall_exit(struct trace_iterator *iter, int flags,
+                                    struct trace_event *event);
 #endif
 
 #ifdef CONFIG_PERF_EVENTS
index 1a314c8..52ed77e 100644 (file)
--- a/ipc/shm.c
+++ b/ipc/shm.c
@@ -273,16 +273,13 @@ static int shm_release(struct inode *ino, struct file *file)
        return 0;
 }
 
-static int shm_fsync(struct file *file, struct dentry *dentry, int datasync)
+static int shm_fsync(struct file *file, int datasync)
 {
-       int (*fsync) (struct file *, struct dentry *, int datasync);
        struct shm_file_data *sfd = shm_file_data(file);
-       int ret = -EINVAL;
 
-       fsync = sfd->file->f_op->fsync;
-       if (fsync)
-               ret = fsync(sfd->file, sfd->file->f_path.dentry, datasync);
-       return ret;
+       if (!sfd->file->f_op->fsync)
+               return -EINVAL;
+       return sfd->file->f_op->fsync(sfd->file, datasync);
 }
 
 static unsigned long shm_get_unmapped_area(struct file *file,
index 3097382..8b92539 100644 (file)
@@ -394,7 +394,7 @@ static cpumask_var_t frozen_cpus;
 
 int disable_nonboot_cpus(void)
 {
-       int cpu, first_cpu, error;
+       int cpu, first_cpu, error = 0;
 
        cpu_maps_update_begin();
        first_cpu = cpumask_first(cpu_online_mask);
index bf9fef6..b6cce14 100644 (file)
@@ -1086,10 +1086,6 @@ static struct task_struct *copy_process(unsigned long clone_flags,
        }
        mpol_fix_fork_child_flag(p);
 #endif
-#ifdef CONFIG_CPUSETS
-       p->cpuset_mem_spread_rotor = node_random(p->mems_allowed);
-       p->cpuset_slab_spread_rotor = node_random(p->mems_allowed);
-#endif
 #ifdef CONFIG_TRACE_IRQFLAGS
        p->irq_events = 0;
 #ifdef __ARCH_WANT_INTERRUPTS_ON_CTXSW
index b9b134b..5c69e99 100644 (file)
@@ -89,7 +89,7 @@ static void hrtimer_get_softirq_time(struct hrtimer_cpu_base *base)
 
        do {
                seq = read_seqbegin(&xtime_lock);
-               xts = current_kernel_time();
+               xts = __current_kernel_time();
                tom = wall_to_monotonic;
        } while (read_seqretry(&xtime_lock, seq));
 
index a4fa381..bd7ce8c 100644 (file)
@@ -2297,11 +2297,6 @@ unlock:
        rcu_read_unlock();
 }
 
-static unsigned long perf_data_size(struct perf_mmap_data *data)
-{
-       return data->nr_pages << (PAGE_SHIFT + data->data_order);
-}
-
 #ifndef CONFIG_PERF_USE_VMALLOC
 
 /*
@@ -2320,6 +2315,19 @@ perf_mmap_to_page(struct perf_mmap_data *data, unsigned long pgoff)
        return virt_to_page(data->data_pages[pgoff - 1]);
 }
 
+static void *perf_mmap_alloc_page(int cpu)
+{
+       struct page *page;
+       int node;
+
+       node = (cpu == -1) ? cpu : cpu_to_node(cpu);
+       page = alloc_pages_node(node, GFP_KERNEL | __GFP_ZERO, 0);
+       if (!page)
+               return NULL;
+
+       return page_address(page);
+}
+
 static struct perf_mmap_data *
 perf_mmap_data_alloc(struct perf_event *event, int nr_pages)
 {
@@ -2336,17 +2344,16 @@ perf_mmap_data_alloc(struct perf_event *event, int nr_pages)
        if (!data)
                goto fail;
 
-       data->user_page = (void *)get_zeroed_page(GFP_KERNEL);
+       data->user_page = perf_mmap_alloc_page(event->cpu);
        if (!data->user_page)
                goto fail_user_page;
 
        for (i = 0; i < nr_pages; i++) {
-               data->data_pages[i] = (void *)get_zeroed_page(GFP_KERNEL);
+               data->data_pages[i] = perf_mmap_alloc_page(event->cpu);
                if (!data->data_pages[i])
                        goto fail_data_pages;
        }
 
-       data->data_order = 0;
        data->nr_pages = nr_pages;
 
        return data;
@@ -2382,6 +2389,11 @@ static void perf_mmap_data_free(struct perf_mmap_data *data)
        kfree(data);
 }
 
+static inline int page_order(struct perf_mmap_data *data)
+{
+       return 0;
+}
+
 #else
 
 /*
@@ -2390,10 +2402,15 @@ static void perf_mmap_data_free(struct perf_mmap_data *data)
  * Required for architectures that have d-cache aliasing issues.
  */
 
+static inline int page_order(struct perf_mmap_data *data)
+{
+       return data->page_order;
+}
+
 static struct page *
 perf_mmap_to_page(struct perf_mmap_data *data, unsigned long pgoff)
 {
-       if (pgoff > (1UL << data->data_order))
+       if (pgoff > (1UL << page_order(data)))
                return NULL;
 
        return vmalloc_to_page((void *)data->user_page + pgoff * PAGE_SIZE);
@@ -2413,7 +2430,7 @@ static void perf_mmap_data_free_work(struct work_struct *work)
        int i, nr;
 
        data = container_of(work, struct perf_mmap_data, work);
-       nr = 1 << data->data_order;
+       nr = 1 << page_order(data);
 
        base = data->user_page;
        for (i = 0; i < nr + 1; i++)
@@ -2452,7 +2469,7 @@ perf_mmap_data_alloc(struct perf_event *event, int nr_pages)
 
        data->user_page = all_buf;
        data->data_pages[0] = all_buf + PAGE_SIZE;
-       data->data_order = ilog2(nr_pages);
+       data->page_order = ilog2(nr_pages);
        data->nr_pages = 1;
 
        return data;
@@ -2466,6 +2483,11 @@ fail:
 
 #endif
 
+static unsigned long perf_data_size(struct perf_mmap_data *data)
+{
+       return data->nr_pages << (PAGE_SHIFT + page_order(data));
+}
+
 static int perf_mmap_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
 {
        struct perf_event *event = vma->vm_file->private_data;
@@ -2506,8 +2528,6 @@ perf_mmap_data_init(struct perf_event *event, struct perf_mmap_data *data)
 {
        long max_size = perf_data_size(data);
 
-       atomic_set(&data->lock, -1);
-
        if (event->attr.watermark) {
                data->watermark = min_t(long, max_size,
                                        event->attr.wakeup_watermark);
@@ -2580,6 +2600,14 @@ static int perf_mmap(struct file *file, struct vm_area_struct *vma)
        long user_extra, extra;
        int ret = 0;
 
+       /*
+        * Don't allow mmap() of inherited per-task counters. This would
+        * create a performance issue due to all children writing to the
+        * same buffer.
+        */
+       if (event->cpu == -1 && event->attr.inherit)
+               return -EINVAL;
+
        if (!(vma->vm_flags & VM_SHARED))
                return -EINVAL;
 
@@ -2885,120 +2913,80 @@ static void perf_output_wakeup(struct perf_output_handle *handle)
 }
 
 /*
- * Curious locking construct.
- *
  * We need to ensure a later event_id doesn't publish a head when a former
- * event_id isn't done writing. However since we need to deal with NMIs we
+ * event isn't done writing. However since we need to deal with NMIs we
  * cannot fully serialize things.
  *
- * What we do is serialize between CPUs so we only have to deal with NMI
- * nesting on a single CPU.
- *
  * We only publish the head (and generate a wakeup) when the outer-most
- * event_id completes.
+ * event completes.
  */
-static void perf_output_lock(struct perf_output_handle *handle)
+static void perf_output_get_handle(struct perf_output_handle *handle)
 {
        struct perf_mmap_data *data = handle->data;
-       int cur, cpu = get_cpu();
-
-       handle->locked = 0;
 
-       for (;;) {
-               cur = atomic_cmpxchg(&data->lock, -1, cpu);
-               if (cur == -1) {
-                       handle->locked = 1;
-                       break;
-               }
-               if (cur == cpu)
-                       break;
-
-               cpu_relax();
-       }
+       preempt_disable();
+       local_inc(&data->nest);
+       handle->wakeup = local_read(&data->wakeup);
 }
 
-static void perf_output_unlock(struct perf_output_handle *handle)
+static void perf_output_put_handle(struct perf_output_handle *handle)
 {
        struct perf_mmap_data *data = handle->data;
        unsigned long head;
-       int cpu;
-
-       data->done_head = data->head;
-
-       if (!handle->locked)
-               goto out;
 
 again:
-       /*
-        * The xchg implies a full barrier that ensures all writes are done
-        * before we publish the new head, matched by a rmb() in userspace when
-        * reading this position.
-        */
-       while ((head = atomic_long_xchg(&data->done_head, 0)))
-               data->user_page->data_head = head;
+       head = local_read(&data->head);
 
        /*
-        * NMI can happen here, which means we can miss a done_head update.
+        * IRQ/NMI can happen here, which means we can miss a head update.
         */
 
-       cpu = atomic_xchg(&data->lock, -1);
-       WARN_ON_ONCE(cpu != smp_processor_id());
+       if (!local_dec_and_test(&data->nest))
+               goto out;
 
        /*
-        * Therefore we have to validate we did not indeed do so.
+        * Publish the known good head. Rely on the full barrier implied
+        * by atomic_dec_and_test() order the data->head read and this
+        * write.
         */
-       if (unlikely(atomic_long_read(&data->done_head))) {
-               /*
-                * Since we had it locked, we can lock it again.
-                */
-               while (atomic_cmpxchg(&data->lock, -1, cpu) != -1)
-                       cpu_relax();
+       data->user_page->data_head = head;
 
+       /*
+        * Now check if we missed an update, rely on the (compiler)
+        * barrier in atomic_dec_and_test() to re-read data->head.
+        */
+       if (unlikely(head != local_read(&data->head))) {
+               local_inc(&data->nest);
                goto again;
        }
 
-       if (atomic_xchg(&data->wakeup, 0))
+       if (handle->wakeup != local_read(&data->wakeup))
                perf_output_wakeup(handle);
-out:
-       put_cpu();
+
+ out:
+       preempt_enable();
 }
 
-void perf_output_copy(struct perf_output_handle *handle,
+__always_inline void perf_output_copy(struct perf_output_handle *handle,
                      const void *buf, unsigned int len)
 {
-       unsigned int pages_mask;
-       unsigned long offset;
-       unsigned int size;
-       void **pages;
-
-       offset          = handle->offset;
-       pages_mask      = handle->data->nr_pages - 1;
-       pages           = handle->data->data_pages;
-
        do {
-               unsigned long page_offset;
-               unsigned long page_size;
-               int nr;
+               unsigned long size = min_t(unsigned long, handle->size, len);
 
-               nr          = (offset >> PAGE_SHIFT) & pages_mask;
-               page_size   = 1UL << (handle->data->data_order + PAGE_SHIFT);
-               page_offset = offset & (page_size - 1);
-               size        = min_t(unsigned int, page_size - page_offset, len);
+               memcpy(handle->addr, buf, size);
 
-               memcpy(pages[nr] + page_offset, buf, size);
+               len -= size;
+               handle->addr += size;
+               handle->size -= size;
+               if (!handle->size) {
+                       struct perf_mmap_data *data = handle->data;
 
-               len         -= size;
-               buf         += size;
-               offset      += size;
+                       handle->page++;
+                       handle->page &= data->nr_pages - 1;
+                       handle->addr = data->data_pages[handle->page];
+                       handle->size = PAGE_SIZE << page_order(data);
+               }
        } while (len);
-
-       handle->offset = offset;
-
-       /*
-        * Check we didn't copy past our reservation window, taking the
-        * possible unsigned int wrap into account.
-        */
-       WARN_ON_ONCE(((long)(handle->head - handle->offset)) < 0);
 }
 
 int perf_output_begin(struct perf_output_handle *handle,
@@ -3036,13 +3024,13 @@ int perf_output_begin(struct perf_output_handle *handle,
        handle->sample  = sample;
 
        if (!data->nr_pages)
-               goto fail;
+               goto out;
 
-       have_lost = atomic_read(&data->lost);
+       have_lost = local_read(&data->lost);
        if (have_lost)
                size += sizeof(lost_event);
 
-       perf_output_lock(handle);
+       perf_output_get_handle(handle);
 
        do {
                /*
@@ -3052,24 +3040,28 @@ int perf_output_begin(struct perf_output_handle *handle,
                 */
                tail = ACCESS_ONCE(data->user_page->data_tail);
                smp_rmb();
-               offset = head = atomic_long_read(&data->head);
+               offset = head = local_read(&data->head);
                head += size;
                if (unlikely(!perf_output_space(data, tail, offset, head)))
                        goto fail;
-       } while (atomic_long_cmpxchg(&data->head, offset, head) != offset);
+       } while (local_cmpxchg(&data->head, offset, head) != offset);
 
-       handle->offset  = offset;
-       handle->head    = head;
+       if (head - local_read(&data->wakeup) > data->watermark)
+               local_add(data->watermark, &data->wakeup);
 
-       if (head - tail > data->watermark)
-               atomic_set(&data->wakeup, 1);
+       handle->page = offset >> (PAGE_SHIFT + page_order(data));
+       handle->page &= data->nr_pages - 1;
+       handle->size = offset & ((PAGE_SIZE << page_order(data)) - 1);
+       handle->addr = data->data_pages[handle->page];
+       handle->addr += handle->size;
+       handle->size = (PAGE_SIZE << page_order(data)) - handle->size;
 
        if (have_lost) {
                lost_event.header.type = PERF_RECORD_LOST;
                lost_event.header.misc = 0;
                lost_event.header.size = sizeof(lost_event);
                lost_event.id          = event->id;
-               lost_event.lost        = atomic_xchg(&data->lost, 0);
+               lost_event.lost        = local_xchg(&data->lost, 0);
 
                perf_output_put(handle, lost_event);
        }
@@ -3077,8 +3069,8 @@ int perf_output_begin(struct perf_output_handle *handle,
        return 0;
 
 fail:
-       atomic_inc(&data->lost);
-       perf_output_unlock(handle);
+       local_inc(&data->lost);
+       perf_output_put_handle(handle);
 out:
        rcu_read_unlock();
 
@@ -3093,14 +3085,14 @@ void perf_output_end(struct perf_output_handle *handle)
        int wakeup_events = event->attr.wakeup_events;
 
        if (handle->sample && wakeup_events) {
-               int events = atomic_inc_return(&data->events);
+               int events = local_inc_return(&data->events);
                if (events >= wakeup_events) {
-                       atomic_sub(wakeup_events, &data->events);
-                       atomic_set(&data->wakeup, 1);
+                       local_sub(wakeup_events, &data->events);
+                       local_inc(&data->wakeup);
                }
        }
 
-       perf_output_unlock(handle);
+       perf_output_put_handle(handle);
        rcu_read_unlock();
 }
 
@@ -3436,22 +3428,13 @@ static void perf_event_task_output(struct perf_event *event,
 {
        struct perf_output_handle handle;
        struct task_struct *task = task_event->task;
-       unsigned long flags;
        int size, ret;
 
-       /*
-        * If this CPU attempts to acquire an rq lock held by a CPU spinning
-        * in perf_output_lock() from interrupt context, it's game over.
-        */
-       local_irq_save(flags);
-
        size  = task_event->event_id.header.size;
        ret = perf_output_begin(&handle, event, size, 0, 0);
 
-       if (ret) {
-               local_irq_restore(flags);
+       if (ret)
                return;
-       }
 
        task_event->event_id.pid = perf_event_pid(event, task);
        task_event->event_id.ppid = perf_event_pid(event, current);
@@ -3462,7 +3445,6 @@ static void perf_event_task_output(struct perf_event *event,
        perf_output_put(&handle, task_event->event_id);
 
        perf_output_end(&handle);
-       local_irq_restore(flags);
 }
 
 static int perf_event_task_match(struct perf_event *event)
@@ -4020,9 +4002,6 @@ static void perf_swevent_add(struct perf_event *event, u64 nr,
        perf_swevent_overflow(event, 0, nmi, data, regs);
 }
 
-static int perf_tp_event_match(struct perf_event *event,
-                               struct perf_sample_data *data);
-
 static int perf_exclude_event(struct perf_event *event,
                              struct pt_regs *regs)
 {
@@ -4052,10 +4031,6 @@ static int perf_swevent_match(struct perf_event *event,
        if (perf_exclude_event(event, regs))
                return 0;
 
-       if (event->attr.type == PERF_TYPE_TRACEPOINT &&
-           !perf_tp_event_match(event, data))
-               return 0;
-
        return 1;
 }
 
@@ -4066,19 +4041,46 @@ static inline u64 swevent_hash(u64 type, u32 event_id)
        return hash_64(val, SWEVENT_HLIST_BITS);
 }
 
-static struct hlist_head *
-find_swevent_head(struct perf_cpu_context *ctx, u64 type, u32 event_id)
+static inline struct hlist_head *
+__find_swevent_head(struct swevent_hlist *hlist, u64 type, u32 event_id)
 {
-       u64 hash;
-       struct swevent_hlist *hlist;
+       u64 hash = swevent_hash(type, event_id);
 
-       hash = swevent_hash(type, event_id);
+       return &hlist->heads[hash];
+}
+
+/* For the read side: events when they trigger */
+static inline struct hlist_head *
+find_swevent_head_rcu(struct perf_cpu_context *ctx, u64 type, u32 event_id)
+{
+       struct swevent_hlist *hlist;
 
        hlist = rcu_dereference(ctx->swevent_hlist);
        if (!hlist)
                return NULL;
 
-       return &hlist->heads[hash];
+       return __find_swevent_head(hlist, type, event_id);
+}
+
+/* For the event head insertion and removal in the hlist */
+static inline struct hlist_head *
+find_swevent_head(struct perf_cpu_context *ctx, struct perf_event *event)
+{
+       struct swevent_hlist *hlist;
+       u32 event_id = event->attr.config;
+       u64 type = event->attr.type;
+
+       /*
+        * Event scheduling is always serialized against hlist allocation
+        * and release. Which makes the protected version suitable here.
+        * The context lock guarantees that.
+        */
+       hlist = rcu_dereference_protected(ctx->swevent_hlist,
+                                         lockdep_is_held(&event->ctx->lock));
+       if (!hlist)
+               return NULL;
+
+       return __find_swevent_head(hlist, type, event_id);
 }
 
 static void do_perf_sw_event(enum perf_type_id type, u32 event_id,
@@ -4095,7 +4097,7 @@ static void do_perf_sw_event(enum perf_type_id type, u32 event_id,
 
        rcu_read_lock();
 
-       head = find_swevent_head(cpuctx, type, event_id);
+       head = find_swevent_head_rcu(cpuctx, type, event_id);
 
        if (!head)
                goto end;
@@ -4110,7 +4112,7 @@ end:
 
 int perf_swevent_get_recursion_context(void)
 {
-       struct perf_cpu_context *cpuctx = &get_cpu_var(perf_cpu_context);
+       struct perf_cpu_context *cpuctx = &__get_cpu_var(perf_cpu_context);
        int rctx;
 
        if (in_nmi())
@@ -4122,10 +4124,8 @@ int perf_swevent_get_recursion_context(void)
        else
                rctx = 0;
 
-       if (cpuctx->recursion[rctx]) {
-               put_cpu_var(perf_cpu_context);
+       if (cpuctx->recursion[rctx])
                return -1;
-       }
 
        cpuctx->recursion[rctx]++;
        barrier();
@@ -4139,7 +4139,6 @@ void perf_swevent_put_recursion_context(int rctx)
        struct perf_cpu_context *cpuctx = &__get_cpu_var(perf_cpu_context);
        barrier();
        cpuctx->recursion[rctx]--;
-       put_cpu_var(perf_cpu_context);
 }
 EXPORT_SYMBOL_GPL(perf_swevent_put_recursion_context);
 
@@ -4150,6 +4149,7 @@ void __perf_sw_event(u32 event_id, u64 nr, int nmi,
        struct perf_sample_data data;
        int rctx;
 
+       preempt_disable_notrace();
        rctx = perf_swevent_get_recursion_context();
        if (rctx < 0)
                return;
@@ -4159,6 +4159,7 @@ void __perf_sw_event(u32 event_id, u64 nr, int nmi,
        do_perf_sw_event(PERF_TYPE_SOFTWARE, event_id, nr, nmi, &data, regs);
 
        perf_swevent_put_recursion_context(rctx);
+       preempt_enable_notrace();
 }
 
 static void perf_swevent_read(struct perf_event *event)
@@ -4178,7 +4179,7 @@ static int perf_swevent_enable(struct perf_event *event)
                perf_swevent_set_period(event);
        }
 
-       head = find_swevent_head(cpuctx, event->attr.type, event->attr.config);
+       head = find_swevent_head(cpuctx, event);
        if (WARN_ON_ONCE(!head))
                return -EINVAL;
 
@@ -4366,6 +4367,14 @@ static const struct pmu perf_ops_task_clock = {
        .read           = task_clock_perf_event_read,
 };
 
+/* Deref the hlist from the update side */
+static inline struct swevent_hlist *
+swevent_hlist_deref(struct perf_cpu_context *cpuctx)
+{
+       return rcu_dereference_protected(cpuctx->swevent_hlist,
+                                        lockdep_is_held(&cpuctx->hlist_mutex));
+}
+
 static void swevent_hlist_release_rcu(struct rcu_head *rcu_head)
 {
        struct swevent_hlist *hlist;
@@ -4376,12 +4385,11 @@ static void swevent_hlist_release_rcu(struct rcu_head *rcu_head)
 
 static void swevent_hlist_release(struct perf_cpu_context *cpuctx)
 {
-       struct swevent_hlist *hlist;
+       struct swevent_hlist *hlist = swevent_hlist_deref(cpuctx);
 
-       if (!cpuctx->swevent_hlist)
+       if (!hlist)
                return;
 
-       hlist = cpuctx->swevent_hlist;
        rcu_assign_pointer(cpuctx->swevent_hlist, NULL);
        call_rcu(&hlist->rcu_head, swevent_hlist_release_rcu);
 }
@@ -4418,7 +4426,7 @@ static int swevent_hlist_get_cpu(struct perf_event *event, int cpu)
 
        mutex_lock(&cpuctx->hlist_mutex);
 
-       if (!cpuctx->swevent_hlist && cpu_online(cpu)) {
+       if (!swevent_hlist_deref(cpuctx) && cpu_online(cpu)) {
                struct swevent_hlist *hlist;
 
                hlist = kzalloc(sizeof(*hlist), GFP_KERNEL);
@@ -4467,10 +4475,46 @@ static int swevent_hlist_get(struct perf_event *event)
 
 #ifdef CONFIG_EVENT_TRACING
 
-void perf_tp_event(int event_id, u64 addr, u64 count, void *record,
-                  int entry_size, struct pt_regs *regs)
+static const struct pmu perf_ops_tracepoint = {
+       .enable         = perf_trace_enable,
+       .disable        = perf_trace_disable,
+       .read           = perf_swevent_read,
+       .unthrottle     = perf_swevent_unthrottle,
+};
+
+static int perf_tp_filter_match(struct perf_event *event,
+                               struct perf_sample_data *data)
+{
+       void *record = data->raw->data;
+
+       if (likely(!event->filter) || filter_match_preds(event->filter, record))
+               return 1;
+       return 0;
+}
+
+static int perf_tp_event_match(struct perf_event *event,
+                               struct perf_sample_data *data,
+                               struct pt_regs *regs)
+{
+       /*
+        * All tracepoints are from kernel-space.
+        */
+       if (event->attr.exclude_kernel)
+               return 0;
+
+       if (!perf_tp_filter_match(event, data))
+               return 0;
+
+       return 1;
+}
+
+void perf_tp_event(u64 addr, u64 count, void *record, int entry_size,
+                  struct pt_regs *regs, struct hlist_head *head)
 {
        struct perf_sample_data data;
+       struct perf_event *event;
+       struct hlist_node *node;
+
        struct perf_raw_record raw = {
                .size = entry_size,
                .data = record,
@@ -4479,26 +4523,18 @@ void perf_tp_event(int event_id, u64 addr, u64 count, void *record,
        perf_sample_data_init(&data, addr);
        data.raw = &raw;
 
-       /* Trace events already protected against recursion */
-       do_perf_sw_event(PERF_TYPE_TRACEPOINT, event_id, count, 1,
-                        &data, regs);
+       rcu_read_lock();
+       hlist_for_each_entry_rcu(event, node, head, hlist_entry) {
+               if (perf_tp_event_match(event, &data, regs))
+                       perf_swevent_add(event, count, 1, &data, regs);
+       }
+       rcu_read_unlock();
 }
 EXPORT_SYMBOL_GPL(perf_tp_event);
 
-static int perf_tp_event_match(struct perf_event *event,
-                               struct perf_sample_data *data)
-{
-       void *record = data->raw->data;
-
-       if (likely(!event->filter) || filter_match_preds(event->filter, record))
-               return 1;
-       return 0;
-}
-
 static void tp_perf_event_destroy(struct perf_event *event)
 {
-       perf_trace_disable(event->attr.config);
-       swevent_hlist_put(event);
+       perf_trace_destroy(event);
 }
 
 static const struct pmu *tp_perf_event_init(struct perf_event *event)
@@ -4514,17 +4550,13 @@ static const struct pmu *tp_perf_event_init(struct perf_event *event)
                        !capable(CAP_SYS_ADMIN))
                return ERR_PTR(-EPERM);
 
-       if (perf_trace_enable(event->attr.config))
+       err = perf_trace_init(event);
+       if (err)
                return NULL;
 
        event->destroy = tp_perf_event_destroy;
-       err = swevent_hlist_get(event);
-       if (err) {
-               perf_trace_disable(event->attr.config);
-               return ERR_PTR(err);
-       }
 
-       return &perf_ops_generic;
+       return &perf_ops_tracepoint;
 }
 
 static int perf_event_set_filter(struct perf_event *event, void __user *arg)
@@ -4552,12 +4584,6 @@ static void perf_event_free_filter(struct perf_event *event)
 
 #else
 
-static int perf_tp_event_match(struct perf_event *event,
-                               struct perf_sample_data *data)
-{
-       return 1;
-}
-
 static const struct pmu *tp_perf_event_init(struct perf_event *event)
 {
        return NULL;
@@ -4894,6 +4920,13 @@ static int perf_event_set_output(struct perf_event *event, int output_fd)
        int fput_needed = 0;
        int ret = -EINVAL;
 
+       /*
+        * Don't allow output of inherited per-task events. This would
+        * create performance issues due to cross cpu access.
+        */
+       if (event->cpu == -1 && event->attr.inherit)
+               return -EINVAL;
+
        if (!output_fd)
                goto set;
 
@@ -4914,6 +4947,18 @@ static int perf_event_set_output(struct perf_event *event, int output_fd)
        if (event->data)
                goto out;
 
+       /*
+        * Don't allow cross-cpu buffers
+        */
+       if (output_event->cpu != event->cpu)
+               goto out;
+
+       /*
+        * If its not a per-cpu buffer, it must be the same task.
+        */
+       if (output_event->cpu == -1 && output_event->ctx != event->ctx)
+               goto out;
+
        atomic_long_inc(&output_file->f_count);
 
 set:
@@ -4954,8 +4999,8 @@ SYSCALL_DEFINE5(perf_event_open,
        struct perf_event_context *ctx;
        struct file *event_file = NULL;
        struct file *group_file = NULL;
+       int event_fd;
        int fput_needed = 0;
-       int fput_needed2 = 0;
        int err;
 
        /* for future expandability... */
@@ -4976,12 +5021,18 @@ SYSCALL_DEFINE5(perf_event_open,
                        return -EINVAL;
        }
 
+       event_fd = get_unused_fd_flags(O_RDWR);
+       if (event_fd < 0)
+               return event_fd;
+
        /*
         * Get the target context (task or percpu):
         */
        ctx = find_get_context(pid, cpu);
-       if (IS_ERR(ctx))
-               return PTR_ERR(ctx);
+       if (IS_ERR(ctx)) {
+               err = PTR_ERR(ctx);
+               goto err_fd;
+       }
 
        /*
         * Look up the group leader (we will attach this event to it):
@@ -5021,13 +5072,11 @@ SYSCALL_DEFINE5(perf_event_open,
        if (IS_ERR(event))
                goto err_put_context;
 
-       err = anon_inode_getfd("[perf_event]", &perf_fops, event, O_RDWR);
-       if (err < 0)
-               goto err_free_put_context;
-
-       event_file = fget_light(err, &fput_needed2);
-       if (!event_file)
+       event_file = anon_inode_getfile("[perf_event]", &perf_fops, event, O_RDWR);
+       if (IS_ERR(event_file)) {
+               err = PTR_ERR(event_file);
                goto err_free_put_context;
+       }
 
        if (flags & PERF_FLAG_FD_OUTPUT) {
                err = perf_event_set_output(event, group_fd);
@@ -5048,19 +5097,19 @@ SYSCALL_DEFINE5(perf_event_open,
        list_add_tail(&event->owner_entry, &current->perf_event_list);
        mutex_unlock(&current->perf_event_mutex);
 
-err_fput_free_put_context:
-       fput_light(event_file, fput_needed2);
+       fput_light(group_file, fput_needed);
+       fd_install(event_fd, event_file);
+       return event_fd;
 
+err_fput_free_put_context:
+       fput(event_file);
 err_free_put_context:
-       if (err < 0)
-               free_event(event);
-
+       free_event(event);
 err_put_context:
-       if (err < 0)
-               put_ctx(ctx);
-
        fput_light(group_file, fput_needed);
-
+       put_ctx(ctx);
+err_fd:
+       put_unused_fd(event_fd);
        return err;
 }
 
index 00d1fda..ad72342 100644 (file)
@@ -559,14 +559,7 @@ SYSCALL_DEFINE3(timer_create, const clockid_t, which_clock,
        new_timer->it_id = (timer_t) new_timer_id;
        new_timer->it_clock = which_clock;
        new_timer->it_overrun = -1;
-       error = CLOCK_DISPATCH(which_clock, timer_create, (new_timer));
-       if (error)
-               goto out;
 
-       /*
-        * return the timer_id now.  The next step is hard to
-        * back out if there is an error.
-        */
        if (copy_to_user(created_timer_id,
                         &new_timer_id, sizeof (new_timer_id))) {
                error = -EFAULT;
@@ -597,6 +590,10 @@ SYSCALL_DEFINE3(timer_create, const clockid_t, which_clock,
        new_timer->sigq->info.si_tid   = new_timer->it_id;
        new_timer->sigq->info.si_code  = SI_TIMER;
 
+       error = CLOCK_DISPATCH(which_clock, timer_create, (new_timer));
+       if (error)
+               goto out;
+
        spin_lock_irq(&current->sighand->siglock);
        new_timer->it_signal = current->signal;
        list_add(&new_timer->list, &current->signal->posix_timers);
index 15b93f6..d484081 100644 (file)
@@ -4054,6 +4054,23 @@ int __sched wait_for_completion_killable(struct completion *x)
 EXPORT_SYMBOL(wait_for_completion_killable);
 
 /**
+ * wait_for_completion_killable_timeout: - waits for completion of a task (w/(to,killable))
+ * @x:  holds the state of this particular completion
+ * @timeout:  timeout value in jiffies
+ *
+ * This waits for either a completion of a specific task to be
+ * signaled or for a specified timeout to expire. It can be
+ * interrupted by a kill signal. The timeout is in jiffies.
+ */
+unsigned long __sched
+wait_for_completion_killable_timeout(struct completion *x,
+                                    unsigned long timeout)
+{
+       return wait_for_common(x, timeout, TASK_KILLABLE);
+}
+EXPORT_SYMBOL(wait_for_completion_killable_timeout);
+
+/**
  *     try_wait_for_completion - try to decrement a completion without blocking
  *     @x:     completion structure
  *
index e3b8c69..2454172 100644 (file)
@@ -752,11 +752,15 @@ unsigned long apply_slack(struct timer_list *timer, unsigned long expires)
 
        expires_limit = expires;
 
-       if (timer->slack > -1)
+       if (timer->slack >= 0) {
                expires_limit = expires + timer->slack;
-       else if (time_after(expires, jiffies)) /* auto slack: use 0.4% */
-               expires_limit = expires + (expires - jiffies)/256;
+       } else {
+               unsigned long now = jiffies;
 
+               /* No slack, if already expired else auto slack 0.4% */
+               if (time_after(expires, now))
+                       expires_limit = expires + (expires - now)/256;
+       }
        mask = expires ^ expires_limit;
        if (mask == 0)
                return expires;
index b3bc91a..36ea2b6 100644 (file)
@@ -675,28 +675,33 @@ static void blk_add_trace_rq(struct request_queue *q, struct request *rq,
        }
 }
 
-static void blk_add_trace_rq_abort(struct request_queue *q, struct request *rq)
+static void blk_add_trace_rq_abort(void *ignore,
+                                  struct request_queue *q, struct request *rq)
 {
        blk_add_trace_rq(q, rq, BLK_TA_ABORT);
 }
 
-static void blk_add_trace_rq_insert(struct request_queue *q, struct request *rq)
+static void blk_add_trace_rq_insert(void *ignore,
+                                   struct request_queue *q, struct request *rq)
 {
        blk_add_trace_rq(q, rq, BLK_TA_INSERT);
 }
 
-static void blk_add_trace_rq_issue(struct request_queue *q, struct request *rq)
+static void blk_add_trace_rq_issue(void *ignore,
+                                  struct request_queue *q, struct request *rq)
 {
        blk_add_trace_rq(q, rq, BLK_TA_ISSUE);
 }
 
-static void blk_add_trace_rq_requeue(struct request_queue *q,
+static void blk_add_trace_rq_requeue(void *ignore,
+                                    struct request_queue *q,
                                     struct request *rq)
 {
        blk_add_trace_rq(q, rq, BLK_TA_REQUEUE);
 }
 
-static void blk_add_trace_rq_complete(struct request_queue *q,
+static void blk_add_trace_rq_complete(void *ignore,
+                                     struct request_queue *q,
                                      struct request *rq)
 {
        blk_add_trace_rq(q, rq, BLK_TA_COMPLETE);
@@ -724,34 +729,40 @@ static void blk_add_trace_bio(struct request_queue *q, struct bio *bio,
                        !bio_flagged(bio, BIO_UPTODATE), 0, NULL);
 }
 
-static void blk_add_trace_bio_bounce(struct request_queue *q, struct bio *bio)
+static void blk_add_trace_bio_bounce(void *ignore,
+                                    struct request_queue *q, struct bio *bio)
 {
        blk_add_trace_bio(q, bio, BLK_TA_BOUNCE);
 }
 
-static void blk_add_trace_bio_complete(struct request_queue *q, struct bio *bio)
+static void blk_add_trace_bio_complete(void *ignore,
+                                      struct request_queue *q, struct bio *bio)
 {
        blk_add_trace_bio(q, bio, BLK_TA_COMPLETE);
 }
 
-static void blk_add_trace_bio_backmerge(struct request_queue *q,
+static void blk_add_trace_bio_backmerge(void *ignore,
+                                       struct request_queue *q,
                                        struct bio *bio)
 {
        blk_add_trace_bio(q, bio, BLK_TA_BACKMERGE);
 }
 
-static void blk_add_trace_bio_frontmerge(struct request_queue *q,
+static void blk_add_trace_bio_frontmerge(void *ignore,
+                                        struct request_queue *q,
                                         struct bio *bio)
 {
        blk_add_trace_bio(q, bio, BLK_TA_FRONTMERGE);
 }
 
-static void blk_add_trace_bio_queue(struct request_queue *q, struct bio *bio)
+static void blk_add_trace_bio_queue(void *ignore,
+                                   struct request_queue *q, struct bio *bio)
 {
        blk_add_trace_bio(q, bio, BLK_TA_QUEUE);
 }
 
-static void blk_add_trace_getrq(struct request_queue *q,
+static void blk_add_trace_getrq(void *ignore,
+                               struct request_queue *q,
                                struct bio *bio, int rw)
 {
        if (bio)
@@ -765,7 +776,8 @@ static void blk_add_trace_getrq(struct request_queue *q,
 }
 
 
-static void blk_add_trace_sleeprq(struct request_queue *q,
+static void blk_add_trace_sleeprq(void *ignore,
+                                 struct request_queue *q,
                                  struct bio *bio, int rw)
 {
        if (bio)
@@ -779,7 +791,7 @@ static void blk_add_trace_sleeprq(struct request_queue *q,
        }
 }
 
-static void blk_add_trace_plug(struct request_queue *q)
+static void blk_add_trace_plug(void *ignore, struct request_queue *q)
 {
        struct blk_trace *bt = q->blk_trace;
 
@@ -787,7 +799,7 @@ static void blk_add_trace_plug(struct request_queue *q)
                __blk_add_trace(bt, 0, 0, 0, BLK_TA_PLUG, 0, 0, NULL);
 }
 
-static void blk_add_trace_unplug_io(struct request_queue *q)
+static void blk_add_trace_unplug_io(void *ignore, struct request_queue *q)
 {
        struct blk_trace *bt = q->blk_trace;
 
@@ -800,7 +812,7 @@ static void blk_add_trace_unplug_io(struct request_queue *q)
        }
 }
 
-static void blk_add_trace_unplug_timer(struct request_queue *q)
+static void blk_add_trace_unplug_timer(void *ignore, struct request_queue *q)
 {
        struct blk_trace *bt = q->blk_trace;
 
@@ -813,7 +825,8 @@ static void blk_add_trace_unplug_timer(struct request_queue *q)
        }
 }
 
-static void blk_add_trace_split(struct request_queue *q, struct bio *bio,
+static void blk_add_trace_split(void *ignore,
+                               struct request_queue *q, struct bio *bio,
                                unsigned int pdu)
 {
        struct blk_trace *bt = q->blk_trace;
@@ -839,8 +852,9 @@ static void blk_add_trace_split(struct request_queue *q, struct bio *bio,
  *     it spans a stripe (or similar). Add a trace for that action.
  *
  **/
-static void blk_add_trace_remap(struct request_queue *q, struct bio *bio,
-                                      dev_t dev, sector_t from)
+static void blk_add_trace_remap(void *ignore,
+                               struct request_queue *q, struct bio *bio,
+                               dev_t dev, sector_t from)
 {
        struct blk_trace *bt = q->blk_trace;
        struct blk_io_trace_remap r;
@@ -869,7 +883,8 @@ static void blk_add_trace_remap(struct request_queue *q, struct bio *bio,
  *     Add a trace for that action.
  *
  **/
-static void blk_add_trace_rq_remap(struct request_queue *q,
+static void blk_add_trace_rq_remap(void *ignore,
+                                  struct request_queue *q,
                                   struct request *rq, dev_t dev,
                                   sector_t from)
 {
@@ -921,64 +936,64 @@ static void blk_register_tracepoints(void)
 {
        int ret;
 
-       ret = register_trace_block_rq_abort(blk_add_trace_rq_abort);
+       ret = register_trace_block_rq_abort(blk_add_trace_rq_abort, NULL);
        WARN_ON(ret);
-       ret = register_trace_block_rq_insert(blk_add_trace_rq_insert);
+       ret = register_trace_block_rq_insert(blk_add_trace_rq_insert, NULL);
        WARN_ON(ret);
-       ret = register_trace_block_rq_issue(blk_add_trace_rq_issue);
+       ret = register_trace_block_rq_issue(blk_add_trace_rq_issue, NULL);
        WARN_ON(ret);
-       ret = register_trace_block_rq_requeue(blk_add_trace_rq_requeue);
+       ret = register_trace_block_rq_requeue(blk_add_trace_rq_requeue, NULL);
        WARN_ON(ret);
-       ret = register_trace_block_rq_complete(blk_add_trace_rq_complete);
+       ret = register_trace_block_rq_complete(blk_add_trace_rq_complete, NULL);
        WARN_ON(ret);
-       ret = register_trace_block_bio_bounce(blk_add_trace_bio_bounce);
+       ret = register_trace_block_bio_bounce(blk_add_trace_bio_bounce, NULL);
        WARN_ON(ret);
-       ret = register_trace_block_bio_complete(blk_add_trace_bio_complete);
+       ret = register_trace_block_bio_complete(blk_add_trace_bio_complete, NULL);
        WARN_ON(ret);
-       ret = register_trace_block_bio_backmerge(blk_add_trace_bio_backmerge);
+       ret = register_trace_block_bio_backmerge(blk_add_trace_bio_backmerge, NULL);
        WARN_ON(ret);
-       ret = register_trace_block_bio_frontmerge(blk_add_trace_bio_frontmerge);
+       ret = register_trace_block_bio_frontmerge(blk_add_trace_bio_frontmerge, NULL);
        WARN_ON(ret);
-       ret = register_trace_block_bio_queue(blk_add_trace_bio_queue);
+       ret = register_trace_block_bio_queue(blk_add_trace_bio_queue, NULL);
        WARN_ON(ret);
-       ret = register_trace_block_getrq(blk_add_trace_getrq);
+       ret = register_trace_block_getrq(blk_add_trace_getrq, NULL);
        WARN_ON(ret);
-       ret = register_trace_block_sleeprq(blk_add_trace_sleeprq);
+       ret = register_trace_block_sleeprq(blk_add_trace_sleeprq, NULL);
        WARN_ON(ret);
-       ret = register_trace_block_plug(blk_add_trace_plug);
+       ret = register_trace_block_plug(blk_add_trace_plug, NULL);
        WARN_ON(ret);
-       ret = register_trace_block_unplug_timer(blk_add_trace_unplug_timer);
+       ret = register_trace_block_unplug_timer(blk_add_trace_unplug_timer, NULL);
        WARN_ON(ret);
-       ret = register_trace_block_unplug_io(blk_add_trace_unplug_io);
+       ret = register_trace_block_unplug_io(blk_add_trace_unplug_io, NULL);
        WARN_ON(ret);
-       ret = register_trace_block_split(blk_add_trace_split);
+       ret = register_trace_block_split(blk_add_trace_split, NULL);
        WARN_ON(ret);
-       ret = register_trace_block_remap(blk_add_trace_remap);
+       ret = register_trace_block_remap(blk_add_trace_remap, NULL);
        WARN_ON(ret);
-       ret = register_trace_block_rq_remap(blk_add_trace_rq_remap);
+       ret = register_trace_block_rq_remap(blk_add_trace_rq_remap, NULL);
        WARN_ON(ret);
 }
 
 static void blk_unregister_tracepoints(void)
 {
-       unregister_trace_block_rq_remap(blk_add_trace_rq_remap);
-       unregister_trace_block_remap(blk_add_trace_remap);
-       unregister_trace_block_split(blk_add_trace_split);
-       unregister_trace_block_unplug_io(blk_add_trace_unplug_io);
-       unregister_trace_block_unplug_timer(blk_add_trace_unplug_timer);
-       unregister_trace_block_plug(blk_add_trace_plug);
-       unregister_trace_block_sleeprq(blk_add_trace_sleeprq);
-       unregister_trace_block_getrq(blk_add_trace_getrq);
-       unregister_trace_block_bio_queue(blk_add_trace_bio_queue);
-       unregister_trace_block_bio_frontmerge(blk_add_trace_bio_frontmerge);
-       unregister_trace_block_bio_backmerge(blk_add_trace_bio_backmerge);
-       unregister_trace_block_bio_complete(blk_add_trace_bio_complete);
-       unregister_trace_block_bio_bounce(blk_add_trace_bio_bounce);
-       unregister_trace_block_rq_complete(blk_add_trace_rq_complete);
-       unregister_trace_block_rq_requeue(blk_add_trace_rq_requeue);
-       unregister_trace_block_rq_issue(blk_add_trace_rq_issue);
-       unregister_trace_block_rq_insert(blk_add_trace_rq_insert);
-       unregister_trace_block_rq_abort(blk_add_trace_rq_abort);
+       unregister_trace_block_rq_remap(blk_add_trace_rq_remap, NULL);
+       unregister_trace_block_remap(blk_add_trace_remap, NULL);
+       unregister_trace_block_split(blk_add_trace_split, NULL);
+       unregister_trace_block_unplug_io(blk_add_trace_unplug_io, NULL);
+       unregister_trace_block_unplug_timer(blk_add_trace_unplug_timer, NULL);
+       unregister_trace_block_plug(blk_add_trace_plug, NULL);
+       unregister_trace_block_sleeprq(blk_add_trace_sleeprq, NULL);
+       unregister_trace_block_getrq(blk_add_trace_getrq, NULL);
+       unregister_trace_block_bio_queue(blk_add_trace_bio_queue, NULL);
+       unregister_trace_block_bio_frontmerge(blk_add_trace_bio_frontmerge, NULL);
+       unregister_trace_block_bio_backmerge(blk_add_trace_bio_backmerge, NULL);
+       unregister_trace_block_bio_complete(blk_add_trace_bio_complete, NULL);
+       unregister_trace_block_bio_bounce(blk_add_trace_bio_bounce, NULL);
+       unregister_trace_block_rq_complete(blk_add_trace_rq_complete, NULL);
+       unregister_trace_block_rq_requeue(blk_add_trace_rq_requeue, NULL);
+       unregister_trace_block_rq_issue(blk_add_trace_rq_issue, NULL);
+       unregister_trace_block_rq_insert(blk_add_trace_rq_insert, NULL);
+       unregister_trace_block_rq_abort(blk_add_trace_rq_abort, NULL);
 
        tracepoint_synchronize_unregister();
 }
@@ -1321,7 +1336,7 @@ out:
 }
 
 static enum print_line_t blk_trace_event_print(struct trace_iterator *iter,
-                                              int flags)
+                                              int flags, struct trace_event *event)
 {
        return print_one_line(iter, false);
 }
@@ -1343,7 +1358,8 @@ static int blk_trace_synthesize_old_trace(struct trace_iterator *iter)
 }
 
 static enum print_line_t
-blk_trace_event_print_binary(struct trace_iterator *iter, int flags)
+blk_trace_event_print_binary(struct trace_iterator *iter, int flags,
+                            struct trace_event *event)
 {
        return blk_trace_synthesize_old_trace(iter) ?
                        TRACE_TYPE_HANDLED : TRACE_TYPE_PARTIAL_LINE;
@@ -1381,12 +1397,16 @@ static struct tracer blk_tracer __read_mostly = {
        .set_flag       = blk_tracer_set_flag,
 };
 
-static struct trace_event trace_blk_event = {
-       .type           = TRACE_BLK,
+static struct trace_event_functions trace_blk_event_funcs = {
        .trace          = blk_trace_event_print,
        .binary         = blk_trace_event_print_binary,
 };
 
+static struct trace_event trace_blk_event = {
+       .type           = TRACE_BLK,
+       .funcs          = &trace_blk_event_funcs,
+};
+
 static int __init init_blk_tracer(void)
 {
        if (!register_ftrace_event(&trace_blk_event)) {
index 32837e1..6d2cb14 100644 (file)
@@ -3234,7 +3234,8 @@ free:
 }
 
 static void
-ftrace_graph_probe_sched_switch(struct task_struct *prev, struct task_struct *next)
+ftrace_graph_probe_sched_switch(void *ignore,
+                       struct task_struct *prev, struct task_struct *next)
 {
        unsigned long long timestamp;
        int index;
@@ -3288,7 +3289,7 @@ static int start_graph_tracing(void)
        } while (ret == -EAGAIN);
 
        if (!ret) {
-               ret = register_trace_sched_switch(ftrace_graph_probe_sched_switch);
+               ret = register_trace_sched_switch(ftrace_graph_probe_sched_switch, NULL);
                if (ret)
                        pr_info("ftrace_graph: Couldn't activate tracepoint"
                                " probe to kernel_sched_switch\n");
@@ -3364,7 +3365,7 @@ void unregister_ftrace_graph(void)
        ftrace_graph_entry = ftrace_graph_entry_stub;
        ftrace_shutdown(FTRACE_STOP_FUNC_RET);
        unregister_pm_notifier(&ftrace_suspend_notifier);
-       unregister_trace_sched_switch(ftrace_graph_probe_sched_switch);
+       unregister_trace_sched_switch(ftrace_graph_probe_sched_switch, NULL);
 
  out:
        mutex_unlock(&ftrace_lock);
index a91da69..bbfc1bb 100644 (file)
@@ -95,7 +95,8 @@ static inline void kmemtrace_free(enum kmemtrace_type_id type_id,
        trace_wake_up();
 }
 
-static void kmemtrace_kmalloc(unsigned long call_site,
+static void kmemtrace_kmalloc(void *ignore,
+                             unsigned long call_site,
                              const void *ptr,
                              size_t bytes_req,
                              size_t bytes_alloc,
@@ -105,7 +106,8 @@ static void kmemtrace_kmalloc(unsigned long call_site,
                        bytes_req, bytes_alloc, gfp_flags, -1);
 }
 
-static void kmemtrace_kmem_cache_alloc(unsigned long call_site,
+static void kmemtrace_kmem_cache_alloc(void *ignore,
+                                      unsigned long call_site,
                                       const void *ptr,
                                       size_t bytes_req,
                                       size_t bytes_alloc,
@@ -115,7 +117,8 @@ static void kmemtrace_kmem_cache_alloc(unsigned long call_site,
                        bytes_req, bytes_alloc, gfp_flags, -1);
 }
 
-static void kmemtrace_kmalloc_node(unsigned long call_site,
+static void kmemtrace_kmalloc_node(void *ignore,
+                                  unsigned long call_site,
                                   const void *ptr,
                                   size_t bytes_req,
                                   size_t bytes_alloc,
@@ -126,7 +129,8 @@ static void kmemtrace_kmalloc_node(unsigned long call_site,
                        bytes_req, bytes_alloc, gfp_flags, node);
 }
 
-static void kmemtrace_kmem_cache_alloc_node(unsigned long call_site,
+static void kmemtrace_kmem_cache_alloc_node(void *ignore,
+                                           unsigned long call_site,
                                            const void *ptr,
                                            size_t bytes_req,
                                            size_t bytes_alloc,
@@ -137,12 +141,14 @@ static void kmemtrace_kmem_cache_alloc_node(unsigned long call_site,
                        bytes_req, bytes_alloc, gfp_flags, node);
 }
 
-static void kmemtrace_kfree(unsigned long call_site, const void *ptr)
+static void
+kmemtrace_kfree(void *ignore, unsigned long call_site, const void *ptr)
 {
        kmemtrace_free(KMEMTRACE_TYPE_KMALLOC, call_site, ptr);
 }
 
-static void kmemtrace_kmem_cache_free(unsigned long call_site, const void *ptr)
+static void kmemtrace_kmem_cache_free(void *ignore,
+                                     unsigned long call_site, const void *ptr)
 {
        kmemtrace_free(KMEMTRACE_TYPE_CACHE, call_site, ptr);
 }
@@ -151,34 +157,34 @@ static int kmemtrace_start_probes(void)
 {
        int err;
 
-       err = register_trace_kmalloc(kmemtrace_kmalloc);
+       err = register_trace_kmalloc(kmemtrace_kmalloc, NULL);
        if (err)
                return err;
-       err = register_trace_kmem_cache_alloc(kmemtrace_kmem_cache_alloc);
+       err = register_trace_kmem_cache_alloc(kmemtrace_kmem_cache_alloc, NULL);
        if (err)
                return err;
-       err = register_trace_kmalloc_node(kmemtrace_kmalloc_node);
+       err = register_trace_kmalloc_node(kmemtrace_kmalloc_node, NULL);
        if (err)
                return err;
-       err = register_trace_kmem_cache_alloc_node(kmemtrace_kmem_cache_alloc_node);
+       err = register_trace_kmem_cache_alloc_node(kmemtrace_kmem_cache_alloc_node, NULL);
        if (err)
                return err;
-       err = register_trace_kfree(kmemtrace_kfree);
+       err = register_trace_kfree(kmemtrace_kfree, NULL);
        if (err)
                return err;
-       err = register_trace_kmem_cache_free(kmemtrace_kmem_cache_free);
+       err = register_trace_kmem_cache_free(kmemtrace_kmem_cache_free, NULL);
 
        return err;
 }
 
 static void kmemtrace_stop_probes(void)
 {
-       unregister_trace_kmalloc(kmemtrace_kmalloc);
-       unregister_trace_kmem_cache_alloc(kmemtrace_kmem_cache_alloc);
-       unregister_trace_kmalloc_node(kmemtrace_kmalloc_node);
-       unregister_trace_kmem_cache_alloc_node(kmemtrace_kmem_cache_alloc_node);
-       unregister_trace_kfree(kmemtrace_kfree);
-       unregister_trace_kmem_cache_free(kmemtrace_kmem_cache_free);
+       unregister_trace_kmalloc(kmemtrace_kmalloc, NULL);
+       unregister_trace_kmem_cache_alloc(kmemtrace_kmem_cache_alloc, NULL);
+       unregister_trace_kmalloc_node(kmemtrace_kmalloc_node, NULL);
+       unregister_trace_kmem_cache_alloc_node(kmemtrace_kmem_cache_alloc_node, NULL);
+       unregister_trace_kfree(kmemtrace_kfree, NULL);
+       unregister_trace_kmem_cache_free(kmemtrace_kmem_cache_free, NULL);
 }
 
 static int kmem_trace_init(struct trace_array *tr)
@@ -237,7 +243,8 @@ struct kmemtrace_user_event_alloc {
 };
 
 static enum print_line_t
-kmemtrace_print_alloc(struct trace_iterator *iter, int flags)
+kmemtrace_print_alloc(struct trace_iterator *iter, int flags,
+                     struct trace_event *event)
 {
        struct trace_seq *s = &iter->seq;
        struct kmemtrace_alloc_entry *entry;
@@ -257,7 +264,8 @@ kmemtrace_print_alloc(struct trace_iterator *iter, int flags)
 }
 
 static enum print_line_t
-kmemtrace_print_free(struct trace_iterator *iter, int flags)
+kmemtrace_print_free(struct trace_iterator *iter, int flags,
+                    struct trace_event *event)
 {
        struct trace_seq *s = &iter->seq;
        struct kmemtrace_free_entry *entry;
@@ -275,7 +283,8 @@ kmemtrace_print_free(struct trace_iterator *iter, int flags)
 }
 
 static enum print_line_t
-kmemtrace_print_alloc_user(struct trace_iterator *iter, int flags)
+kmemtrace_print_alloc_user(struct trace_iterator *iter, int flags,
+                          struct trace_event *event)
 {
        struct trace_seq *s = &iter->seq;
        struct kmemtrace_alloc_entry *entry;
@@ -309,7 +318,8 @@ kmemtrace_print_alloc_user(struct trace_iterator *iter, int flags)
 }
 
 static enum print_line_t
-kmemtrace_print_free_user(struct trace_iterator *iter, int flags)
+kmemtrace_print_free_user(struct trace_iterator *iter, int flags,
+                         struct trace_event *event)
 {
        struct trace_seq *s = &iter->seq;
        struct kmemtrace_free_entry *entry;
@@ -463,18 +473,26 @@ static enum print_line_t kmemtrace_print_line(struct trace_iterator *iter)
        }
 }
 
-static struct trace_event kmem_trace_alloc = {
-       .type                   = TRACE_KMEM_ALLOC,
+static struct trace_event_functions kmem_trace_alloc_funcs = {
        .trace                  = kmemtrace_print_alloc,
        .binary                 = kmemtrace_print_alloc_user,
 };
 
-static struct trace_event kmem_trace_free = {
-       .type                   = TRACE_KMEM_FREE,
+static struct trace_event kmem_trace_alloc = {
+       .type                   = TRACE_KMEM_ALLOC,
+       .funcs                  = &kmem_trace_alloc_funcs,
+};
+
+static struct trace_event_functions kmem_trace_free_funcs = {
        .trace                  = kmemtrace_print_free,
        .binary                 = kmemtrace_print_free_user,
 };
 
+static struct trace_event kmem_trace_free = {
+       .type                   = TRACE_KMEM_FREE,
+       .funcs                  = &kmem_trace_free_funcs,
+};
+
 static struct tracer kmem_tracer __read_mostly = {
        .name                   = "kmemtrace",
        .init                   = kmem_trace_init,
index 8a76339..55e4851 100644 (file)
@@ -1936,7 +1936,7 @@ static enum print_line_t print_trace_fmt(struct trace_iterator *iter)
        }
 
        if (event)
-               return event->trace(iter, sym_flags);
+               return event->funcs->trace(iter, sym_flags, event);
 
        if (!trace_seq_printf(s, "Unknown type %d\n", entry->type))
                goto partial;
@@ -1962,7 +1962,7 @@ static enum print_line_t print_raw_fmt(struct trace_iterator *iter)
 
        event = ftrace_find_event(entry->type);
        if (event)
-               return event->raw(iter, 0);
+               return event->funcs->raw(iter, 0, event);
 
        if (!trace_seq_printf(s, "%d ?\n", entry->type))
                goto partial;
@@ -1989,7 +1989,7 @@ static enum print_line_t print_hex_fmt(struct trace_iterator *iter)
 
        event = ftrace_find_event(entry->type);
        if (event) {
-               enum print_line_t ret = event->hex(iter, 0);
+               enum print_line_t ret = event->funcs->hex(iter, 0, event);
                if (ret != TRACE_TYPE_HANDLED)
                        return ret;
        }
@@ -2014,7 +2014,8 @@ static enum print_line_t print_bin_fmt(struct trace_iterator *iter)
        }
 
        event = ftrace_find_event(entry->type);
-       return event ? event->binary(iter, 0) : TRACE_TYPE_HANDLED;
+       return event ? event->funcs->binary(iter, 0, event) :
+               TRACE_TYPE_HANDLED;
 }
 
 int trace_empty(struct trace_iterator *iter)
index d1ce0be..2cd9639 100644 (file)
@@ -405,12 +405,12 @@ void ftrace_trace_userstack(struct ring_buffer *buffer, unsigned long flags,
 void __trace_stack(struct trace_array *tr, unsigned long flags, int skip,
                   int pc);
 #else
-static inline void ftrace_trace_stack(struct trace_array *tr,
+static inline void ftrace_trace_stack(struct ring_buffer *buffer,
                                      unsigned long flags, int skip, int pc)
 {
 }
 
-static inline void ftrace_trace_userstack(struct trace_array *tr,
+static inline void ftrace_trace_userstack(struct ring_buffer *buffer,
                                          unsigned long flags, int pc)
 {
 }
@@ -778,12 +778,15 @@ extern void print_subsystem_event_filter(struct event_subsystem *system,
                                         struct trace_seq *s);
 extern int filter_assign_type(const char *type);
 
+struct list_head *
+trace_get_fields(struct ftrace_event_call *event_call);
+
 static inline int
 filter_check_discard(struct ftrace_event_call *call, void *rec,
                     struct ring_buffer *buffer,
                     struct ring_buffer_event *event)
 {
-       if (unlikely(call->filter_active) &&
+       if (unlikely(call->flags & TRACE_EVENT_FL_FILTERED) &&
            !filter_match_preds(call->filter, rec)) {
                ring_buffer_discard_commit(buffer, event);
                return 1;
index b9bc4d4..8d3538b 100644 (file)
@@ -143,7 +143,7 @@ static void branch_trace_reset(struct trace_array *tr)
 }
 
 static enum print_line_t trace_branch_print(struct trace_iterator *iter,
-                                           int flags)
+                                           int flags, struct trace_event *event)
 {
        struct trace_branch *field;
 
@@ -167,9 +167,13 @@ static void branch_print_header(struct seq_file *s)
                "    |\n");
 }
 
+static struct trace_event_functions trace_branch_funcs = {
+       .trace          = trace_branch_print,
+};
+
 static struct trace_event trace_branch_event = {
        .type           = TRACE_BRANCH,
-       .trace          = trace_branch_print,
+       .funcs          = &trace_branch_funcs,
 };
 
 static struct tracer branch_trace __read_mostly =
index 0565bb4..cb6f365 100644 (file)
@@ -9,13 +9,9 @@
 #include <linux/kprobes.h>
 #include "trace.h"
 
-DEFINE_PER_CPU(struct pt_regs, perf_trace_regs);
-EXPORT_PER_CPU_SYMBOL_GPL(perf_trace_regs);
-
 EXPORT_SYMBOL_GPL(perf_arch_fetch_caller_regs);
 
-static char *perf_trace_buf;
-static char *perf_trace_buf_nmi;
+static char *perf_trace_buf[4];
 
 /*
  * Force it to be aligned to unsigned long to avoid misaligned accesses
@@ -27,57 +23,82 @@ typedef typeof(unsigned long [PERF_MAX_TRACE_SIZE / sizeof(unsigned long)])
 /* Count the events in use (per event id, not per instance) */
 static int     total_ref_count;
 
-static int perf_trace_event_enable(struct ftrace_event_call *event)
+static int perf_trace_event_init(struct ftrace_event_call *tp_event,
+                                struct perf_event *p_event)
 {
-       char *buf;
+       struct hlist_head *list;
        int ret = -ENOMEM;
+       int cpu;
 
-       if (event->perf_refcount++ > 0)
+       p_event->tp_event = tp_event;
+       if (tp_event->perf_refcount++ > 0)
                return 0;
 
-       if (!total_ref_count) {
-               buf = (char *)alloc_percpu(perf_trace_t);
-               if (!buf)
-                       goto fail_buf;
+       list = alloc_percpu(struct hlist_head);
+       if (!list)
+               goto fail;
 
-               rcu_assign_pointer(perf_trace_buf, buf);
+       for_each_possible_cpu(cpu)
+               INIT_HLIST_HEAD(per_cpu_ptr(list, cpu));
 
-               buf = (char *)alloc_percpu(perf_trace_t);
-               if (!buf)
-                       goto fail_buf_nmi;
+       tp_event->perf_events = list;
 
-               rcu_assign_pointer(perf_trace_buf_nmi, buf);
-       }
+       if (!total_ref_count) {
+               char *buf;
+               int i;
 
-       ret = event->perf_event_enable(event);
-       if (!ret) {
-               total_ref_count++;
-               return 0;
+               for (i = 0; i < 4; i++) {
+                       buf = (char *)alloc_percpu(perf_trace_t);
+                       if (!buf)
+                               goto fail;
+
+                       perf_trace_buf[i] = buf;
+               }
        }
 
-fail_buf_nmi:
+       if (tp_event->class->reg)
+               ret = tp_event->class->reg(tp_event, TRACE_REG_PERF_REGISTER);
+       else
+               ret = tracepoint_probe_register(tp_event->name,
+                                               tp_event->class->perf_probe,
+                                               tp_event);
+
+       if (ret)
+               goto fail;
+
+       total_ref_count++;
+       return 0;
+
+fail:
        if (!total_ref_count) {
-               free_percpu(perf_trace_buf_nmi);
-               free_percpu(perf_trace_buf);
-               perf_trace_buf_nmi = NULL;
-               perf_trace_buf = NULL;
+               int i;
+
+               for (i = 0; i < 4; i++) {
+                       free_percpu(perf_trace_buf[i]);
+                       perf_trace_buf[i] = NULL;
+               }
+       }
+
+       if (!--tp_event->perf_refcount) {
+               free_percpu(tp_event->perf_events);
+               tp_event->perf_events = NULL;
        }
-fail_buf:
-       event->perf_refcount--;
 
        return ret;
 }
 
-int perf_trace_enable(int event_id)
+int perf_trace_init(struct perf_event *p_event)
 {
-       struct ftrace_event_call *event;
+       struct ftrace_event_call *tp_event;
+       int event_id = p_event->attr.config;
        int ret = -EINVAL;
 
        mutex_lock(&event_mutex);
-       list_for_each_entry(event, &ftrace_events, list) {
-               if (event->id == event_id && event->perf_event_enable &&
-                   try_module_get(event->mod)) {
-                       ret = perf_trace_event_enable(event);
+       list_for_each_entry(tp_event, &ftrace_events, list) {
+               if (tp_event->event.type == event_id &&
+                   tp_event->class && tp_event->class->perf_probe &&
+                   try_module_get(tp_event->mod)) {
+                       ret = perf_trace_event_init(tp_event, p_event);
                        break;
                }
        }
@@ -86,90 +107,78 @@ int perf_trace_enable(int event_id)
        return ret;
 }
 
-static void perf_trace_event_disable(struct ftrace_event_call *event)
+int perf_trace_enable(struct perf_event *p_event)
 {
-       char *buf, *nmi_buf;
-
-       if (--event->perf_refcount > 0)
-               return;
-
-       event->perf_event_disable(event);
+       struct ftrace_event_call *tp_event = p_event->tp_event;
+       struct hlist_head *list;
 
-       if (!--total_ref_count) {
-               buf = perf_trace_buf;
-               rcu_assign_pointer(perf_trace_buf, NULL);
+       list = tp_event->perf_events;
+       if (WARN_ON_ONCE(!list))
+               return -EINVAL;
 
-               nmi_buf = perf_trace_buf_nmi;
-               rcu_assign_pointer(perf_trace_buf_nmi, NULL);
+       list = per_cpu_ptr(list, smp_processor_id());
+       hlist_add_head_rcu(&p_event->hlist_entry, list);
 
-               /*
-                * Ensure every events in profiling have finished before
-                * releasing the buffers
-                */
-               synchronize_sched();
+       return 0;
+}
 
-               free_percpu(buf);
-               free_percpu(nmi_buf);
-       }
+void perf_trace_disable(struct perf_event *p_event)
+{
+       hlist_del_rcu(&p_event->hlist_entry);
 }
 
-void perf_trace_disable(int event_id)
+void perf_trace_destroy(struct perf_event *p_event)
 {
-       struct ftrace_event_call *event;
+       struct ftrace_event_call *tp_event = p_event->tp_event;
+       int i;
 
-       mutex_lock(&event_mutex);
-       list_for_each_entry(event, &ftrace_events, list) {
-               if (event->id == event_id) {
-                       perf_trace_event_disable(event);
-                       module_put(event->mod);
-                       break;
+       if (--tp_event->perf_refcount > 0)
+               return;
+
+       if (tp_event->class->reg)
+               tp_event->class->reg(tp_event, TRACE_REG_PERF_UNREGISTER);
+       else
+               tracepoint_probe_unregister(tp_event->name,
+                                           tp_event->class->perf_probe,
+                                           tp_event);
+
+       free_percpu(tp_event->perf_events);
+       tp_event->perf_events = NULL;
+
+       if (!--total_ref_count) {
+               for (i = 0; i < 4; i++) {
+                       free_percpu(perf_trace_buf[i]);
+                       perf_trace_buf[i] = NULL;
                }
        }
-       mutex_unlock(&event_mutex);
 }
 
 __kprobes void *perf_trace_buf_prepare(int size, unsigned short type,
-                                      int *rctxp, unsigned long *irq_flags)
+                                      struct pt_regs *regs, int *rctxp)
 {
        struct trace_entry *entry;
-       char *trace_buf, *raw_data;
-       int pc, cpu;
+       unsigned long flags;
+       char *raw_data;
+       int pc;
 
        BUILD_BUG_ON(PERF_MAX_TRACE_SIZE % sizeof(unsigned long));
 
        pc = preempt_count();
 
-       /* Protect the per cpu buffer, begin the rcu read side */
-       local_irq_save(*irq_flags);
-
        *rctxp = perf_swevent_get_recursion_context();
        if (*rctxp < 0)
-               goto err_recursion;
-
-       cpu = smp_processor_id();
-
-       if (in_nmi())
-               trace_buf = rcu_dereference_sched(perf_trace_buf_nmi);
-       else
-               trace_buf = rcu_dereference_sched(perf_trace_buf);
-
-       if (!trace_buf)
-               goto err;
+               return NULL;
 
-       raw_data = per_cpu_ptr(trace_buf, cpu);
+       raw_data = per_cpu_ptr(perf_trace_buf[*rctxp], smp_processor_id());
 
        /* zero the dead bytes from align to not leak stack to user */
        memset(&raw_data[size - sizeof(u64)], 0, sizeof(u64));
 
        entry = (struct trace_entry *)raw_data;
-       tracing_generic_entry_update(entry, *irq_flags, pc);
+       local_save_flags(flags);
+       tracing_generic_entry_update(entry, flags, pc);
        entry->type = type;
 
        return raw_data;
-err:
-       perf_swevent_put_recursion_context(*rctxp);
-err_recursion:
-       local_irq_restore(*irq_flags);
-       return NULL;
 }
 EXPORT_SYMBOL_GPL(perf_trace_buf_prepare);
index c697c70..53cffc0 100644 (file)
@@ -29,11 +29,23 @@ DEFINE_MUTEX(event_mutex);
 
 LIST_HEAD(ftrace_events);
 
+struct list_head *
+trace_get_fields(struct ftrace_event_call *event_call)
+{
+       if (!event_call->class->get_fields)
+               return &event_call->class->fields;
+       return event_call->class->get_fields(event_call);
+}
+
 int trace_define_field(struct ftrace_event_call *call, const char *type,
                       const char *name, int offset, int size, int is_signed,
                       int filter_type)
 {
        struct ftrace_event_field *field;
+       struct list_head *head;
+
+       if (WARN_ON(!call->class))
+               return 0;
 
        field = kzalloc(sizeof(*field), GFP_KERNEL);
        if (!field)
@@ -56,7 +68,8 @@ int trace_define_field(struct ftrace_event_call *call, const char *type,
        field->size = size;
        field->is_signed = is_signed;
 
-       list_add(&field->link, &call->fields);
+       head = trace_get_fields(call);
+       list_add(&field->link, head);
 
        return 0;
 
@@ -94,8 +107,10 @@ static int trace_define_common_fields(struct ftrace_event_call *call)
 void trace_destroy_fields(struct ftrace_event_call *call)
 {
        struct ftrace_event_field *field, *next;
+       struct list_head *head;
 
-       list_for_each_entry_safe(field, next, &call->fields, link) {
+       head = trace_get_fields(call);
+       list_for_each_entry_safe(field, next, head, link) {
                list_del(&field->link);
                kfree(field->type);
                kfree(field->name);
@@ -107,11 +122,9 @@ int trace_event_raw_init(struct ftrace_event_call *call)
 {
        int id;
 
-       id = register_ftrace_event(call->event);
+       id = register_ftrace_event(&call->event);
        if (!id)
                return -ENODEV;
-       call->id = id;
-       INIT_LIST_HEAD(&call->fields);
 
        return 0;
 }
@@ -124,23 +137,33 @@ static int ftrace_event_enable_disable(struct ftrace_event_call *call,
 
        switch (enable) {
        case 0:
-               if (call->enabled) {
-                       call->enabled = 0;
+               if (call->flags & TRACE_EVENT_FL_ENABLED) {
+                       call->flags &= ~TRACE_EVENT_FL_ENABLED;
                        tracing_stop_cmdline_record();
-                       call->unregfunc(call);
+                       if (call->class->reg)
+                               call->class->reg(call, TRACE_REG_UNREGISTER);
+                       else
+                               tracepoint_probe_unregister(call->name,
+                                                           call->class->probe,
+                                                           call);
                }
                break;
        case 1:
-               if (!call->enabled) {
+               if (!(call->flags & TRACE_EVENT_FL_ENABLED)) {
                        tracing_start_cmdline_record();
-                       ret = call->regfunc(call);
+                       if (call->class->reg)
+                               ret = call->class->reg(call, TRACE_REG_REGISTER);
+                       else
+                               ret = tracepoint_probe_register(call->name,
+                                                               call->class->probe,
+                                                               call);
                        if (ret) {
                                tracing_stop_cmdline_record();
                                pr_info("event trace: Could not enable event "
                                        "%s\n", call->name);
                                break;
                        }
-                       call->enabled = 1;
+                       call->flags |= TRACE_EVENT_FL_ENABLED;
                }
                break;
        }
@@ -171,15 +194,16 @@ static int __ftrace_set_clr_event(const char *match, const char *sub,
        mutex_lock(&event_mutex);
        list_for_each_entry(call, &ftrace_events, list) {
 
-               if (!call->name || !call->regfunc)
+               if (!call->name || !call->class ||
+                   (!call->class->probe && !call->class->reg))
                        continue;
 
                if (match &&
                    strcmp(match, call->name) != 0 &&
-                   strcmp(match, call->system) != 0)
+                   strcmp(match, call->class->system) != 0)
                        continue;
 
-               if (sub && strcmp(sub, call->system) != 0)
+               if (sub && strcmp(sub, call->class->system) != 0)
                        continue;
 
                if (event && strcmp(event, call->name) != 0)
@@ -297,7 +321,7 @@ t_next(struct seq_file *m, void *v, loff_t *pos)
                 * The ftrace subsystem is for showing formats only.
                 * They can not be enabled or disabled via the event files.
                 */
-               if (call->regfunc)
+               if (call->class && (call->class->probe || call->class->reg))
                        return call;
        }
 
@@ -328,7 +352,7 @@ s_next(struct seq_file *m, void *v, loff_t *pos)
        (*pos)++;
 
        list_for_each_entry_continue(call, &ftrace_events, list) {
-               if (call->enabled)
+               if (call->flags & TRACE_EVENT_FL_ENABLED)
                        return call;
        }
 
@@ -355,8 +379,8 @@ static int t_show(struct seq_file *m, void *v)
 {
        struct ftrace_event_call *call = v;
 
-       if (strcmp(call->system, TRACE_SYSTEM) != 0)
-               seq_printf(m, "%s:", call->system);
+       if (strcmp(call->class->system, TRACE_SYSTEM) != 0)
+               seq_printf(m, "%s:", call->class->system);
        seq_printf(m, "%s\n", call->name);
 
        return 0;
@@ -387,7 +411,7 @@ event_enable_read(struct file *filp, char __user *ubuf, size_t cnt,
        struct ftrace_event_call *call = filp->private_data;
        char *buf;
 
-       if (call->enabled)
+       if (call->flags & TRACE_EVENT_FL_ENABLED)
                buf = "1\n";
        else
                buf = "0\n";
@@ -450,10 +474,11 @@ system_enable_read(struct file *filp, char __user *ubuf, size_t cnt,
 
        mutex_lock(&event_mutex);
        list_for_each_entry(call, &ftrace_events, list) {
-               if (!call->name || !call->regfunc)
+               if (!call->name || !call->class ||
+                   (!call->class->probe && !call->class->reg))
                        continue;
 
-               if (system && strcmp(call->system, system) != 0)
+               if (system && strcmp(call->class->system, system) != 0)
                        continue;
 
                /*
@@ -461,7 +486,7 @@ system_enable_read(struct file *filp, char __user *ubuf, size_t cnt,
                 * or if all events or cleared, or if we have
                 * a mixture.
                 */
-               set |= (1 << !!call->enabled);
+               set |= (1 << !!(call->flags & TRACE_EVENT_FL_ENABLED));
 
                /*
                 * If we have a mixture, no need to look further.
@@ -525,6 +550,7 @@ event_format_read(struct file *filp, char __user *ubuf, size_t cnt,
 {
        struct ftrace_event_call *call = filp->private_data;
        struct ftrace_event_field *field;
+       struct list_head *head;
        struct trace_seq *s;
        int common_field_count = 5;
        char *buf;
@@ -540,10 +566,11 @@ event_format_read(struct file *filp, char __user *ubuf, size_t cnt,
        trace_seq_init(s);
 
        trace_seq_printf(s, "name: %s\n", call->name);
-       trace_seq_printf(s, "ID: %d\n", call->id);
+       trace_seq_printf(s, "ID: %d\n", call->event.type);
        trace_seq_printf(s, "format:\n");
 
-       list_for_each_entry_reverse(field, &call->fields, link) {
+       head = trace_get_fields(call);
+       list_for_each_entry_reverse(field, head, link) {
                /*
                 * Smartly shows the array type(except dynamic array).
                 * Normal:
@@ -613,7 +640,7 @@ event_id_read(struct file *filp, char __user *ubuf, size_t cnt, loff_t *ppos)
                return -ENOMEM;
 
        trace_seq_init(s);
-       trace_seq_printf(s, "%d\n", call->id);
+       trace_seq_printf(s, "%d\n", call->event.type);
 
        r = simple_read_from_buffer(ubuf, cnt, ppos,
                                    s->buffer, s->len);
@@ -919,14 +946,15 @@ event_create_dir(struct ftrace_event_call *call, struct dentry *d_events,
                 const struct file_operations *filter,
                 const struct file_operations *format)
 {
+       struct list_head *head;
        int ret;
 
        /*
         * If the trace point header did not define TRACE_SYSTEM
         * then the system would be called "TRACE_SYSTEM".
         */
-       if (strcmp(call->system, TRACE_SYSTEM) != 0)
-               d_events = event_subsystem_dir(call->system, d_events);
+       if (strcmp(call->class->system, TRACE_SYSTEM) != 0)
+               d_events = event_subsystem_dir(call->class->system, d_events);
 
        call->dir = debugfs_create_dir(call->name, d_events);
        if (!call->dir) {
@@ -935,22 +963,31 @@ event_create_dir(struct ftrace_event_call *call, struct dentry *d_events,
                return -1;
        }
 
-       if (call->regfunc)
+       if (call->class->probe || call->class->reg)
                trace_create_file("enable", 0644, call->dir, call,
                                  enable);
 
-       if (call->id && call->perf_event_enable)
+#ifdef CONFIG_PERF_EVENTS
+       if (call->event.type && (call->class->perf_probe || call->class->reg))
                trace_create_file("id", 0444, call->dir, call,
                                  id);
+#endif
 
-       if (call->define_fields) {
-               ret = trace_define_common_fields(call);
-               if (!ret)
-                       ret = call->define_fields(call);
-               if (ret < 0) {
-                       pr_warning("Could not initialize trace point"
-                                  " events/%s\n", call->name);
-                       return ret;
+       if (call->class->define_fields) {
+               /*
+                * Other events may have the same class. Only update
+                * the fields if they are not already defined.
+                */
+               head = trace_get_fields(call);
+               if (list_empty(head)) {
+                       ret = trace_define_common_fields(call);
+                       if (!ret)
+                               ret = call->class->define_fields(call);
+                       if (ret < 0) {
+                               pr_warning("Could not initialize trace point"
+                                          " events/%s\n", call->name);
+                               return ret;
+                       }
                }
                trace_create_file("filter", 0644, call->dir, call,
                                  filter);
@@ -970,8 +1007,8 @@ static int __trace_add_event_call(struct ftrace_event_call *call)
        if (!call->name)
                return -EINVAL;
 
-       if (call->raw_init) {
-               ret = call->raw_init(call);
+       if (call->class->raw_init) {
+               ret = call->class->raw_init(call);
                if (ret < 0) {
                        if (ret != -ENOSYS)
                                pr_warning("Could not initialize trace "
@@ -1035,13 +1072,13 @@ static void remove_subsystem_dir(const char *name)
 static void __trace_remove_event_call(struct ftrace_event_call *call)
 {
        ftrace_event_enable_disable(call, 0);
-       if (call->event)
-               __unregister_ftrace_event(call->event);
+       if (call->event.funcs)
+               __unregister_ftrace_event(&call->event);
        debugfs_remove_recursive(call->dir);
        list_del(&call->list);
        trace_destroy_fields(call);
        destroy_preds(call);
-       remove_subsystem_dir(call->system);
+       remove_subsystem_dir(call->class->system);
 }
 
 /* Remove an event_call */
@@ -1132,8 +1169,8 @@ static void trace_module_add_events(struct module *mod)
                /* The linker may leave blanks */
                if (!call->name)
                        continue;
-               if (call->raw_init) {
-                       ret = call->raw_init(call);
+               if (call->class->raw_init) {
+                       ret = call->class->raw_init(call);
                        if (ret < 0) {
                                if (ret != -ENOSYS)
                                        pr_warning("Could not initialize trace "
@@ -1286,8 +1323,8 @@ static __init int event_trace_init(void)
                /* The linker may leave blanks */
                if (!call->name)
                        continue;
-               if (call->raw_init) {
-                       ret = call->raw_init(call);
+               if (call->class->raw_init) {
+                       ret = call->class->raw_init(call);
                        if (ret < 0) {
                                if (ret != -ENOSYS)
                                        pr_warning("Could not initialize trace "
@@ -1388,8 +1425,8 @@ static __init void event_trace_self_tests(void)
 
        list_for_each_entry(call, &ftrace_events, list) {
 
-               /* Only test those that have a regfunc */
-               if (!call->regfunc)
+               /* Only test those that have a probe */
+               if (!call->class || !call->class->probe)
                        continue;
 
 /*
@@ -1399,8 +1436,8 @@ static __init void event_trace_self_tests(void)
  * syscalls as we test.
  */
 #ifndef CONFIG_EVENT_TRACE_TEST_SYSCALLS
-               if (call->system &&
-                   strcmp(call->system, "syscalls") == 0)
+               if (call->class->system &&
+                   strcmp(call->class->system, "syscalls") == 0)
                        continue;
 #endif
 
@@ -1410,7 +1447,7 @@ static __init void event_trace_self_tests(void)
                 * If an event is already enabled, someone is using
                 * it and the self test should not be on.
                 */
-               if (call->enabled) {
+               if (call->flags & TRACE_EVENT_FL_ENABLED) {
                        pr_warning("Enabled event during self test!\n");
                        WARN_ON_ONCE(1);
                        continue;
index 58092d8..57bb1bb 100644 (file)
@@ -500,8 +500,10 @@ static struct ftrace_event_field *
 find_event_field(struct ftrace_event_call *call, char *name)
 {
        struct ftrace_event_field *field;
+       struct list_head *head;
 
-       list_for_each_entry(field, &call->fields, link) {
+       head = trace_get_fields(call);
+       list_for_each_entry(field, head, link) {
                if (!strcmp(field->name, name))
                        return field;
        }
@@ -545,7 +547,7 @@ static void filter_disable_preds(struct ftrace_event_call *call)
        struct event_filter *filter = call->filter;
        int i;
 
-       call->filter_active = 0;
+       call->flags &= ~TRACE_EVENT_FL_FILTERED;
        filter->n_preds = 0;
 
        for (i = 0; i < MAX_FILTER_PRED; i++)
@@ -572,7 +574,7 @@ void destroy_preds(struct ftrace_event_call *call)
 {
        __free_preds(call->filter);
        call->filter = NULL;
-       call->filter_active = 0;
+       call->flags &= ~TRACE_EVENT_FL_FILTERED;
 }
 
 static struct event_filter *__alloc_preds(void)
@@ -611,7 +613,7 @@ static int init_preds(struct ftrace_event_call *call)
        if (call->filter)
                return 0;
 
-       call->filter_active = 0;
+       call->flags &= ~TRACE_EVENT_FL_FILTERED;
        call->filter = __alloc_preds();
        if (IS_ERR(call->filter))
                return PTR_ERR(call->filter);
@@ -625,10 +627,10 @@ static int init_subsystem_preds(struct event_subsystem *system)
        int err;
 
        list_for_each_entry(call, &ftrace_events, list) {
-               if (!call->define_fields)
+               if (!call->class || !call->class->define_fields)
                        continue;
 
-               if (strcmp(call->system, system->name) != 0)
+               if (strcmp(call->class->system, system->name) != 0)
                        continue;
 
                err = init_preds(call);
@@ -644,10 +646,10 @@ static void filter_free_subsystem_preds(struct event_subsystem *system)
        struct ftrace_event_call *call;
 
        list_for_each_entry(call, &ftrace_events, list) {
-               if (!call->define_fields)
+               if (!call->class || !call->class->define_fields)
                        continue;
 
-               if (strcmp(call->system, system->name) != 0)
+               if (strcmp(call->class->system, system->name) != 0)
                        continue;
 
                filter_disable_preds(call);
@@ -1249,10 +1251,10 @@ static int replace_system_preds(struct event_subsystem *system,
        list_for_each_entry(call, &ftrace_events, list) {
                struct event_filter *filter = call->filter;
 
-               if (!call->define_fields)
+               if (!call->class || !call->class->define_fields)
                        continue;
 
-               if (strcmp(call->system, system->name) != 0)
+               if (strcmp(call->class->system, system->name) != 0)
                        continue;
 
                /* try to see if the filter can be applied */
@@ -1266,7 +1268,7 @@ static int replace_system_preds(struct event_subsystem *system,
                if (err)
                        filter_disable_preds(call);
                else {
-                       call->filter_active = 1;
+                       call->flags |= TRACE_EVENT_FL_FILTERED;
                        replace_filter_string(filter, filter_string);
                }
                fail = false;
@@ -1315,7 +1317,7 @@ int apply_event_filter(struct ftrace_event_call *call, char *filter_string)
        if (err)
                append_filter_err(ps, call->filter);
        else
-               call->filter_active = 1;
+               call->flags |= TRACE_EVENT_FL_FILTERED;
 out:
        filter_opstack_clear(ps);
        postfix_clear(ps);
@@ -1393,7 +1395,7 @@ int ftrace_profile_set_filter(struct perf_event *event, int event_id,
        mutex_lock(&event_mutex);
 
        list_for_each_entry(call, &ftrace_events, list) {
-               if (call->id == event_id)
+               if (call->event.type == event_id)
                        break;
        }
 
index e091f64..8536e2a 100644 (file)
@@ -127,7 +127,7 @@ ftrace_define_fields_##name(struct ftrace_event_call *event_call)   \
 
 static int ftrace_raw_init_event(struct ftrace_event_call *call)
 {
-       INIT_LIST_HEAD(&call->fields);
+       INIT_LIST_HEAD(&call->class->fields);
        return 0;
 }
 
@@ -153,17 +153,21 @@ static int ftrace_raw_init_event(struct ftrace_event_call *call)
 #define F_printk(fmt, args...) #fmt ", "  __stringify(args)
 
 #undef FTRACE_ENTRY
-#define FTRACE_ENTRY(call, struct_name, type, tstruct, print)          \
+#define FTRACE_ENTRY(call, struct_name, etype, tstruct, print)         \
+                                                                       \
+struct ftrace_event_class event_class_ftrace_##call = {                        \
+       .system                 = __stringify(TRACE_SYSTEM),            \
+       .define_fields          = ftrace_define_fields_##call,          \
+       .raw_init               = ftrace_raw_init_event,                \
+};                                                                     \
                                                                        \
 struct ftrace_event_call __used                                                \
 __attribute__((__aligned__(4)))                                                \
 __attribute__((section("_ftrace_events"))) event_##call = {            \
        .name                   = #call,                                \
-       .id                     = type,                                 \
-       .system                 = __stringify(TRACE_SYSTEM),            \
-       .raw_init               = ftrace_raw_init_event,                \
+       .event.type             = etype,                                \
+       .class                  = &event_class_ftrace_##call,           \
        .print_fmt              = print,                                \
-       .define_fields          = ftrace_define_fields_##call,          \
 };                                                                     \
 
 #include "trace_entries.h"
index dd11c83..79f4bac 100644 (file)
@@ -1025,7 +1025,7 @@ print_graph_comment(struct trace_seq *s, struct trace_entry *ent,
                if (!event)
                        return TRACE_TYPE_UNHANDLED;
 
-               ret = event->trace(iter, sym_flags);
+               ret = event->funcs->trace(iter, sym_flags, event);
                if (ret != TRACE_TYPE_HANDLED)
                        return ret;
        }
@@ -1112,7 +1112,8 @@ print_graph_function(struct trace_iterator *iter)
 }
 
 static enum print_line_t
-print_graph_function_event(struct trace_iterator *iter, int flags)
+print_graph_function_event(struct trace_iterator *iter, int flags,
+                          struct trace_event *event)
 {
        return print_graph_function(iter);
 }
@@ -1225,14 +1226,18 @@ void graph_trace_close(struct trace_iterator *iter)
        }
 }
 
+static struct trace_event_functions graph_functions = {
+       .trace          = print_graph_function_event,
+};
+
 static struct trace_event graph_trace_entry_event = {
        .type           = TRACE_GRAPH_ENT,
-       .trace          = print_graph_function_event,
+       .funcs          = &graph_functions,
 };
 
 static struct trace_event graph_trace_ret_event = {
        .type           = TRACE_GRAPH_RET,
-       .trace          = print_graph_function_event,
+       .funcs          = &graph_functions
 };
 
 static struct tracer graph_trace __read_mostly = {
index a751432..faf7cef 100644 (file)
@@ -324,8 +324,8 @@ struct trace_probe {
        unsigned long           nhit;
        unsigned int            flags;  /* For TP_FLAG_* */
        const char              *symbol;        /* symbol name */
+       struct ftrace_event_class       class;
        struct ftrace_event_call        call;
-       struct trace_event              event;
        ssize_t                 size;           /* trace entry size */
        unsigned int            nr_args;
        struct probe_arg        args[];
@@ -404,6 +404,7 @@ static struct trace_probe *alloc_trace_probe(const char *group,
                goto error;
        }
 
+       tp->call.class = &tp->class;
        tp->call.name = kstrdup(event, GFP_KERNEL);
        if (!tp->call.name)
                goto error;
@@ -413,8 +414,8 @@ static struct trace_probe *alloc_trace_probe(const char *group,
                goto error;
        }
 
-       tp->call.system = kstrdup(group, GFP_KERNEL);
-       if (!tp->call.system)
+       tp->class.system = kstrdup(group, GFP_KERNEL);
+       if (!tp->class.system)
                goto error;
 
        INIT_LIST_HEAD(&tp->list);
@@ -443,7 +444,7 @@ static void free_trace_probe(struct trace_probe *tp)
        for (i = 0; i < tp->nr_args; i++)
                free_probe_arg(&tp->args[i]);
 
-       kfree(tp->call.system);
+       kfree(tp->call.class->system);
        kfree(tp->call.name);
        kfree(tp->symbol);
        kfree(tp);
@@ -456,7 +457,7 @@ static struct trace_probe *find_probe_event(const char *event,
 
        list_for_each_entry(tp, &probe_list, list)
                if (strcmp(tp->call.name, event) == 0 &&
-                   strcmp(tp->call.system, group) == 0)
+                   strcmp(tp->call.class->system, group) == 0)
                        return tp;
        return NULL;
 }
@@ -481,7 +482,7 @@ static int register_trace_probe(struct trace_probe *tp)
        mutex_lock(&probe_lock);
 
        /* register as an event */
-       old_tp = find_probe_event(tp->call.name, tp->call.system);
+       old_tp = find_probe_event(tp->call.name, tp->call.class->system);
        if (old_tp) {
                /* delete old event */
                unregister_trace_probe(old_tp);
@@ -904,7 +905,7 @@ static int probes_seq_show(struct seq_file *m, void *v)
        int i;
 
        seq_printf(m, "%c", probe_is_return(tp) ? 'r' : 'p');
-       seq_printf(m, ":%s/%s", tp->call.system, tp->call.name);
+       seq_printf(m, ":%s/%s", tp->call.class->system, tp->call.name);
 
        if (!tp->symbol)
                seq_printf(m, " 0x%p", tp->rp.kp.addr);
@@ -1061,8 +1062,8 @@ static __kprobes void kprobe_trace_func(struct kprobe *kp, struct pt_regs *regs)
 
        size = sizeof(*entry) + tp->size;
 
-       event = trace_current_buffer_lock_reserve(&buffer, call->id, size,
-                                                 irq_flags, pc);
+       event = trace_current_buffer_lock_reserve(&buffer, call->event.type,
+                                                 size, irq_flags, pc);
        if (!event)
                return;
 
@@ -1094,8 +1095,8 @@ static __kprobes void kretprobe_trace_func(struct kretprobe_instance *ri,
 
        size = sizeof(*entry) + tp->size;
 
-       event = trace_current_buffer_lock_reserve(&buffer, call->id, size,
-                                                 irq_flags, pc);
+       event = trace_current_buffer_lock_reserve(&buffer, call->event.type,
+                                                 size, irq_flags, pc);
        if (!event)
                return;
 
@@ -1112,18 +1113,17 @@ static __kprobes void kretprobe_trace_func(struct kretprobe_instance *ri,
 
 /* Event entry printers */
 enum print_line_t
-print_kprobe_event(struct trace_iterator *iter, int flags)
+print_kprobe_event(struct trace_iterator *iter, int flags,
+                  struct trace_event *event)
 {
        struct kprobe_trace_entry_head *field;
        struct trace_seq *s = &iter->seq;
-       struct trace_event *event;
        struct trace_probe *tp;
        u8 *data;
        int i;
 
        field = (struct kprobe_trace_entry_head *)iter->ent;
-       event = ftrace_find_event(field->ent.type);
-       tp = container_of(event, struct trace_probe, event);
+       tp = container_of(event, struct trace_probe, call.event);
 
        if (!trace_seq_printf(s, "%s: (", tp->call.name))
                goto partial;
@@ -1149,18 +1149,17 @@ partial:
 }
 
 enum print_line_t
-print_kretprobe_event(struct trace_iterator *iter, int flags)
+print_kretprobe_event(struct trace_iterator *iter, int flags,
+                     struct trace_event *event)
 {
        struct kretprobe_trace_entry_head *field;
        struct trace_seq *s = &iter->seq;
-       struct trace_event *event;
        struct trace_probe *tp;
        u8 *data;
        int i;
 
        field = (struct kretprobe_trace_entry_head *)iter->ent;
-       event = ftrace_find_event(field->ent.type);
-       tp = container_of(event, struct trace_probe, event);
+       tp = container_of(event, struct trace_probe, call.event);
 
        if (!trace_seq_printf(s, "%s: (", tp->call.name))
                goto partial;
@@ -1217,8 +1216,6 @@ static void probe_event_disable(struct ftrace_event_call *call)
 
 static int probe_event_raw_init(struct ftrace_event_call *event_call)
 {
-       INIT_LIST_HEAD(&event_call->fields);
-
        return 0;
 }
 
@@ -1341,9 +1338,9 @@ static __kprobes void kprobe_perf_func(struct kprobe *kp,
        struct trace_probe *tp = container_of(kp, struct trace_probe, rp.kp);
        struct ftrace_event_call *call = &tp->call;
        struct kprobe_trace_entry_head *entry;
+       struct hlist_head *head;
        u8 *data;
        int size, __size, i;
-       unsigned long irq_flags;
        int rctx;
 
        __size = sizeof(*entry) + tp->size;
@@ -1353,7 +1350,7 @@ static __kprobes void kprobe_perf_func(struct kprobe *kp,
                     "profile buffer not large enough"))
                return;
 
-       entry = perf_trace_buf_prepare(size, call->id, &rctx, &irq_flags);
+       entry = perf_trace_buf_prepare(size, call->event.type, regs, &rctx);
        if (!entry)
                return;
 
@@ -1362,7 +1359,8 @@ static __kprobes void kprobe_perf_func(struct kprobe *kp,
        for (i = 0; i < tp->nr_args; i++)
                call_fetch(&tp->args[i].fetch, regs, data + tp->args[i].offset);
 
-       perf_trace_buf_submit(entry, size, rctx, entry->ip, 1, irq_flags, regs);
+       head = per_cpu_ptr(call->perf_events, smp_processor_id());
+       perf_trace_buf_submit(entry, size, rctx, entry->ip, 1, regs, head);
 }
 
 /* Kretprobe profile handler */
@@ -1372,9 +1370,9 @@ static __kprobes void kretprobe_perf_func(struct kretprobe_instance *ri,
        struct trace_probe *tp = container_of(ri->rp, struct trace_probe, rp);
        struct ftrace_event_call *call = &tp->call;
        struct kretprobe_trace_entry_head *entry;
+       struct hlist_head *head;
        u8 *data;
        int size, __size, i;
-       unsigned long irq_flags;
        int rctx;
 
        __size = sizeof(*entry) + tp->size;
@@ -1384,7 +1382,7 @@ static __kprobes void kretprobe_perf_func(struct kretprobe_instance *ri,
                     "profile buffer not large enough"))
                return;
 
-       entry = perf_trace_buf_prepare(size, call->id, &rctx, &irq_flags);
+       entry = perf_trace_buf_prepare(size, call->event.type, regs, &rctx);
        if (!entry)
                return;
 
@@ -1394,8 +1392,8 @@ static __kprobes void kretprobe_perf_func(struct kretprobe_instance *ri,
        for (i = 0; i < tp->nr_args; i++)
                call_fetch(&tp->args[i].fetch, regs, data + tp->args[i].offset);
 
-       perf_trace_buf_submit(entry, size, rctx, entry->ret_ip, 1,
-                              irq_flags, regs);
+       head = per_cpu_ptr(call->perf_events, smp_processor_id());
+       perf_trace_buf_submit(entry, size, rctx, entry->ret_ip, 1, regs, head);
 }
 
 static int probe_perf_enable(struct ftrace_event_call *call)
@@ -1425,6 +1423,26 @@ static void probe_perf_disable(struct ftrace_event_call *call)
 }
 #endif /* CONFIG_PERF_EVENTS */
 
+static __kprobes
+int kprobe_register(struct ftrace_event_call *event, enum trace_reg type)
+{
+       switch (type) {
+       case TRACE_REG_REGISTER:
+               return probe_event_enable(event);
+       case TRACE_REG_UNREGISTER:
+               probe_event_disable(event);
+               return 0;
+
+#ifdef CONFIG_PERF_EVENTS
+       case TRACE_REG_PERF_REGISTER:
+               return probe_perf_enable(event);
+       case TRACE_REG_PERF_UNREGISTER:
+               probe_perf_disable(event);
+               return 0;
+#endif
+       }
+       return 0;
+}
 
 static __kprobes
 int kprobe_dispatcher(struct kprobe *kp, struct pt_regs *regs)
@@ -1454,6 +1472,14 @@ int kretprobe_dispatcher(struct kretprobe_instance *ri, struct pt_regs *regs)
        return 0;       /* We don't tweek kernel, so just return 0 */
 }
 
+static struct trace_event_functions kretprobe_funcs = {
+       .trace          = print_kretprobe_event
+};
+
+static struct trace_event_functions kprobe_funcs = {
+       .trace          = print_kprobe_event
+};
+
 static int register_probe_event(struct trace_probe *tp)
 {
        struct ftrace_event_call *call = &tp->call;
@@ -1461,36 +1487,31 @@ static int register_probe_event(struct trace_probe *tp)
 
        /* Initialize ftrace_event_call */
        if (probe_is_return(tp)) {
-               tp->event.trace = print_kretprobe_event;
-               call->raw_init = probe_event_raw_init;
-               call->define_fields = kretprobe_event_define_fields;
+               INIT_LIST_HEAD(&call->class->fields);
+               call->event.funcs = &kretprobe_funcs;
+               call->class->raw_init = probe_event_raw_init;
+               call->class->define_fields = kretprobe_event_define_fields;
        } else {
-               tp->event.trace = print_kprobe_event;
-               call->raw_init = probe_event_raw_init;
-               call->define_fields = kprobe_event_define_fields;
+               INIT_LIST_HEAD(&call->class->fields);
+               call->event.funcs = &kprobe_funcs;
+               call->class->raw_init = probe_event_raw_init;
+               call->class->define_fields = kprobe_event_define_fields;
        }
        if (set_print_fmt(tp) < 0)
                return -ENOMEM;
-       call->event = &tp->event;
-       call->id = register_ftrace_event(&tp->event);
-       if (!call->id) {
+       ret = register_ftrace_event(&call->event);
+       if (!ret) {
                kfree(call->print_fmt);
                return -ENODEV;
        }
-       call->enabled = 0;
-       call->regfunc = probe_event_enable;
-       call->unregfunc = probe_event_disable;
-
-#ifdef CONFIG_PERF_EVENTS
-       call->perf_event_enable = probe_perf_enable;
-       call->perf_event_disable = probe_perf_disable;
-#endif
+       call->flags = 0;
+       call->class->reg = kprobe_register;
        call->data = tp;
        ret = trace_add_event_call(call);
        if (ret) {
                pr_info("Failed to register kprobe event: %s\n", call->name);
                kfree(call->print_fmt);
-               unregister_ftrace_event(&tp->event);
+               unregister_ftrace_event(&call->event);
        }
        return ret;
 }
index ab13d70..57c1b45 100644 (file)
@@ -742,6 +742,9 @@ int register_ftrace_event(struct trace_event *event)
        if (WARN_ON(!event))
                goto out;
 
+       if (WARN_ON(!event->funcs))
+               goto out;
+
        INIT_LIST_HEAD(&event->list);
 
        if (!event->type) {
@@ -774,14 +777,14 @@ int register_ftrace_event(struct trace_event *event)
                        goto out;
        }
 
-       if (event->trace == NULL)
-               event->trace = trace_nop_print;
-       if (event->raw == NULL)
-               event->raw = trace_nop_print;
-       if (event->hex == NULL)
-               event->hex = trace_nop_print;
-       if (event->binary == NULL)
-               event->binary = trace_nop_print;
+       if (event->funcs->trace == NULL)
+               event->funcs->trace = trace_nop_print;
+       if (event->funcs->raw == NULL)
+               event->funcs->raw = trace_nop_print;
+       if (event->funcs->hex == NULL)
+               event->funcs->hex = trace_nop_print;
+       if (event->funcs->binary == NULL)
+               event->funcs->binary = trace_nop_print;
 
        key = event->type & (EVENT_HASHSIZE - 1);
 
@@ -823,13 +826,15 @@ EXPORT_SYMBOL_GPL(unregister_ftrace_event);
  * Standard events
  */
 
-enum print_line_t trace_nop_print(struct trace_iterator *iter, int flags)
+enum print_line_t trace_nop_print(struct trace_iterator *iter, int flags,
+                                 struct trace_event *event)
 {
        return TRACE_TYPE_HANDLED;
 }
 
 /* TRACE_FN */
-static enum print_line_t trace_fn_trace(struct trace_iterator *iter, int flags)
+static enum print_line_t trace_fn_trace(struct trace_iterator *iter, int flags,
+                                       struct trace_event *event)
 {
        struct ftrace_entry *field;
        struct trace_seq *s = &iter->seq;
@@ -856,7 +861,8 @@ static enum print_line_t trace_fn_trace(struct trace_iterator *iter, int flags)
        return TRACE_TYPE_PARTIAL_LINE;
 }
 
-static enum print_line_t trace_fn_raw(struct trace_iterator *iter, int flags)
+static enum print_line_t trace_fn_raw(struct trace_iterator *iter, int flags,
+                                     struct trace_event *event)
 {
        struct ftrace_entry *field;
 
@@ -870,7 +876,8 @@ static enum print_line_t trace_fn_raw(struct trace_iterator *iter, int flags)
        return TRACE_TYPE_HANDLED;
 }
 
-static enum print_line_t trace_fn_hex(struct trace_iterator *iter, int flags)
+static enum print_line_t trace_fn_hex(struct trace_iterator *iter, int flags,
+                                     struct trace_event *event)
 {
        struct ftrace_entry *field;
        struct trace_seq *s = &iter->seq;
@@ -883,7 +890,8 @@ static enum print_line_t trace_fn_hex(struct trace_iterator *iter, int flags)
        return TRACE_TYPE_HANDLED;
 }
 
-static enum print_line_t trace_fn_bin(struct trace_iterator *iter, int flags)
+static enum print_line_t trace_fn_bin(struct trace_iterator *iter, int flags,
+                                     struct trace_event *event)
 {
        struct ftrace_entry *field;
        struct trace_seq *s = &iter->seq;
@@ -896,14 +904,18 @@ static enum print_line_t trace_fn_bin(struct trace_iterator *iter, int flags)
        return TRACE_TYPE_HANDLED;
 }
 
-static struct trace_event trace_fn_event = {
-       .type           = TRACE_FN,
+static struct trace_event_functions trace_fn_funcs = {
        .trace          = trace_fn_trace,
        .raw            = trace_fn_raw,
        .hex            = trace_fn_hex,
        .binary         = trace_fn_bin,
 };
 
+static struct trace_event trace_fn_event = {
+       .type           = TRACE_FN,
+       .funcs          = &trace_fn_funcs,
+};
+
 /* TRACE_CTX an TRACE_WAKE */
 static enum print_line_t trace_ctxwake_print(struct trace_iterator *iter,
                                             char *delim)
@@ -932,13 +944,14 @@ static enum print_line_t trace_ctxwake_print(struct trace_iterator *iter,
        return TRACE_TYPE_HANDLED;
 }
 
-static enum print_line_t trace_ctx_print(struct trace_iterator *iter, int flags)
+static enum print_line_t trace_ctx_print(struct trace_iterator *iter, int flags,
+                                        struct trace_event *event)
 {
        return trace_ctxwake_print(iter, "==>");
 }
 
 static enum print_line_t trace_wake_print(struct trace_iterator *iter,
-                                         int flags)
+                                         int flags, struct trace_event *event)
 {
        return trace_ctxwake_print(iter, "  +");
 }
@@ -966,12 +979,14 @@ static int trace_ctxwake_raw(struct trace_iterator *iter, char S)
        return TRACE_TYPE_HANDLED;
 }
 
-static enum print_line_t trace_ctx_raw(struct trace_iterator *iter, int flags)
+static enum print_line_t trace_ctx_raw(struct trace_iterator *iter, int flags,
+                                      struct trace_event *event)
 {
        return trace_ctxwake_raw(iter, 0);
 }
 
-static enum print_line_t trace_wake_raw(struct trace_iterator *iter, int flags)
+static enum print_line_t trace_wake_raw(struct trace_iterator *iter, int flags,
+                                       struct trace_event *event)
 {
        return trace_ctxwake_raw(iter, '+');
 }
@@ -1000,18 +1015,20 @@ static int trace_ctxwake_hex(struct trace_iterator *iter, char S)
        return TRACE_TYPE_HANDLED;
 }
 
-static enum print_line_t trace_ctx_hex(struct trace_iterator *iter, int flags)
+static enum print_line_t trace_ctx_hex(struct trace_iterator *iter, int flags,
+                                      struct trace_event *event)
 {
        return trace_ctxwake_hex(iter, 0);
 }
 
-static enum print_line_t trace_wake_hex(struct trace_iterator *iter, int flags)
+static enum print_line_t trace_wake_hex(struct trace_iterator *iter, int flags,
+                                       struct trace_event *event)
 {
        return trace_ctxwake_hex(iter, '+');
 }
 
 static enum print_line_t trace_ctxwake_bin(struct trace_iterator *iter,
-                                          int flags)
+                                          int flags, struct trace_event *event)
 {
        struct ctx_switch_entry *field;
        struct trace_seq *s = &iter->seq;
@@ -1028,25 +1045,33 @@ static enum print_line_t trace_ctxwake_bin(struct trace_iterator *iter,
        return TRACE_TYPE_HANDLED;
 }
 
-static struct trace_event trace_ctx_event = {
-       .type           = TRACE_CTX,
+static struct trace_event_functions trace_ctx_funcs = {
        .trace          = trace_ctx_print,
        .raw            = trace_ctx_raw,
        .hex            = trace_ctx_hex,
        .binary         = trace_ctxwake_bin,
 };
 
-static struct trace_event trace_wake_event = {
-       .type           = TRACE_WAKE,
+static struct trace_event trace_ctx_event = {
+       .type           = TRACE_CTX,
+       .funcs          = &trace_ctx_funcs,
+};
+
+static struct trace_event_functions trace_wake_funcs = {
        .trace          = trace_wake_print,
        .raw            = trace_wake_raw,
        .hex            = trace_wake_hex,
        .binary         = trace_ctxwake_bin,
 };
 
+static struct trace_event trace_wake_event = {
+       .type           = TRACE_WAKE,
+       .funcs          = &trace_wake_funcs,
+};
+
 /* TRACE_SPECIAL */
 static enum print_line_t trace_special_print(struct trace_iterator *iter,
-                                            int flags)
+                                            int flags, struct trace_event *event)
 {
        struct special_entry *field;
 
@@ -1062,7 +1087,7 @@ static enum print_line_t trace_special_print(struct trace_iterator *iter,
 }
 
 static enum print_line_t trace_special_hex(struct trace_iterator *iter,
-                                          int flags)
+                                          int flags, struct trace_event *event)
 {
        struct special_entry *field;
        struct trace_seq *s = &iter->seq;
@@ -1077,7 +1102,7 @@ static enum print_line_t trace_special_hex(struct trace_iterator *iter,
 }
 
 static enum print_line_t trace_special_bin(struct trace_iterator *iter,
-                                          int flags)
+                                          int flags, struct trace_event *event)
 {
        struct special_entry *field;
        struct trace_seq *s = &iter->seq;
@@ -1091,18 +1116,22 @@ static enum print_line_t trace_special_bin(struct trace_iterator *iter,
        return TRACE_TYPE_HANDLED;
 }
 
-static struct trace_event trace_special_event = {
-       .type           = TRACE_SPECIAL,
+static struct trace_event_functions trace_special_funcs = {
        .trace          = trace_special_print,
        .raw            = trace_special_print,
        .hex            = trace_special_hex,
        .binary         = trace_special_bin,
 };
 
+static struct trace_event trace_special_event = {
+       .type           = TRACE_SPECIAL,
+       .funcs          = &trace_special_funcs,
+};
+
 /* TRACE_STACK */
 
 static enum print_line_t trace_stack_print(struct trace_iterator *iter,
-                                          int flags)
+                                          int flags, struct trace_event *event)
 {
        struct stack_entry *field;
        struct trace_seq *s = &iter->seq;
@@ -1130,17 +1159,21 @@ static enum print_line_t trace_stack_print(struct trace_iterator *iter,
        return TRACE_TYPE_PARTIAL_LINE;
 }
 
-static struct trace_event trace_stack_event = {
-       .type           = TRACE_STACK,
+static struct trace_event_functions trace_stack_funcs = {
        .trace          = trace_stack_print,
        .raw            = trace_special_print,
        .hex            = trace_special_hex,
        .binary         = trace_special_bin,
 };
 
+static struct trace_event trace_stack_event = {
+       .type           = TRACE_STACK,
+       .funcs          = &trace_stack_funcs,
+};
+
 /* TRACE_USER_STACK */
 static enum print_line_t trace_user_stack_print(struct trace_iterator *iter,
-                                               int flags)
+                                               int flags, struct trace_event *event)
 {
        struct userstack_entry *field;
        struct trace_seq *s = &iter->seq;
@@ -1159,17 +1192,22 @@ static enum print_line_t trace_user_stack_print(struct trace_iterator *iter,
        return TRACE_TYPE_PARTIAL_LINE;
 }
 
-static struct trace_event trace_user_stack_event = {
-       .type           = TRACE_USER_STACK,
+static struct trace_event_functions trace_user_stack_funcs = {
        .trace          = trace_user_stack_print,
        .raw            = trace_special_print,
        .hex            = trace_special_hex,
        .binary         = trace_special_bin,
 };
 
+static struct trace_event trace_user_stack_event = {
+       .type           = TRACE_USER_STACK,
+       .funcs          = &trace_user_stack_funcs,
+};
+
 /* TRACE_BPRINT */
 static enum print_line_t
-trace_bprint_print(struct trace_iterator *iter, int flags)
+trace_bprint_print(struct trace_iterator *iter, int flags,
+                  struct trace_event *event)
 {
        struct trace_entry *entry = iter->ent;
        struct trace_seq *s = &iter->seq;
@@ -1194,7 +1232,8 @@ trace_bprint_print(struct trace_iterator *iter, int flags)
 
 
 static enum print_line_t
-trace_bprint_raw(struct trace_iterator *iter, int flags)
+trace_bprint_raw(struct trace_iterator *iter, int flags,
+                struct trace_event *event)
 {
        struct bprint_entry *field;
        struct trace_seq *s = &iter->seq;
@@ -1213,16 +1252,19 @@ trace_bprint_raw(struct trace_iterator *iter, int flags)
        return TRACE_TYPE_PARTIAL_LINE;
 }
 
+static struct trace_event_functions trace_bprint_funcs = {
+       .trace          = trace_bprint_print,
+       .raw            = trace_bprint_raw,
+};
 
 static struct trace_event trace_bprint_event = {
        .type           = TRACE_BPRINT,
-       .trace          = trace_bprint_print,
-       .raw            = trace_bprint_raw,
+       .funcs          = &trace_bprint_funcs,
 };
 
 /* TRACE_PRINT */
 static enum print_line_t trace_print_print(struct trace_iterator *iter,
-                                          int flags)
+                                          int flags, struct trace_event *event)
 {
        struct print_entry *field;
        struct trace_seq *s = &iter->seq;
@@ -1241,7 +1283,8 @@ static enum print_line_t trace_print_print(struct trace_iterator *iter,
        return TRACE_TYPE_PARTIAL_LINE;
 }
 
-static enum print_line_t trace_print_raw(struct trace_iterator *iter, int flags)
+static enum print_line_t trace_print_raw(struct trace_iterator *iter, int flags,
+                                        struct trace_event *event)
 {
        struct print_entry *field;
 
@@ -1256,12 +1299,16 @@ static enum print_line_t trace_print_raw(struct trace_iterator *iter, int flags)
        return TRACE_TYPE_PARTIAL_LINE;
 }
 
-static struct trace_event trace_print_event = {
-       .type           = TRACE_PRINT,
+static struct trace_event_functions trace_print_funcs = {
        .trace          = trace_print_print,
        .raw            = trace_print_raw,
 };
 
+static struct trace_event trace_print_event = {
+       .type           = TRACE_PRINT,
+       .funcs          = &trace_print_funcs,
+};
+
 
 static struct trace_event *events[] __initdata = {
        &trace_fn_event,
index 9d91c72..c038eba 100644 (file)
@@ -25,7 +25,7 @@ extern void trace_event_read_unlock(void);
 extern struct trace_event *ftrace_find_event(int type);
 
 extern enum print_line_t trace_nop_print(struct trace_iterator *iter,
-                                        int flags);
+                                        int flags, struct trace_event *event);
 extern int
 trace_print_lat_fmt(struct trace_seq *s, struct trace_entry *entry);
 
index a55fccf..8f758d0 100644 (file)
@@ -50,7 +50,7 @@ tracing_sched_switch_trace(struct trace_array *tr,
 }
 
 static void
-probe_sched_switch(struct task_struct *prev, struct task_struct *next)
+probe_sched_switch(void *ignore, struct task_struct *prev, struct task_struct *next)
 {
        struct trace_array_cpu *data;
        unsigned long flags;
@@ -108,7 +108,7 @@ tracing_sched_wakeup_trace(struct trace_array *tr,
 }
 
 static void
-probe_sched_wakeup(struct task_struct *wakee, int success)
+probe_sched_wakeup(void *ignore, struct task_struct *wakee, int success)
 {
        struct trace_array_cpu *data;
        unsigned long flags;
@@ -138,21 +138,21 @@ static int tracing_sched_register(void)
 {
        int ret;
 
-       ret = register_trace_sched_wakeup(probe_sched_wakeup);
+       ret = register_trace_sched_wakeup(probe_sched_wakeup, NULL);
        if (ret) {
                pr_info("wakeup trace: Couldn't activate tracepoint"
                        " probe to kernel_sched_wakeup\n");
                return ret;
        }
 
-       ret = register_trace_sched_wakeup_new(probe_sched_wakeup);
+       ret = register_trace_sched_wakeup_new(probe_sched_wakeup, NULL);
        if (ret) {
                pr_info("wakeup trace: Couldn't activate tracepoint"
                        " probe to kernel_sched_wakeup_new\n");
                goto fail_deprobe;
        }
 
-       ret = register_trace_sched_switch(probe_sched_switch);
+       ret = register_trace_sched_switch(probe_sched_switch, NULL);
        if (ret) {
                pr_info("sched trace: Couldn't activate tracepoint"
                        " probe to kernel_sched_switch\n");
@@ -161,17 +161,17 @@ static int tracing_sched_register(void)
 
        return ret;
 fail_deprobe_wake_new:
-       unregister_trace_sched_wakeup_new(probe_sched_wakeup);
+       unregister_trace_sched_wakeup_new(probe_sched_wakeup, NULL);
 fail_deprobe:
-       unregister_trace_sched_wakeup(probe_sched_wakeup);
+       unregister_trace_sched_wakeup(probe_sched_wakeup, NULL);
        return ret;
 }
 
 static void tracing_sched_unregister(void)
 {
-       unregister_trace_sched_switch(probe_sched_switch);
-       unregister_trace_sched_wakeup_new(probe_sched_wakeup);
-       unregister_trace_sched_wakeup(probe_sched_wakeup);
+       unregister_trace_sched_switch(probe_sched_switch, NULL);
+       unregister_trace_sched_wakeup_new(probe_sched_wakeup, NULL);
+       unregister_trace_sched_wakeup(probe_sched_wakeup, NULL);
 }
 
 static void tracing_start_sched_switch(void)
index 8052446..0e73bc2 100644 (file)
@@ -98,7 +98,8 @@ static int report_latency(cycle_t delta)
        return 1;
 }
 
-static void probe_wakeup_migrate_task(struct task_struct *task, int cpu)
+static void
+probe_wakeup_migrate_task(void *ignore, struct task_struct *task, int cpu)
 {
        if (task != wakeup_task)
                return;
@@ -107,7 +108,8 @@ static void probe_wakeup_migrate_task(struct task_struct *task, int cpu)
 }
 
 static void notrace
-probe_wakeup_sched_switch(struct task_struct *prev, struct task_struct *next)
+probe_wakeup_sched_switch(void *ignore,
+                         struct task_struct *prev, struct task_struct *next)
 {
        struct trace_array_cpu *data;
        cycle_t T0, T1, delta;
@@ -199,7 +201,7 @@ static void wakeup_reset(struct trace_array *tr)
 }
 
 static void
-probe_wakeup(struct task_struct *p, int success)
+probe_wakeup(void *ignore, struct task_struct *p, int success)
 {
        struct trace_array_cpu *data;
        int cpu = smp_processor_id();
@@ -263,28 +265,28 @@ static void start_wakeup_tracer(struct trace_array *tr)
 {
        int ret;
 
-       ret = register_trace_sched_wakeup(probe_wakeup);
+       ret = register_trace_sched_wakeup(probe_wakeup, NULL);
        if (ret) {
                pr_info("wakeup trace: Couldn't activate tracepoint"
                        " probe to kernel_sched_wakeup\n");
                return;
        }
 
-       ret = register_trace_sched_wakeup_new(probe_wakeup);
+       ret = register_trace_sched_wakeup_new(probe_wakeup, NULL);
        if (ret) {
                pr_info("wakeup trace: Couldn't activate tracepoint"
                        " probe to kernel_sched_wakeup_new\n");
                goto fail_deprobe;
        }
 
-       ret = register_trace_sched_switch(probe_wakeup_sched_switch);
+       ret = register_trace_sched_switch(probe_wakeup_sched_switch, NULL);
        if (ret) {
                pr_info("sched trace: Couldn't activate tracepoint"
                        " probe to kernel_sched_switch\n");
                goto fail_deprobe_wake_new;
        }
 
-       ret = register_trace_sched_migrate_task(probe_wakeup_migrate_task);
+       ret = register_trace_sched_migrate_task(probe_wakeup_migrate_task, NULL);
        if (ret) {
                pr_info("wakeup trace: Couldn't activate tracepoint"
                        " probe to kernel_sched_migrate_task\n");
@@ -311,19 +313,19 @@ static void start_wakeup_tracer(struct trace_array *tr)
 
        return;
 fail_deprobe_wake_new:
-       unregister_trace_sched_wakeup_new(probe_wakeup);
+       unregister_trace_sched_wakeup_new(probe_wakeup, NULL);
 fail_deprobe:
-       unregister_trace_sched_wakeup(probe_wakeup);
+       unregister_trace_sched_wakeup(probe_wakeup, NULL);
 }
 
 static void stop_wakeup_tracer(struct trace_array *tr)
 {
        tracer_enabled = 0;
        unregister_ftrace_function(&trace_ops);
-       unregister_trace_sched_switch(probe_wakeup_sched_switch);
-       unregister_trace_sched_wakeup_new(probe_wakeup);
-       unregister_trace_sched_wakeup(probe_wakeup);
-       unregister_trace_sched_migrate_task(probe_wakeup_migrate_task);
+       unregister_trace_sched_switch(probe_wakeup_sched_switch, NULL);
+       unregister_trace_sched_wakeup_new(probe_wakeup, NULL);
+       unregister_trace_sched_wakeup(probe_wakeup, NULL);
+       unregister_trace_sched_migrate_task(probe_wakeup_migrate_task, NULL);
 }
 
 static int __wakeup_tracer_init(struct trace_array *tr)
index 4d6d711..d2c859c 100644 (file)
@@ -15,6 +15,54 @@ static int sys_refcount_exit;
 static DECLARE_BITMAP(enabled_enter_syscalls, NR_syscalls);
 static DECLARE_BITMAP(enabled_exit_syscalls, NR_syscalls);
 
+static int syscall_enter_register(struct ftrace_event_call *event,
+                                enum trace_reg type);
+static int syscall_exit_register(struct ftrace_event_call *event,
+                                enum trace_reg type);
+
+static int syscall_enter_define_fields(struct ftrace_event_call *call);
+static int syscall_exit_define_fields(struct ftrace_event_call *call);
+
+static struct list_head *
+syscall_get_enter_fields(struct ftrace_event_call *call)
+{
+       struct syscall_metadata *entry = call->data;
+
+       return &entry->enter_fields;
+}
+
+static struct list_head *
+syscall_get_exit_fields(struct ftrace_event_call *call)
+{
+       struct syscall_metadata *entry = call->data;
+
+       return &entry->exit_fields;
+}
+
+struct trace_event_functions enter_syscall_print_funcs = {
+       .trace                  = print_syscall_enter,
+};
+
+struct trace_event_functions exit_syscall_print_funcs = {
+       .trace                  = print_syscall_exit,
+};
+
+struct ftrace_event_class event_class_syscall_enter = {
+       .system                 = "syscalls",
+       .reg                    = syscall_enter_register,
+       .define_fields          = syscall_enter_define_fields,
+       .get_fields             = syscall_get_enter_fields,
+       .raw_init               = init_syscall_trace,
+};
+
+struct ftrace_event_class event_class_syscall_exit = {
+       .system                 = "syscalls",
+       .reg                    = syscall_exit_register,
+       .define_fields          = syscall_exit_define_fields,
+       .get_fields             = syscall_get_exit_fields,
+       .raw_init               = init_syscall_trace,
+};
+
 extern unsigned long __start_syscalls_metadata[];
 extern unsigned long __stop_syscalls_metadata[];
 
@@ -53,7 +101,8 @@ static struct syscall_metadata *syscall_nr_to_meta(int nr)
 }
 
 enum print_line_t
-print_syscall_enter(struct trace_iterator *iter, int flags)
+print_syscall_enter(struct trace_iterator *iter, int flags,
+                   struct trace_event *event)
 {
        struct trace_seq *s = &iter->seq;
        struct trace_entry *ent = iter->ent;
@@ -68,7 +117,7 @@ print_syscall_enter(struct trace_iterator *iter, int flags)
        if (!entry)
                goto end;
 
-       if (entry->enter_event->id != ent->type) {
+       if (entry->enter_event->event.type != ent->type) {
                WARN_ON_ONCE(1);
                goto end;
        }
@@ -105,7 +154,8 @@ end:
 }
 
 enum print_line_t
-print_syscall_exit(struct trace_iterator *iter, int flags)
+print_syscall_exit(struct trace_iterator *iter, int flags,
+                  struct trace_event *event)
 {
        struct trace_seq *s = &iter->seq;
        struct trace_entry *ent = iter->ent;
@@ -123,7 +173,7 @@ print_syscall_exit(struct trace_iterator *iter, int flags)
                return TRACE_TYPE_HANDLED;
        }
 
-       if (entry->exit_event->id != ent->type) {
+       if (entry->exit_event->event.type != ent->type) {
                WARN_ON_ONCE(1);
                return TRACE_TYPE_UNHANDLED;
        }
@@ -205,7 +255,7 @@ static void free_syscall_print_fmt(struct ftrace_event_call *call)
                kfree(call->print_fmt);
 }
 
-int syscall_enter_define_fields(struct ftrace_event_call *call)
+static int syscall_enter_define_fields(struct ftrace_event_call *call)
 {
        struct syscall_trace_enter trace;
        struct syscall_metadata *meta = call->data;
@@ -228,7 +278,7 @@ int syscall_enter_define_fields(struct ftrace_event_call *call)
        return ret;
 }
 
-int syscall_exit_define_fields(struct ftrace_event_call *call)
+static int syscall_exit_define_fields(struct ftrace_event_call *call)
 {
        struct syscall_trace_exit trace;
        int ret;
@@ -243,7 +293,7 @@ int syscall_exit_define_fields(struct ftrace_event_call *call)
        return ret;
 }
 
-void ftrace_syscall_enter(struct pt_regs *regs, long id)
+void ftrace_syscall_enter(void *ignore, struct pt_regs *regs, long id)
 {
        struct syscall_trace_enter *entry;
        struct syscall_metadata *sys_data;
@@ -265,7 +315,7 @@ void ftrace_syscall_enter(struct pt_regs *regs, long id)
        size = sizeof(*entry) + sizeof(unsigned long) * sys_data->nb_args;
 
        event = trace_current_buffer_lock_reserve(&buffer,
-                       sys_data->enter_event->id, size, 0, 0);
+                       sys_data->enter_event->event.type, size, 0, 0);
        if (!event)
                return;
 
@@ -278,7 +328,7 @@ void ftrace_syscall_enter(struct pt_regs *regs, long id)
                trace_current_buffer_unlock_commit(buffer, event, 0, 0);
 }
 
-void ftrace_syscall_exit(struct pt_regs *regs, long ret)
+void ftrace_syscall_exit(void *ignore, struct pt_regs *regs, long ret)
 {
        struct syscall_trace_exit *entry;
        struct syscall_metadata *sys_data;
@@ -297,7 +347,7 @@ void ftrace_syscall_exit(struct pt_regs *regs, long ret)
                return;
 
        event = trace_current_buffer_lock_reserve(&buffer,
-                       sys_data->exit_event->id, sizeof(*entry), 0, 0);
+                       sys_data->exit_event->event.type, sizeof(*entry), 0, 0);
        if (!event)
                return;
 
@@ -320,7 +370,7 @@ int reg_event_syscall_enter(struct ftrace_event_call *call)
                return -ENOSYS;
        mutex_lock(&syscall_trace_lock);
        if (!sys_refcount_enter)
-               ret = register_trace_sys_enter(ftrace_syscall_enter);
+               ret = register_trace_sys_enter(ftrace_syscall_enter, NULL);
        if (!ret) {
                set_bit(num, enabled_enter_syscalls);
                sys_refcount_enter++;
@@ -340,7 +390,7 @@ void unreg_event_syscall_enter(struct ftrace_event_call *call)
        sys_refcount_enter--;
        clear_bit(num, enabled_enter_syscalls);
        if (!sys_refcount_enter)
-               unregister_trace_sys_enter(ftrace_syscall_enter);
+               unregister_trace_sys_enter(ftrace_syscall_enter, NULL);
        mutex_unlock(&syscall_trace_lock);
 }
 
@@ -354,7 +404,7 @@ int reg_event_syscall_exit(struct ftrace_event_call *call)
                return -ENOSYS;
        mutex_lock(&syscall_trace_lock);
        if (!sys_refcount_exit)
-               ret = register_trace_sys_exit(ftrace_syscall_exit);
+               ret = register_trace_sys_exit(ftrace_syscall_exit, NULL);
        if (!ret) {
                set_bit(num, enabled_exit_syscalls);
                sys_refcount_exit++;
@@ -374,7 +424,7 @@ void unreg_event_syscall_exit(struct ftrace_event_call *call)
        sys_refcount_exit--;
        clear_bit(num, enabled_exit_syscalls);
        if (!sys_refcount_exit)
-               unregister_trace_sys_exit(ftrace_syscall_exit);
+               unregister_trace_sys_exit(ftrace_syscall_exit, NULL);
        mutex_unlock(&syscall_trace_lock);
 }
 
@@ -434,11 +484,11 @@ static DECLARE_BITMAP(enabled_perf_exit_syscalls, NR_syscalls);
 static int sys_perf_refcount_enter;
 static int sys_perf_refcount_exit;
 
-static void perf_syscall_enter(struct pt_regs *regs, long id)
+static void perf_syscall_enter(void *ignore, struct pt_regs *regs, long id)
 {
        struct syscall_metadata *sys_data;
        struct syscall_trace_enter *rec;
-       unsigned long flags;
+       struct hlist_head *head;
        int syscall_nr;
        int rctx;
        int size;
@@ -461,14 +511,16 @@ static void perf_syscall_enter(struct pt_regs *regs, long id)
                return;
 
        rec = (struct syscall_trace_enter *)perf_trace_buf_prepare(size,
-                               sys_data->enter_event->id, &rctx, &flags);
+                               sys_data->enter_event->event.type, regs, &rctx);
        if (!rec)
                return;
 
        rec->nr = syscall_nr;
        syscall_get_arguments(current, regs, 0, sys_data->nb_args,
                               (unsigned long *)&rec->args);
-       perf_trace_buf_submit(rec, size, rctx, 0, 1, flags, regs);
+
+       head = per_cpu_ptr(sys_data->enter_event->perf_events, smp_processor_id());
+       perf_trace_buf_submit(rec, size, rctx, 0, 1, regs, head);
 }
 
 int perf_sysenter_enable(struct ftrace_event_call *call)
@@ -480,7 +532,7 @@ int perf_sysenter_enable(struct ftrace_event_call *call)
 
        mutex_lock(&syscall_trace_lock);
        if (!sys_perf_refcount_enter)
-               ret = register_trace_sys_enter(perf_syscall_enter);
+               ret = register_trace_sys_enter(perf_syscall_enter, NULL);
        if (ret) {
                pr_info("event trace: Could not activate"
                                "syscall entry trace point");
@@ -502,15 +554,15 @@ void perf_sysenter_disable(struct ftrace_event_call *call)
        sys_perf_refcount_enter--;
        clear_bit(num, enabled_perf_enter_syscalls);
        if (!sys_perf_refcount_enter)
-               unregister_trace_sys_enter(perf_syscall_enter);
+               unregister_trace_sys_enter(perf_syscall_enter, NULL);
        mutex_unlock(&syscall_trace_lock);
 }
 
-static void perf_syscall_exit(struct pt_regs *regs, long ret)
+static void perf_syscall_exit(void *ignore, struct pt_regs *regs, long ret)
 {
        struct syscall_metadata *sys_data;
        struct syscall_trace_exit *rec;
-       unsigned long flags;
+       struct hlist_head *head;
        int syscall_nr;
        int rctx;
        int size;
@@ -536,14 +588,15 @@ static void perf_syscall_exit(struct pt_regs *regs, long ret)
                return;
 
        rec = (struct syscall_trace_exit *)perf_trace_buf_prepare(size,
-                               sys_data->exit_event->id, &rctx, &flags);
+                               sys_data->exit_event->event.type, regs, &rctx);
        if (!rec)
                return;
 
        rec->nr = syscall_nr;
        rec->ret = syscall_get_return_value(current, regs);
 
-       perf_trace_buf_submit(rec, size, rctx, 0, 1, flags, regs);
+       head = per_cpu_ptr(sys_data->exit_event->perf_events, smp_processor_id());
+       perf_trace_buf_submit(rec, size, rctx, 0, 1, regs, head);
 }
 
 int perf_sysexit_enable(struct ftrace_event_call *call)
@@ -555,7 +608,7 @@ int perf_sysexit_enable(struct ftrace_event_call *call)
 
        mutex_lock(&syscall_trace_lock);
        if (!sys_perf_refcount_exit)
-               ret = register_trace_sys_exit(perf_syscall_exit);
+               ret = register_trace_sys_exit(perf_syscall_exit, NULL);
        if (ret) {
                pr_info("event trace: Could not activate"
                                "syscall exit trace point");
@@ -577,9 +630,50 @@ void perf_sysexit_disable(struct ftrace_event_call *call)
        sys_perf_refcount_exit--;
        clear_bit(num, enabled_perf_exit_syscalls);
        if (!sys_perf_refcount_exit)
-               unregister_trace_sys_exit(perf_syscall_exit);
+               unregister_trace_sys_exit(perf_syscall_exit, NULL);
        mutex_unlock(&syscall_trace_lock);
 }
 
 #endif /* CONFIG_PERF_EVENTS */
 
+static int syscall_enter_register(struct ftrace_event_call *event,
+                                enum trace_reg type)
+{
+       switch (type) {
+       case TRACE_REG_REGISTER:
+               return reg_event_syscall_enter(event);
+       case TRACE_REG_UNREGISTER:
+               unreg_event_syscall_enter(event);
+               return 0;
+
+#ifdef CONFIG_PERF_EVENTS
+       case TRACE_REG_PERF_REGISTER:
+               return perf_sysenter_enable(event);
+       case TRACE_REG_PERF_UNREGISTER:
+               perf_sysenter_disable(event);
+               return 0;
+#endif
+       }
+       return 0;
+}
+
+static int syscall_exit_register(struct ftrace_event_call *event,
+                                enum trace_reg type)
+{
+       switch (type) {
+       case TRACE_REG_REGISTER:
+               return reg_event_syscall_exit(event);
+       case TRACE_REG_UNREGISTER:
+               unreg_event_syscall_exit(event);
+               return 0;
+
+#ifdef CONFIG_PERF_EVENTS
+       case TRACE_REG_PERF_REGISTER:
+               return perf_sysexit_enable(event);
+       case TRACE_REG_PERF_UNREGISTER:
+               perf_sysexit_disable(event);
+               return 0;
+#endif
+       }
+       return 0;
+}
index cc2d2fa..a7cc379 100644 (file)
@@ -49,7 +49,8 @@ static void cpu_workqueue_stat_free(struct kref *kref)
 
 /* Insertion of a work */
 static void
-probe_workqueue_insertion(struct task_struct *wq_thread,
+probe_workqueue_insertion(void *ignore,
+                         struct task_struct *wq_thread,
                          struct work_struct *work)
 {
        int cpu = cpumask_first(&wq_thread->cpus_allowed);
@@ -70,7 +71,8 @@ found:
 
 /* Execution of a work */
 static void
-probe_workqueue_execution(struct task_struct *wq_thread,
+probe_workqueue_execution(void *ignore,
+                         struct task_struct *wq_thread,
                          struct work_struct *work)
 {
        int cpu = cpumask_first(&wq_thread->cpus_allowed);
@@ -90,7 +92,8 @@ found:
 }
 
 /* Creation of a cpu workqueue thread */
-static void probe_workqueue_creation(struct task_struct *wq_thread, int cpu)
+static void probe_workqueue_creation(void *ignore,
+                                    struct task_struct *wq_thread, int cpu)
 {
        struct cpu_workqueue_stats *cws;
        unsigned long flags;
@@ -114,7 +117,8 @@ static void probe_workqueue_creation(struct task_struct *wq_thread, int cpu)
 }
 
 /* Destruction of a cpu workqueue thread */
-static void probe_workqueue_destruction(struct task_struct *wq_thread)
+static void
+probe_workqueue_destruction(void *ignore, struct task_struct *wq_thread)
 {
        /* Workqueue only execute on one cpu */
        int cpu = cpumask_first(&wq_thread->cpus_allowed);
@@ -259,19 +263,19 @@ int __init trace_workqueue_early_init(void)
 {
        int ret, cpu;
 
-       ret = register_trace_workqueue_insertion(probe_workqueue_insertion);
+       ret = register_trace_workqueue_insertion(probe_workqueue_insertion, NULL);
        if (ret)
                goto out;
 
-       ret = register_trace_workqueue_execution(probe_workqueue_execution);
+       ret = register_trace_workqueue_execution(probe_workqueue_execution, NULL);
        if (ret)
                goto no_insertion;
 
-       ret = register_trace_workqueue_creation(probe_workqueue_creation);
+       ret = register_trace_workqueue_creation(probe_workqueue_creation, NULL);
        if (ret)
                goto no_execution;
 
-       ret = register_trace_workqueue_destruction(probe_workqueue_destruction);
+       ret = register_trace_workqueue_destruction(probe_workqueue_destruction, NULL);
        if (ret)
                goto no_creation;
 
@@ -283,11 +287,11 @@ int __init trace_workqueue_early_init(void)
        return 0;
 
 no_creation:
-       unregister_trace_workqueue_creation(probe_workqueue_creation);
+       unregister_trace_workqueue_creation(probe_workqueue_creation, NULL);
 no_execution:
-       unregister_trace_workqueue_execution(probe_workqueue_execution);
+       unregister_trace_workqueue_execution(probe_workqueue_execution, NULL);
 no_insertion:
-       unregister_trace_workqueue_insertion(probe_workqueue_insertion);
+       unregister_trace_workqueue_insertion(probe_workqueue_insertion, NULL);
 out:
        pr_warning("trace_workqueue: unable to trace workqueues\n");
 
index cc89be5..c77f3ec 100644 (file)
@@ -54,7 +54,7 @@ static struct hlist_head tracepoint_table[TRACEPOINT_TABLE_SIZE];
  */
 struct tracepoint_entry {
        struct hlist_node hlist;
-       void **funcs;
+       struct tracepoint_func *funcs;
        int refcount;   /* Number of times armed. 0 if disarmed. */
        char name[0];
 };
@@ -64,12 +64,12 @@ struct tp_probes {
                struct rcu_head rcu;
                struct list_head list;
        } u;
-       void *probes[0];
+       struct tracepoint_func probes[0];
 };
 
 static inline void *allocate_probes(int count)
 {
-       struct tp_probes *p  = kmalloc(count * sizeof(void *)
+       struct tp_probes *p  = kmalloc(count * sizeof(struct tracepoint_func)
                        + sizeof(struct tp_probes), GFP_KERNEL);
        return p == NULL ? NULL : p->probes;
 }
@@ -79,7 +79,7 @@ static void rcu_free_old_probes(struct rcu_head *head)
        kfree(container_of(head, struct tp_probes, u.rcu));
 }
 
-static inline void release_probes(void *old)
+static inline void release_probes(struct tracepoint_func *old)
 {
        if (old) {
                struct tp_probes *tp_probes = container_of(old,
@@ -95,15 +95,16 @@ static void debug_print_probes(struct tracepoint_entry *entry)
        if (!tracepoint_debug || !entry->funcs)
                return;
 
-       for (i = 0; entry->funcs[i]; i++)
-               printk(KERN_DEBUG "Probe %d : %p\n", i, entry->funcs[i]);
+       for (i = 0; entry->funcs[i].func; i++)
+               printk(KERN_DEBUG "Probe %d : %p\n", i, entry->funcs[i].func);
 }
 
-static void *
-tracepoint_entry_add_probe(struct tracepoint_entry *entry, void *probe)
+static struct tracepoint_func *
+tracepoint_entry_add_probe(struct tracepoint_entry *entry,
+                          void *probe, void *data)
 {
        int nr_probes = 0;
-       void **old, **new;
+       struct tracepoint_func *old, *new;
 
        WARN_ON(!probe);
 
@@ -111,8 +112,9 @@ tracepoint_entry_add_probe(struct tracepoint_entry *entry, void *probe)
        old = entry->funcs;
        if (old) {
                /* (N -> N+1), (N != 0, 1) probes */
-               for (nr_probes = 0; old[nr_probes]; nr_probes++)
-                       if (old[nr_probes] == probe)
+               for (nr_probes = 0; old[nr_probes].func; nr_probes++)
+                       if (old[nr_probes].func == probe &&
+                           old[nr_probes].data == data)
                                return ERR_PTR(-EEXIST);
        }
        /* + 2 : one for new probe, one for NULL func */
@@ -120,9 +122,10 @@ tracepoint_entry_add_probe(struct tracepoint_entry *entry, void *probe)
        if (new == NULL)
                return ERR_PTR(-ENOMEM);
        if (old)
-               memcpy(new, old, nr_probes * sizeof(void *));
-       new[nr_probes] = probe;
-       new[nr_probes + 1] = NULL;
+               memcpy(new, old, nr_probes * sizeof(struct tracepoint_func));
+       new[nr_probes].func = probe;
+       new[nr_probes].data = data;
+       new[nr_probes + 1].func = NULL;
        entry->refcount = nr_probes + 1;
        entry->funcs = new;
        debug_print_probes(entry);
@@ -130,10 +133,11 @@ tracepoint_entry_add_probe(struct tracepoint_entry *entry, void *probe)
 }
 
 static void *
-tracepoint_entry_remove_probe(struct tracepoint_entry *entry, void *probe)
+tracepoint_entry_remove_probe(struct tracepoint_entry *entry,
+                             void *probe, void *data)
 {
        int nr_probes = 0, nr_del = 0, i;
-       void **old, **new;
+       struct tracepoint_func *old, *new;
 
        old = entry->funcs;
 
@@ -142,8 +146,10 @@ tracepoint_entry_remove_probe(struct tracepoint_entry *entry, void *probe)
 
        debug_print_probes(entry);
        /* (N -> M), (N > 1, M >= 0) probes */
-       for (nr_probes = 0; old[nr_probes]; nr_probes++) {
-               if ((!probe || old[nr_probes] == probe))
+       for (nr_probes = 0; old[nr_probes].func; nr_probes++) {
+               if (!probe ||
+                   (old[nr_probes].func == probe &&
+                    old[nr_probes].data == data))
                        nr_del++;
        }
 
@@ -160,10 +166,11 @@ tracepoint_entry_remove_probe(struct tracepoint_entry *entry, void *probe)
                new = allocate_probes(nr_probes - nr_del + 1);
                if (new == NULL)
                        return ERR_PTR(-ENOMEM);
-               for (i = 0; old[i]; i++)
-                       if ((probe && old[i] != probe))
+               for (i = 0; old[i].func; i++)
+                       if (probe &&
+                           (old[i].func != probe || old[i].data != data))
                                new[j++] = old[i];
-               new[nr_probes - nr_del] = NULL;
+               new[nr_probes - nr_del].func = NULL;
                entry->refcount = nr_probes - nr_del;
                entry->funcs = new;
        }
@@ -315,18 +322,19 @@ static void tracepoint_update_probes(void)
        module_update_tracepoints();
 }
 
-static void *tracepoint_add_probe(const char *name, void *probe)
+static struct tracepoint_func *
+tracepoint_add_probe(const char *name, void *probe, void *data)
 {
        struct tracepoint_entry *entry;
-       void *old;
+       struct tracepoint_func *old;
 
        entry = get_tracepoint(name);
        if (!entry) {
                entry = add_tracepoint(name);
                if (IS_ERR(entry))
-                       return entry;
+                       return (struct tracepoint_func *)entry;
        }
-       old = tracepoint_entry_add_probe(entry, probe);
+       old = tracepoint_entry_add_probe(entry, probe, data);
        if (IS_ERR(old) && !entry->refcount)
                remove_tracepoint(entry);
        return old;
@@ -340,12 +348,12 @@ static void *tracepoint_add_probe(const char *name, void *probe)
  * Returns 0 if ok, error value on error.
  * The probe address must at least be aligned on the architecture pointer size.
  */
-int tracepoint_probe_register(const char *name, void *probe)
+int tracepoint_probe_register(const char *name, void *probe, void *data)
 {
-       void *old;
+       struct tracepoint_func *old;
 
        mutex_lock(&tracepoints_mutex);
-       old = tracepoint_add_probe(name, probe);
+       old = tracepoint_add_probe(name, probe, data);
        mutex_unlock(&tracepoints_mutex);
        if (IS_ERR(old))
                return PTR_ERR(old);
@@ -356,15 +364,16 @@ int tracepoint_probe_register(const char *name, void *probe)
 }
 EXPORT_SYMBOL_GPL(tracepoint_probe_register);
 
-static void *tracepoint_remove_probe(const char *name, void *probe)
+static struct tracepoint_func *
+tracepoint_remove_probe(const char *name, void *probe, void *data)
 {
        struct tracepoint_entry *entry;
-       void *old;
+       struct tracepoint_func *old;
 
        entry = get_tracepoint(name);
        if (!entry)
                return ERR_PTR(-ENOENT);
-       old = tracepoint_entry_remove_probe(entry, probe);
+       old = tracepoint_entry_remove_probe(entry, probe, data);
        if (IS_ERR(old))
                return old;
        if (!entry->refcount)
@@ -382,12 +391,12 @@ static void *tracepoint_remove_probe(const char *name, void *probe)
  * itself uses stop_machine(), which insures that every preempt disabled section
  * have finished.
  */
-int tracepoint_probe_unregister(const char *name, void *probe)
+int tracepoint_probe_unregister(const char *name, void *probe, void *data)
 {
-       void *old;
+       struct tracepoint_func *old;
 
        mutex_lock(&tracepoints_mutex);
-       old = tracepoint_remove_probe(name, probe);
+       old = tracepoint_remove_probe(name, probe, data);
        mutex_unlock(&tracepoints_mutex);
        if (IS_ERR(old))
                return PTR_ERR(old);
@@ -418,12 +427,13 @@ static void tracepoint_add_old_probes(void *old)
  *
  * caller must call tracepoint_probe_update_all()
  */
-int tracepoint_probe_register_noupdate(const char *name, void *probe)
+int tracepoint_probe_register_noupdate(const char *name, void *probe,
+                                      void *data)
 {
-       void *old;
+       struct tracepoint_func *old;
 
        mutex_lock(&tracepoints_mutex);
-       old = tracepoint_add_probe(name, probe);
+       old = tracepoint_add_probe(name, probe, data);
        if (IS_ERR(old)) {
                mutex_unlock(&tracepoints_mutex);
                return PTR_ERR(old);
@@ -441,12 +451,13 @@ EXPORT_SYMBOL_GPL(tracepoint_probe_register_noupdate);
  *
  * caller must call tracepoint_probe_update_all()
  */
-int tracepoint_probe_unregister_noupdate(const char *name, void *probe)
+int tracepoint_probe_unregister_noupdate(const char *name, void *probe,
+                                        void *data)
 {
-       void *old;
+       struct tracepoint_func *old;
 
        mutex_lock(&tracepoints_mutex);
-       old = tracepoint_remove_probe(name, probe);
+       old = tracepoint_remove_probe(name, probe, data);
        if (IS_ERR(old)) {
                mutex_unlock(&tracepoints_mutex);
                return PTR_ERR(old);
index c8567a5..3f1062c 100644 (file)
@@ -21,7 +21,7 @@ lib-y += kobject.o kref.o klist.o
 
 obj-y += bcd.o div64.o sort.o parser.o halfmd4.o debug_locks.o random32.o \
         bust_spinlocks.o hexdump.o kasprintf.o bitmap.o scatterlist.o \
-        string_helpers.o gcd.o lcm.o list_sort.o
+        string_helpers.o gcd.o lcm.o list_sort.o uuid.o
 
 ifeq ($(CONFIG_DEBUG_KOBJECT),y)
 CFLAGS_kobject.o += -DDEBUG
index 65e482c..9087d71 100644 (file)
@@ -9,6 +9,7 @@
  * (at your option) any later version.
  */
 #include <linux/init.h>
+#include <linux/kernel.h>
 #include <asm/atomic.h>
 
 #define INIT(c) do { atomic64_set(&v, c); r = c; } while (0)
index d7137e7..ffb78c9 100644 (file)
@@ -672,7 +672,7 @@ static int bitmap_pos_to_ord(const unsigned long *buf, int pos, int bits)
  *
  * The bit positions 0 through @bits are valid positions in @buf.
  */
-int bitmap_ord_to_pos(const unsigned long *buf, int ord, int bits)
+static int bitmap_ord_to_pos(const unsigned long *buf, int ord, int bits)
 {
        int pos = 0;
 
diff --git a/lib/uuid.c b/lib/uuid.c
new file mode 100644 (file)
index 0000000..8fadd7c
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * Unified UUID/GUID definition
+ *
+ * Copyright (C) 2009, Intel Corp.
+ *     Huang Ying <ying.huang@intel.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License version
+ * 2 as published by the Free Software Foundation;
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/uuid.h>
+#include <linux/random.h>
+
+static void __uuid_gen_common(__u8 b[16])
+{
+       int i;
+       u32 r;
+
+       for (i = 0; i < 4; i++) {
+               r = random32();
+               memcpy(b + i * 4, &r, 4);
+       }
+       /* reversion 0b10 */
+       b[8] = (b[8] & 0x3F) | 0x80;
+}
+
+void uuid_le_gen(uuid_le *lu)
+{
+       __uuid_gen_common(lu->b);
+       /* version 4 : random generation */
+       lu->b[7] = (lu->b[7] & 0x0F) | 0x40;
+}
+EXPORT_SYMBOL_GPL(uuid_le_gen);
+
+void uuid_be_gen(uuid_be *bu)
+{
+       __uuid_gen_common(bu->b);
+       /* version 4 : random generation */
+       bu->b[6] = (bu->b[6] & 0x0F) | 0x40;
+}
+EXPORT_SYMBOL_GPL(uuid_be_gen);
index 855eaf5..7e5030a 100644 (file)
@@ -727,10 +727,11 @@ done2:
        if (inode->i_mapping->nrpages && (info->flags & SHMEM_PAGEIN)) {
                /*
                 * Call truncate_inode_pages again: racing shmem_unuse_inode
-                * may have swizzled a page in from swap since vmtruncate or
-                * generic_delete_inode did it, before we lowered next_index.
-                * Also, though shmem_getpage checks i_size before adding to
-                * cache, no recheck after: so fix the narrow window there too.
+                * may have swizzled a page in from swap since
+                * truncate_pagecache or generic_delete_inode did it, before we
+                * lowered next_index.  Also, though shmem_getpage checks
+                * i_size before adding to cache, no recheck after: so fix the
+                * narrow window there too.
                 *
                 * Recalling truncate_inode_pages_range and unmap_mapping_range
                 * every time for punch_hole (which never got a chance to clear
@@ -760,19 +761,16 @@ done2:
        }
 }
 
-static void shmem_truncate(struct inode *inode)
-{
-       shmem_truncate_range(inode, inode->i_size, (loff_t)-1);
-}
-
 static int shmem_notify_change(struct dentry *dentry, struct iattr *attr)
 {
        struct inode *inode = dentry->d_inode;
-       struct page *page = NULL;
        int error;
 
        if (S_ISREG(inode->i_mode) && (attr->ia_valid & ATTR_SIZE)) {
-               if (attr->ia_size < inode->i_size) {
+               loff_t newsize = attr->ia_size;
+               struct page *page = NULL;
+
+               if (newsize < inode->i_size) {
                        /*
                         * If truncating down to a partial page, then
                         * if that page is already allocated, hold it
@@ -780,9 +778,9 @@ static int shmem_notify_change(struct dentry *dentry, struct iattr *attr)
                         * truncate_partial_page cannnot miss it were
                         * it assigned to swap.
                         */
-                       if (attr->ia_size & (PAGE_CACHE_SIZE-1)) {
+                       if (newsize & (PAGE_CACHE_SIZE-1)) {
                                (void) shmem_getpage(inode,
-                                       attr->ia_size>>PAGE_CACHE_SHIFT,
+                                       newsize >> PAGE_CACHE_SHIFT,
                                                &page, SGP_READ, NULL);
                                if (page)
                                        unlock_page(page);
@@ -794,24 +792,29 @@ static int shmem_notify_change(struct dentry *dentry, struct iattr *attr)
                         * if it's being fully truncated to zero-length: the
                         * nrpages check is efficient enough in that case.
                         */
-                       if (attr->ia_size) {
+                       if (newsize) {
                                struct shmem_inode_info *info = SHMEM_I(inode);
                                spin_lock(&info->lock);
                                info->flags &= ~SHMEM_PAGEIN;
                                spin_unlock(&info->lock);
                        }
                }
+
+               error = simple_setsize(inode, newsize);
+               if (page)
+                       page_cache_release(page);
+               if (error)
+                       return error;
+               shmem_truncate_range(inode, newsize, (loff_t)-1);
        }
 
        error = inode_change_ok(inode, attr);
        if (!error)
-               error = inode_setattr(inode, attr);
+               generic_setattr(inode, attr);
 #ifdef CONFIG_TMPFS_POSIX_ACL
        if (!error && (attr->ia_valid & ATTR_MODE))
                error = generic_acl_chmod(inode);
 #endif
-       if (page)
-               page_cache_release(page);
        return error;
 }
 
@@ -819,11 +822,11 @@ static void shmem_delete_inode(struct inode *inode)
 {
        struct shmem_inode_info *info = SHMEM_I(inode);
 
-       if (inode->i_op->truncate == shmem_truncate) {
+       if (inode->i_mapping->a_ops == &shmem_aops) {
                truncate_inode_pages(inode->i_mapping, 0);
                shmem_unacct_size(info->flags, inode->i_size);
                inode->i_size = 0;
-               shmem_truncate(inode);
+               shmem_truncate_range(inode, 0, (loff_t)-1);
                if (!list_empty(&info->swaplist)) {
                        mutex_lock(&shmem_swaplist_mutex);
                        list_del_init(&info->swaplist);
@@ -2022,7 +2025,6 @@ static const struct inode_operations shmem_symlink_inline_operations = {
 };
 
 static const struct inode_operations shmem_symlink_inode_operations = {
-       .truncate       = shmem_truncate,
        .readlink       = generic_readlink,
        .follow_link    = shmem_follow_link,
        .put_link       = shmem_put_link,
@@ -2433,14 +2435,13 @@ static const struct file_operations shmem_file_operations = {
        .write          = do_sync_write,
        .aio_read       = shmem_file_aio_read,
        .aio_write      = generic_file_aio_write,
-       .fsync          = simple_sync_file,
+       .fsync          = noop_fsync,
        .splice_read    = generic_file_splice_read,
        .splice_write   = generic_file_splice_write,
 #endif
 };
 
 static const struct inode_operations shmem_inode_operations = {
-       .truncate       = shmem_truncate,
        .setattr        = shmem_notify_change,
        .truncate_range = shmem_truncate_range,
 #ifdef CONFIG_TMPFS_POSIX_ACL
index f42675a..937571b 100644 (file)
@@ -548,18 +548,18 @@ EXPORT_SYMBOL(truncate_pagecache);
  * NOTE! We have to be ready to update the memory sharing
  * between the file and the memory map for a potential last
  * incomplete page.  Ugly, but necessary.
+ *
+ * This function is deprecated and simple_setsize or truncate_pagecache
+ * should be used instead.
  */
 int vmtruncate(struct inode *inode, loff_t offset)
 {
-       loff_t oldsize;
        int error;
 
-       error = inode_newsize_ok(inode, offset);
+       error = simple_setsize(inode, offset);
        if (error)
                return error;
-       oldsize = inode->i_size;
-       i_size_write(inode, offset);
-       truncate_pagecache(inode, oldsize, offset);
+
        if (inode->i_op->truncate)
                inode->i_op->truncate(inode);
 
index e009753..f5b6f43 100644 (file)
@@ -229,15 +229,17 @@ EXPORT_SYMBOL(skb_free_datagram);
 
 void skb_free_datagram_locked(struct sock *sk, struct sk_buff *skb)
 {
+       bool slow;
+
        if (likely(atomic_read(&skb->users) == 1))
                smp_rmb();
        else if (likely(!atomic_dec_and_test(&skb->users)))
                return;
 
-       lock_sock_bh(sk);
+       slow = lock_sock_fast(sk);
        skb_orphan(skb);
        sk_mem_reclaim_partial(sk);
-       unlock_sock_bh(sk);
+       unlock_sock_fast(sk, slow);
 
        /* skb is now orphaned, can be freed outside of locked section */
        __kfree_skb(skb);
index cf208d8..ad41529 100644 (file)
@@ -172,12 +172,12 @@ out:
        return;
 }
 
-static void trace_kfree_skb_hit(struct sk_buff *skb, void *location)
+static void trace_kfree_skb_hit(void *ignore, struct sk_buff *skb, void *location)
 {
        trace_drop_common(skb, location);
 }
 
-static void trace_napi_poll_hit(struct napi_struct *napi)
+static void trace_napi_poll_hit(void *ignore, struct napi_struct *napi)
 {
        struct dm_hw_stat_delta *new_stat;
 
@@ -225,12 +225,12 @@ static int set_all_monitor_traces(int state)
 
        switch (state) {
        case TRACE_ON:
-               rc |= register_trace_kfree_skb(trace_kfree_skb_hit);
-               rc |= register_trace_napi_poll(trace_napi_poll_hit);
+               rc |= register_trace_kfree_skb(trace_kfree_skb_hit, NULL);
+               rc |= register_trace_napi_poll(trace_napi_poll_hit, NULL);
                break;
        case TRACE_OFF:
-               rc |= unregister_trace_kfree_skb(trace_kfree_skb_hit);
-               rc |= unregister_trace_napi_poll(trace_napi_poll_hit);
+               rc |= unregister_trace_kfree_skb(trace_kfree_skb_hit, NULL);
+               rc |= unregister_trace_napi_poll(trace_napi_poll_hit, NULL);
 
                tracepoint_synchronize_unregister();
 
index bff3790..6ba1c0e 100644 (file)
@@ -934,6 +934,7 @@ int __neigh_event_send(struct neighbour *neigh, struct sk_buff *skb)
                                kfree_skb(buff);
                                NEIGH_CACHE_STAT_INC(neigh->tbl, unres_discards);
                        }
+                       skb_dst_force(skb);
                        __skb_queue_tail(&neigh->arp_queue, skb);
                }
                rc = 1;
index 7ab86f3..1a2af24 100644 (file)
@@ -650,11 +650,12 @@ static inline int rtnl_vfinfo_size(const struct net_device *dev)
        if (dev->dev.parent && dev_is_pci(dev->dev.parent)) {
 
                int num_vfs = dev_num_vf(dev->dev.parent);
-               size_t size = nlmsg_total_size(sizeof(struct nlattr));
-               size += nlmsg_total_size(num_vfs * sizeof(struct nlattr));
-               size += num_vfs * (sizeof(struct ifla_vf_mac) +
-                                 sizeof(struct ifla_vf_vlan) +
-                                 sizeof(struct ifla_vf_tx_rate));
+               size_t size = nla_total_size(sizeof(struct nlattr));
+               size += nla_total_size(num_vfs * sizeof(struct nlattr));
+               size += num_vfs *
+                       (nla_total_size(sizeof(struct ifla_vf_mac)) +
+                        nla_total_size(sizeof(struct ifla_vf_vlan)) +
+                        nla_total_size(sizeof(struct ifla_vf_tx_rate)));
                return size;
        } else
                return 0;
@@ -722,14 +723,13 @@ static int rtnl_vf_ports_fill(struct sk_buff *skb, struct net_device *dev)
 
        for (vf = 0; vf < dev_num_vf(dev->dev.parent); vf++) {
                vf_port = nla_nest_start(skb, IFLA_VF_PORT);
-               if (!vf_port) {
-                       nla_nest_cancel(skb, vf_ports);
-                       return -EMSGSIZE;
-               }
+               if (!vf_port)
+                       goto nla_put_failure;
                NLA_PUT_U32(skb, IFLA_PORT_VF, vf);
                err = dev->netdev_ops->ndo_get_vf_port(dev, vf, skb);
+               if (err == -EMSGSIZE)
+                       goto nla_put_failure;
                if (err) {
-nla_put_failure:
                        nla_nest_cancel(skb, vf_port);
                        continue;
                }
@@ -739,6 +739,10 @@ nla_put_failure:
        nla_nest_end(skb, vf_ports);
 
        return 0;
+
+nla_put_failure:
+       nla_nest_cancel(skb, vf_ports);
+       return -EMSGSIZE;
 }
 
 static int rtnl_port_self_fill(struct sk_buff *skb, struct net_device *dev)
@@ -753,7 +757,7 @@ static int rtnl_port_self_fill(struct sk_buff *skb, struct net_device *dev)
        err = dev->netdev_ops->ndo_get_vf_port(dev, PORT_SELF_VF, skb);
        if (err) {
                nla_nest_cancel(skb, port_self);
-               return err;
+               return (err == -EMSGSIZE) ? err : 0;
        }
 
        nla_nest_end(skb, port_self);
index 37fe9b6..2cf7f9f 100644 (file)
@@ -2007,6 +2007,39 @@ void release_sock(struct sock *sk)
 }
 EXPORT_SYMBOL(release_sock);
 
+/**
+ * lock_sock_fast - fast version of lock_sock
+ * @sk: socket
+ *
+ * This version should be used for very small section, where process wont block
+ * return false if fast path is taken
+ *   sk_lock.slock locked, owned = 0, BH disabled
+ * return true if slow path is taken
+ *   sk_lock.slock unlocked, owned = 1, BH enabled
+ */
+bool lock_sock_fast(struct sock *sk)
+{
+       might_sleep();
+       spin_lock_bh(&sk->sk_lock.slock);
+
+       if (!sk->sk_lock.owned)
+               /*
+                * Note : We must disable BH
+                */
+               return false;
+
+       __lock_sock(sk);
+       sk->sk_lock.owned = 1;
+       spin_unlock(&sk->sk_lock.slock);
+       /*
+        * The sk_lock has mutex_lock() semantics here:
+        */
+       mutex_acquire(&sk->sk_lock.dep_map, 0, 0, _RET_IP_);
+       local_bh_enable();
+       return true;
+}
+EXPORT_SYMBOL(lock_sock_fast);
+
 int sock_get_timestamp(struct sock *sk, struct timeval __user *userstamp)
 {
        struct timeval tv;
index 4588910..856123f 100644 (file)
@@ -1911,7 +1911,7 @@ static int __ipmr_fill_mroute(struct mr_table *mrt, struct sk_buff *skb,
        struct rtattr *mp_head;
 
        /* If cache is unresolved, don't try to parse IIF and OIF */
-       if (c->mfc_parent > MAXVIFS)
+       if (c->mfc_parent >= MAXVIFS)
                return -ENOENT;
 
        if (VIF_EXISTS(mrt, c->mfc_parent))
index baeec29..5858574 100644 (file)
@@ -1063,10 +1063,11 @@ static unsigned int first_packet_length(struct sock *sk)
        spin_unlock_bh(&rcvq->lock);
 
        if (!skb_queue_empty(&list_kill)) {
-               lock_sock_bh(sk);
+               bool slow = lock_sock_fast(sk);
+
                __skb_queue_purge(&list_kill);
                sk_mem_reclaim_partial(sk);
-               unlock_sock_bh(sk);
+               unlock_sock_fast(sk, slow);
        }
        return res;
 }
@@ -1123,6 +1124,7 @@ int udp_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
        int peeked;
        int err;
        int is_udplite = IS_UDPLITE(sk);
+       bool slow;
 
        /*
         *      Check any passed addresses
@@ -1197,10 +1199,10 @@ out:
        return err;
 
 csum_copy_err:
-       lock_sock_bh(sk);
+       slow = lock_sock_fast(sk);
        if (!skb_kill_datagram(sk, skb, flags))
                UDP_INC_STATS_USER(sock_net(sk), UDP_MIB_INERRORS, is_udplite);
-       unlock_sock_bh(sk);
+       unlock_sock_fast(sk, slow);
 
        if (noblock)
                return -EAGAIN;
@@ -1625,9 +1627,9 @@ int udp_rcv(struct sk_buff *skb)
 
 void udp_destroy_sock(struct sock *sk)
 {
-       lock_sock_bh(sk);
+       bool slow = lock_sock_fast(sk);
        udp_flush_pending_frames(sk);
-       unlock_sock_bh(sk);
+       unlock_sock_fast(sk, slow);
 }
 
 /*
index cd963f6..89425af 100644 (file)
@@ -507,7 +507,7 @@ int ip6_forward(struct sk_buff *skb)
        if (mtu < IPV6_MIN_MTU)
                mtu = IPV6_MIN_MTU;
 
-       if (skb->len > mtu) {
+       if (skb->len > mtu && !skb_is_gso(skb)) {
                /* Again, force OUTPUT device used as source address */
                skb->dev = dst->dev;
                icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu);
index bd9e7d3..073071f 100644 (file)
@@ -2017,7 +2017,7 @@ static int __ip6mr_fill_mroute(struct mr6_table *mrt, struct sk_buff *skb,
        struct rtattr *mp_head;
 
        /* If cache is unresolved, don't try to parse IIF and OIF */
-       if (c->mf6c_parent > MAXMIFS)
+       if (c->mf6c_parent >= MAXMIFS)
                return -ENOENT;
 
        if (MIF_EXISTS(mrt, c->mf6c_parent))
index 3d7a2c0..87be586 100644 (file)
@@ -328,6 +328,7 @@ int udpv6_recvmsg(struct kiocb *iocb, struct sock *sk,
        int err;
        int is_udplite = IS_UDPLITE(sk);
        int is_udp4;
+       bool slow;
 
        if (addr_len)
                *addr_len=sizeof(struct sockaddr_in6);
@@ -424,7 +425,7 @@ out:
        return err;
 
 csum_copy_err:
-       lock_sock_bh(sk);
+       slow = lock_sock_fast(sk);
        if (!skb_kill_datagram(sk, skb, flags)) {
                if (is_udp4)
                        UDP_INC_STATS_USER(sock_net(sk),
@@ -433,7 +434,7 @@ csum_copy_err:
                        UDP6_INC_STATS_USER(sock_net(sk),
                                        UDP_MIB_INERRORS, is_udplite);
        }
-       unlock_sock_bh(sk);
+       unlock_sock_fast(sk, slow);
 
        if (flags & MSG_DONTWAIT)
                return -EAGAIN;
index c8b4599..9637e45 100644 (file)
@@ -1619,7 +1619,7 @@ static void iucv_callback_rx(struct iucv_path *path, struct iucv_message *msg)
 save_message:
        save_msg = kzalloc(sizeof(struct sock_msg_q), GFP_ATOMIC | GFP_DMA);
        if (!save_msg)
-               return;
+               goto out_unlock;
        save_msg->path = path;
        save_msg->msg = *msg;
 
index d7920d9..859d9fd 100644 (file)
@@ -76,7 +76,7 @@ tee_tg_route4(struct sk_buff *skb, const struct xt_tee_tginfo *info)
        if (ip_route_output_key(net, &rt, &fl) != 0)
                return false;
 
-       dst_release(skb_dst(skb));
+       skb_dst_drop(skb);
        skb_dst_set(skb, &rt->u.dst);
        skb->dev      = rt->u.dst.dev;
        skb->protocol = htons(ETH_P_IP);
@@ -157,7 +157,7 @@ tee_tg_route6(struct sk_buff *skb, const struct xt_tee_tginfo *info)
        if (dst == NULL)
                return false;
 
-       dst_release(skb_dst(skb));
+       skb_dst_drop(skb);
        skb_dst_set(skb, dst);
        skb->dev      = dst->dev;
        skb->protocol = htons(ETH_P_IPV6);
index dffdc49..4d46be9 100644 (file)
@@ -7,7 +7,5 @@
 DECLARE_TRACE(subsys_event,
        TP_PROTO(struct inode *inode, struct file *file),
        TP_ARGS(inode, file));
-DECLARE_TRACE(subsys_eventb,
-       TP_PROTO(void),
-       TP_ARGS());
+DECLARE_TRACE_NOARGS(subsys_eventb);
 #endif
index 9e60eb6..744c0b9 100644 (file)
@@ -13,7 +13,8 @@
  * Here the caller only guarantees locking for struct file and struct inode.
  * Locking must therefore be done in the probe to use the dentry.
  */
-static void probe_subsys_event(struct inode *inode, struct file *file)
+static void probe_subsys_event(void *ignore,
+                              struct inode *inode, struct file *file)
 {
        path_get(&file->f_path);
        dget(file->f_path.dentry);
@@ -23,7 +24,7 @@ static void probe_subsys_event(struct inode *inode, struct file *file)
        path_put(&file->f_path);
 }
 
-static void probe_subsys_eventb(void)
+static void probe_subsys_eventb(void *ignore)
 {
        printk(KERN_INFO "Event B is encountered\n");
 }
@@ -32,9 +33,9 @@ static int __init tp_sample_trace_init(void)
 {
        int ret;
 
-       ret = register_trace_subsys_event(probe_subsys_event);
+       ret = register_trace_subsys_event(probe_subsys_event, NULL);
        WARN_ON(ret);
-       ret = register_trace_subsys_eventb(probe_subsys_eventb);
+       ret = register_trace_subsys_eventb(probe_subsys_eventb, NULL);
        WARN_ON(ret);
 
        return 0;
@@ -44,8 +45,8 @@ module_init(tp_sample_trace_init);
 
 static void __exit tp_sample_trace_exit(void)
 {
-       unregister_trace_subsys_eventb(probe_subsys_eventb);
-       unregister_trace_subsys_event(probe_subsys_event);
+       unregister_trace_subsys_eventb(probe_subsys_eventb, NULL);
+       unregister_trace_subsys_event(probe_subsys_event, NULL);
        tracepoint_synchronize_unregister();
 }
 
index be2a960..9fcf990 100644 (file)
@@ -12,7 +12,8 @@
  * Here the caller only guarantees locking for struct file and struct inode.
  * Locking must therefore be done in the probe to use the dentry.
  */
-static void probe_subsys_event(struct inode *inode, struct file *file)
+static void probe_subsys_event(void *ignore,
+                              struct inode *inode, struct file *file)
 {
        printk(KERN_INFO "Event is encountered with inode number %lu\n",
                inode->i_ino);
@@ -22,7 +23,7 @@ static int __init tp_sample_trace_init(void)
 {
        int ret;
 
-       ret = register_trace_subsys_event(probe_subsys_event);
+       ret = register_trace_subsys_event(probe_subsys_event, NULL);
        WARN_ON(ret);
 
        return 0;
@@ -32,7 +33,7 @@ module_init(tp_sample_trace_init);
 
 static void __exit tp_sample_trace_exit(void)
 {
-       unregister_trace_subsys_event(probe_subsys_event);
+       unregister_trace_subsys_event(probe_subsys_event, NULL);
        tracepoint_synchronize_unregister();
 }
 
index 3e763d6..446cf97 100644 (file)
@@ -516,6 +516,7 @@ get the interrupt driven case to work efficiently */
                        break;
        if (i == 0x5000) {
                printk(KERN_ERR "au1000 AC97: AC97 command read timeout\n");
+               spin_unlock(&au1000->ac97_lock);
                return 0;
        }
 
index 1f47741..13c2144 100644 (file)
@@ -1277,7 +1277,7 @@ static irqreturn_t AtaInterrupt(int irq, void *dummy)
                 * (almost) like on the TT.
                 */
                write_sq_ignore_int = 0;
-               return IRQ_HANDLED;
+               goto out;
        }
 
        if (!write_sq.active) {
@@ -1285,7 +1285,7 @@ static irqreturn_t AtaInterrupt(int irq, void *dummy)
                 * the sq variables, so better don't do anything here.
                 */
                WAKE_UP(write_sq.sync_queue);
-               return IRQ_HANDLED;
+               goto out;
        }
 
        /* Probably ;) one frame is finished. Well, in fact it may be that a
@@ -1322,6 +1322,7 @@ static irqreturn_t AtaInterrupt(int irq, void *dummy)
        /* We are not playing after AtaPlay(), so there
           is nothing to play any more. Wake up a process
           waiting for audio output to drain. */
+out:
        spin_unlock(&dmasound.lock);
        return IRQ_HANDLED;
 }
index 99400de..0173bbe 100644 (file)
@@ -50,7 +50,7 @@ i.e 3.05.02 is a development version
 #define HPI_VER_RELEASE(v) ((int)(v & 0xFF))
 
 /* Use single digits for versions less that 10 to avoid octal. */
-#define HPI_VER HPI_VERSION_CONSTRUCTOR(4L, 3, 18)
+#define HPI_VER HPI_VERSION_CONSTRUCTOR(4L, 3, 25)
 
 /* Library version as documented in hpi-api-versions.txt */
 #define HPI_LIB_VER  HPI_VERSION_CONSTRUCTOR(9, 0, 0)
@@ -1632,6 +1632,12 @@ u16 hpi_tuner_get_hd_radio_sdk_version(const struct hpi_hsubsys *ph_subsys,
 u16 hpi_tuner_get_hd_radio_signal_quality(const struct hpi_hsubsys *ph_subsys,
        u32 h_control, u32 *pquality);
 
+u16 hpi_tuner_get_hd_radio_signal_blend(const struct hpi_hsubsys *ph_subsys,
+       u32 h_control, u32 *pblend);
+
+u16 hpi_tuner_set_hd_radio_signal_blend(const struct hpi_hsubsys *ph_subsys,
+       u32 h_control, const u32 blend);
+
 /****************************/
 /* PADs control             */
 /****************************/
index 839ecb2..12dab5e 100644 (file)
@@ -691,9 +691,6 @@ static short hpi6000_adapter_boot_load_dsp(struct hpi_adapter_obj *pao,
        case 0x6200:
                boot_load_family = HPI_ADAPTER_FAMILY_ASI(0x6200);
                break;
-       case 0x8800:
-               boot_load_family = HPI_ADAPTER_FAMILY_ASI(0x8800);
-               break;
        default:
                return HPI6000_ERROR_UNHANDLED_SUBSYS_ID;
        }
@@ -1775,7 +1772,6 @@ static void hw_message(struct hpi_adapter_obj *pao, struct hpi_message *phm,
        u16 error = 0;
        u16 dsp_index = 0;
        u16 num_dsp = ((struct hpi_hw_obj *)pao->priv)->num_dsp;
-       hpios_dsplock_lock(pao);
 
        if (num_dsp < 2)
                dsp_index = 0;
@@ -1796,6 +1792,8 @@ static void hw_message(struct hpi_adapter_obj *pao, struct hpi_message *phm,
                        }
                }
        }
+
+       hpios_dsplock_lock(pao);
        error = hpi6000_message_response_sequence(pao, dsp_index, phm, phr);
 
        /* maybe an error response */
index 5e88c1f..e89991e 100644 (file)
@@ -966,23 +966,16 @@ static void outstream_write(struct hpi_adapter_obj *pao,
        status = &interface->outstream_host_buffer_status[phm->obj_index];
 
        if (phw->flag_outstream_just_reset[phm->obj_index]) {
-               /* Format can only change after reset. Must tell DSP. */
-               u16 function = phm->function;
-               phw->flag_outstream_just_reset[phm->obj_index] = 0;
-               phm->function = HPI_OSTREAM_SET_FORMAT;
-               hw_message(pao, phm, phr);      /* send the format to the DSP */
-               phm->function = function;
-               if (phr->error)
-                       return;
-       }
-#if 1
-       if (phw->flag_outstream_just_reset[phm->obj_index]) {
                /* First OutStremWrite() call following reset will write data to the
-                  adapter's buffers, reducing delay before stream can start
+                  adapter's buffers, reducing delay before stream can start. The DSP
+                  takes care of setting the stream data format using format information
+                  embedded in phm.
                 */
                int partial_write = 0;
                unsigned int original_size = 0;
 
+               phw->flag_outstream_just_reset[phm->obj_index] = 0;
+
                /* Send the first buffer to the DSP the old way. */
                /* Limit size of first transfer - */
                /* expect that this will not usually be triggered. */
@@ -1012,7 +1005,6 @@ static void outstream_write(struct hpi_adapter_obj *pao,
                        original_size - HPI6205_SIZEOF_DATA;
                phm->u.d.u.data.pb_data += HPI6205_SIZEOF_DATA;
        }
-#endif
 
        space_available = outstream_get_space_available(status);
        if (space_available < (long)phm->u.d.u.data.data_size) {
@@ -1369,6 +1361,9 @@ static u16 adapter_boot_load_dsp(struct hpi_adapter_obj *pao,
        case HPI_ADAPTER_FAMILY_ASI(0x6500):
                firmware_id = HPI_ADAPTER_FAMILY_ASI(0x6600);
                break;
+       case HPI_ADAPTER_FAMILY_ASI(0x8800):
+               firmware_id = HPI_ADAPTER_FAMILY_ASI(0x8900);
+               break;
        }
        boot_code_id[1] = firmware_id;
 
index f1cd6f1..fdd0ce0 100644 (file)
@@ -232,6 +232,8 @@ enum HPI_BUSES {
 #define HPI_TUNER_HDRADIO_SDK_VERSION   HPI_CTL_ATTR(TUNER, 13)
 /** HD Radio DSP firmware version. */
 #define HPI_TUNER_HDRADIO_DSP_VERSION   HPI_CTL_ATTR(TUNER, 14)
+/** HD Radio signal blend (force analog, or automatic). */
+#define HPI_TUNER_HDRADIO_BLEND         HPI_CTL_ATTR(TUNER, 15)
 
 /** \} */
 
@@ -478,8 +480,10 @@ Threshold is a -ve number in units of dB/100,
 
 /** First 2 hex digits define the adapter family */
 #define HPI_ADAPTER_FAMILY_MASK         0xff00
+#define HPI_MODULE_FAMILY_MASK          0xfff0
 
 #define HPI_ADAPTER_FAMILY_ASI(f)   (f & HPI_ADAPTER_FAMILY_MASK)
+#define HPI_MODULE_FAMILY_ASI(f)   (f & HPI_MODULE_FAMILY_MASK)
 #define HPI_ADAPTER_ASI(f)   (f)
 
 /******************************************* message types */
@@ -970,6 +974,7 @@ struct hpi_control_union_msg {
                                u32 mode;
                                u32 value;
                        } mode;
+                       u32 blend;
                } tuner;
        } u;
 };
index 565102c..fcd6453 100644 (file)
@@ -347,20 +347,15 @@ short hpi_check_control_cache(struct hpi_control_cache *p_cache,
                        found = 0;
                break;
        case HPI_CONTROL_TUNER:
-               {
-                       struct hpi_control_cache_single *pCT =
-                               (struct hpi_control_cache_single *)pI;
-                       if (phm->u.c.attribute == HPI_TUNER_FREQ)
-                               phr->u.c.param1 = pCT->u.t.freq_ink_hz;
-                       else if (phm->u.c.attribute == HPI_TUNER_BAND)
-                               phr->u.c.param1 = pCT->u.t.band;
-                       else if ((phm->u.c.attribute == HPI_TUNER_LEVEL)
-                               && (phm->u.c.param1 ==
-                                       HPI_TUNER_LEVEL_AVERAGE))
-                               phr->u.c.param1 = pCT->u.t.level;
-                       else
-                               found = 0;
-               }
+               if (phm->u.c.attribute == HPI_TUNER_FREQ)
+                       phr->u.c.param1 = pC->u.t.freq_ink_hz;
+               else if (phm->u.c.attribute == HPI_TUNER_BAND)
+                       phr->u.c.param1 = pC->u.t.band;
+               else if ((phm->u.c.attribute == HPI_TUNER_LEVEL)
+                       && (phm->u.c.param1 == HPI_TUNER_LEVEL_AVERAGE))
+                       phr->u.c.param1 = pC->u.t.level;
+               else
+                       found = 0;
                break;
        case HPI_CONTROL_AESEBU_RECEIVER:
                if (phm->u.c.attribute == HPI_AESEBURX_ERRORSTATUS)
@@ -503,6 +498,9 @@ void hpi_sync_control_cache(struct hpi_control_cache *p_cache,
        struct hpi_control_cache_single *pC;
        struct hpi_control_cache_info *pI;
 
+       if (phr->error)
+               return;
+
        if (!find_control(phm, p_cache, &pI, &control_index))
                return;
 
@@ -520,8 +518,6 @@ void hpi_sync_control_cache(struct hpi_control_cache *p_cache,
                break;
        case HPI_CONTROL_MULTIPLEXER:
                /* mux does not return its setting on Set command. */
-               if (phr->error)
-                       return;
                if (phm->u.c.attribute == HPI_MULTIPLEXER_SOURCE) {
                        pC->u.x.source_node_type = (u16)phm->u.c.param1;
                        pC->u.x.source_node_index = (u16)phm->u.c.param2;
@@ -529,8 +525,6 @@ void hpi_sync_control_cache(struct hpi_control_cache *p_cache,
                break;
        case HPI_CONTROL_CHANNEL_MODE:
                /* mode does not return its setting on Set command. */
-               if (phr->error)
-                       return;
                if (phm->u.c.attribute == HPI_CHANNEL_MODE_MODE)
                        pC->u.m.mode = (u16)phm->u.c.param1;
                break;
@@ -545,20 +539,14 @@ void hpi_sync_control_cache(struct hpi_control_cache *p_cache,
                        pC->u.phantom_power.state = (u16)phm->u.c.param1;
                break;
        case HPI_CONTROL_AESEBU_TRANSMITTER:
-               if (phr->error)
-                       return;
                if (phm->u.c.attribute == HPI_AESEBUTX_FORMAT)
                        pC->u.aes3tx.format = phm->u.c.param1;
                break;
        case HPI_CONTROL_AESEBU_RECEIVER:
-               if (phr->error)
-                       return;
                if (phm->u.c.attribute == HPI_AESEBURX_FORMAT)
                        pC->u.aes3rx.source = phm->u.c.param1;
                break;
        case HPI_CONTROL_SAMPLECLOCK:
-               if (phr->error)
-                       return;
                if (phm->u.c.attribute == HPI_SAMPLECLOCK_SOURCE)
                        pC->u.clk.source = (u16)phm->u.c.param1;
                else if (phm->u.c.attribute == HPI_SAMPLECLOCK_SOURCE_INDEX)
@@ -590,7 +578,7 @@ struct hpi_control_cache *hpi_alloc_control_cache(const u32
 
 void hpi_free_control_cache(struct hpi_control_cache *p_cache)
 {
-       if ((p_cache->init) && (p_cache->p_info)) {
+       if (p_cache->init) {
                kfree(p_cache->p_info);
                p_cache->p_info = NULL;
                p_cache->init = 0;
index eda26b3..298eef3 100644 (file)
@@ -2946,6 +2946,20 @@ u16 hpi_tuner_get_hd_radio_signal_quality(const struct hpi_hsubsys *ph_subsys,
                HPI_TUNER_HDRADIO_SIGNAL_QUALITY, 0, 0, pquality, NULL);
 }
 
+u16 hpi_tuner_get_hd_radio_signal_blend(const struct hpi_hsubsys *ph_subsys,
+       u32 h_control, u32 *pblend)
+{
+       return hpi_control_param_get(ph_subsys, h_control,
+               HPI_TUNER_HDRADIO_BLEND, 0, 0, pblend, NULL);
+}
+
+u16 hpi_tuner_set_hd_radio_signal_blend(const struct hpi_hsubsys *ph_subsys,
+       u32 h_control, const u32 blend)
+{
+       return hpi_control_param_set(ph_subsys, h_control,
+               HPI_TUNER_HDRADIO_BLEND, blend, 0);
+}
+
 u16 hpi_tuner_getRDS(const struct hpi_hsubsys *ph_subsys, u32 h_control,
        char *p_data)
 {
@@ -3266,8 +3280,7 @@ u16 hpi_entity_find_next(struct hpi_entity *container_entity,
 
 void hpi_entity_free(struct hpi_entity *entity)
 {
-       if (entity != NULL)
-               kfree(entity);
+       kfree(entity);
 }
 
 static u16 hpi_entity_alloc_and_copy(struct hpi_entity *src,
index de615cf..742ee12 100644 (file)
@@ -89,26 +89,3 @@ u16 hpios_locked_mem_free(struct consistent_dma_area *p_mem_area)
 void hpios_locked_mem_free_all(void)
 {
 }
-
-void __iomem *hpios_map_io(struct pci_dev *pci_dev, int idx,
-       unsigned int length)
-{
-       HPI_DEBUG_LOG(DEBUG, "mapping %d %s %08llx-%08llx %04llx len 0x%x\n",
-               idx, pci_dev->resource[idx].name,
-               (unsigned long long)pci_resource_start(pci_dev, idx),
-               (unsigned long long)pci_resource_end(pci_dev, idx),
-               (unsigned long long)pci_resource_flags(pci_dev, idx), length);
-
-       if (!(pci_resource_flags(pci_dev, idx) & IORESOURCE_MEM)) {
-               HPI_DEBUG_LOG(ERROR, "not an io memory resource\n");
-               return NULL;
-       }
-
-       if (length > pci_resource_len(pci_dev, idx)) {
-               HPI_DEBUG_LOG(ERROR, "resource too small for requested %d \n",
-                       length);
-               return NULL;
-       }
-
-       return ioremap(pci_resource_start(pci_dev, idx), length);
-}
index a62c3f1..370f39b 100644 (file)
@@ -166,13 +166,4 @@ struct hpi_adapter {
        void __iomem *ap_remapped_mem_base[HPI_MAX_ADAPTER_MEM_SPACES];
 };
 
-static inline void hpios_unmap_io(void __iomem *addr,
-       unsigned long size)
-{
-       iounmap(addr);
-}
-
-void __iomem *hpios_map_io(struct pci_dev *pci_dev, int idx,
-       unsigned int length);
-
 #endif
index 77e22c2..dc79564 100644 (file)
@@ -2288,8 +2288,10 @@ static struct snd_pci_quirk position_fix_list[] __devinitdata = {
        SND_PCI_QUIRK(0x1028, 0x01f6, "Dell Latitude 131L", POS_FIX_LPIB),
        SND_PCI_QUIRK(0x103c, 0x306d, "HP dv3", POS_FIX_LPIB),
        SND_PCI_QUIRK(0x1043, 0x813d, "ASUS P5AD2", POS_FIX_LPIB),
+       SND_PCI_QUIRK(0x104d, 0x9069, "Sony VPCS11V9E", POS_FIX_LPIB),
        SND_PCI_QUIRK(0x1106, 0x3288, "ASUS M2V-MX SE", POS_FIX_LPIB),
        SND_PCI_QUIRK(0x1179, 0xff10, "Toshiba A100-259", POS_FIX_LPIB),
+       SND_PCI_QUIRK(0x1297, 0x3166, "Shuttle", POS_FIX_LPIB),
        SND_PCI_QUIRK(0x1458, 0xa022, "ga-ma770-ud3", POS_FIX_LPIB),
        SND_PCI_QUIRK(0x1462, 0x1002, "MSI Wind U115", POS_FIX_LPIB),
        SND_PCI_QUIRK(0x1565, 0x820f, "Biostar Microtech", POS_FIX_LPIB),
index e863649..2bf2cb5 100644 (file)
@@ -2975,6 +2975,8 @@ static struct snd_pci_quirk cxt5066_cfg_tbl[] = {
        SND_PCI_QUIRK(0x1179, 0xff50, "Toshiba Satellite P500-PSPGSC-01800T", CXT5066_OLPC_XO_1_5),
        SND_PCI_QUIRK(0x1179, 0xffe0, "Toshiba Satellite Pro T130-15F", CXT5066_OLPC_XO_1_5),
        SND_PCI_QUIRK(0x17aa, 0x21b2, "Thinkpad X100e", CXT5066_IDEAPAD),
+       SND_PCI_QUIRK(0x17aa, 0x21b3, "Thinkpad Edge 13 (197)", CXT5066_IDEAPAD),
+       SND_PCI_QUIRK(0x17aa, 0x21b4, "Thinkpad Edge", CXT5066_IDEAPAD),
        SND_PCI_QUIRK(0x17aa, 0x3a0d, "ideapad", CXT5066_IDEAPAD),
        SND_PCI_QUIRK(0x17aa, 0x215e, "Lenovo Thinkpad", CXT5066_THINKPAD),
        {}
index 8ae2020..0221ca7 100644 (file)
@@ -426,8 +426,8 @@ static const struct soc_enum wm8350_enum[] = {
        SOC_ENUM_SINGLE(WM8350_INPUT_MIXER_VOLUME, 15, 2, wm8350_lr),
 };
 
-static DECLARE_TLV_DB_LINEAR(pre_amp_tlv, -1200, 3525);
-static DECLARE_TLV_DB_LINEAR(out_pga_tlv, -5700, 600);
+static DECLARE_TLV_DB_SCALE(pre_amp_tlv, -1200, 3525, 0);
+static DECLARE_TLV_DB_SCALE(out_pga_tlv, -5700, 600, 0);
 static DECLARE_TLV_DB_SCALE(dac_pcm_tlv, -7163, 36, 1);
 static DECLARE_TLV_DB_SCALE(adc_pcm_tlv, -12700, 50, 1);
 static DECLARE_TLV_DB_SCALE(out_mix_tlv, -1500, 300, 1);
index 7f5d080..8f29406 100644 (file)
@@ -107,21 +107,21 @@ static void wm8400_codec_reset(struct snd_soc_codec *codec)
        wm8400_reset_codec_reg_cache(wm8400->wm8400);
 }
 
-static const DECLARE_TLV_DB_LINEAR(rec_mix_tlv, -1500, 600);
+static const DECLARE_TLV_DB_SCALE(rec_mix_tlv, -1500, 600, 0);
 
-static const DECLARE_TLV_DB_LINEAR(in_pga_tlv, -1650, 3000);
+static const DECLARE_TLV_DB_SCALE(in_pga_tlv, -1650, 3000, 0);
 
-static const DECLARE_TLV_DB_LINEAR(out_mix_tlv, -2100, 0);
+static const DECLARE_TLV_DB_SCALE(out_mix_tlv, -2100, 0, 0);
 
-static const DECLARE_TLV_DB_LINEAR(out_pga_tlv, -7300, 600);
+static const DECLARE_TLV_DB_SCALE(out_pga_tlv, -7300, 600, 0);
 
-static const DECLARE_TLV_DB_LINEAR(out_omix_tlv, -600, 0);
+static const DECLARE_TLV_DB_SCALE(out_omix_tlv, -600, 0, 0);
 
-static const DECLARE_TLV_DB_LINEAR(out_dac_tlv, -7163, 0);
+static const DECLARE_TLV_DB_SCALE(out_dac_tlv, -7163, 0, 0);
 
-static const DECLARE_TLV_DB_LINEAR(in_adc_tlv, -7163, 1763);
+static const DECLARE_TLV_DB_SCALE(in_adc_tlv, -7163, 1763, 0);
 
-static const DECLARE_TLV_DB_LINEAR(out_sidetone_tlv, -3600, 0);
+static const DECLARE_TLV_DB_SCALE(out_sidetone_tlv, -3600, 0, 0);
 
 static int wm8400_outpga_put_volsw_vu(struct snd_kcontrol *kcontrol,
         struct snd_ctl_elem_value *ucontrol)
@@ -440,7 +440,7 @@ static int outmixer_event (struct snd_soc_dapm_widget *w,
 /* INMIX dB values */
 static const unsigned int in_mix_tlv[] = {
        TLV_DB_RANGE_HEAD(1),
-       0,7, TLV_DB_LINEAR_ITEM(-1200, 600),
+       0,7, TLV_DB_SCALE_ITEM(-1200, 600, 0),
 };
 
 /* Left In PGA Connections */
index 7b536d9..c018772 100644 (file)
@@ -111,21 +111,21 @@ static const u16 wm8990_reg[] = {
 
 #define wm8990_reset(c) snd_soc_write(c, WM8990_RESET, 0)
 
-static const DECLARE_TLV_DB_LINEAR(rec_mix_tlv, -1500, 600);
+static const DECLARE_TLV_DB_SCALE(rec_mix_tlv, -1500, 600, 0);
 
-static const DECLARE_TLV_DB_LINEAR(in_pga_tlv, -1650, 3000);
+static const DECLARE_TLV_DB_SCALE(in_pga_tlv, -1650, 3000, 0);
 
-static const DECLARE_TLV_DB_LINEAR(out_mix_tlv, 0, -2100);
+static const DECLARE_TLV_DB_SCALE(out_mix_tlv, 0, -2100, 0);
 
-static const DECLARE_TLV_DB_LINEAR(out_pga_tlv, -7300, 600);
+static const DECLARE_TLV_DB_SCALE(out_pga_tlv, -7300, 600, 0);
 
-static const DECLARE_TLV_DB_LINEAR(out_omix_tlv, -600, 0);
+static const DECLARE_TLV_DB_SCALE(out_omix_tlv, -600, 0, 0);
 
-static const DECLARE_TLV_DB_LINEAR(out_dac_tlv, -7163, 0);
+static const DECLARE_TLV_DB_SCALE(out_dac_tlv, -7163, 0, 0);
 
-static const DECLARE_TLV_DB_LINEAR(in_adc_tlv, -7163, 1763);
+static const DECLARE_TLV_DB_SCALE(in_adc_tlv, -7163, 1763, 0);
 
-static const DECLARE_TLV_DB_LINEAR(out_sidetone_tlv, -3600, 0);
+static const DECLARE_TLV_DB_SCALE(out_sidetone_tlv, -3600, 0, 0);
 
 static int wm899x_outpga_put_volsw_vu(struct snd_kcontrol *kcontrol,
        struct snd_ctl_elem_value *ucontrol)
@@ -451,7 +451,7 @@ static int outmixer_event(struct snd_soc_dapm_widget *w,
 /* INMIX dB values */
 static const unsigned int in_mix_tlv[] = {
        TLV_DB_RANGE_HEAD(1),
-       0, 7, TLV_DB_LINEAR_ITEM(-1200, 600),
+       0, 7, TLV_DB_SCALE_ITEM(-1200, 600, 0),
 };
 
 /* Left In PGA Connections */
index 2b31ac6..05f19c9 100644 (file)
@@ -73,7 +73,8 @@ static void snd_imx_dma_err_callback(int channel, void *data, int err)
 {
        struct snd_pcm_substream *substream = data;
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
-       struct imx_pcm_dma_params *dma_params = rtd->dai->cpu_dai->dma_data;
+       struct imx_pcm_dma_params *dma_params = 
+               snd_soc_dai_get_dma_data(rtd->dai->cpu_dai, substream);
        struct snd_pcm_runtime *runtime = substream->runtime;
        struct imx_pcm_runtime_data *iprtd = runtime->private_data;
        int ret;
@@ -102,7 +103,7 @@ static int imx_ssi_dma_alloc(struct snd_pcm_substream *substream)
        struct imx_pcm_runtime_data *iprtd = runtime->private_data;
        int ret;
 
-       dma_params = snd_soc_get_dma_data(rtd->dai->cpu_dai, substream);
+       dma_params = snd_soc_dai_get_dma_data(rtd->dai->cpu_dai, substream);
 
        iprtd->dma = imx_dma_request_by_prio(DRV_NAME, DMA_PRIO_HIGH);
        if (iprtd->dma < 0) {
@@ -212,7 +213,7 @@ static int snd_imx_pcm_prepare(struct snd_pcm_substream *substream)
        struct imx_pcm_runtime_data *iprtd = runtime->private_data;
        int err;
 
-       dma_params = snd_soc_get_dma_data(rtd->dai->cpu_dai, substream);
+       dma_params = snd_soc_dai_get_dma_data(rtd->dai->cpu_dai, substream);
 
        iprtd->substream = substream;
        iprtd->buf = (unsigned int *)substream->dma_buffer.area;
index d86ee1b..eeed5ed 100644 (file)
@@ -588,6 +588,8 @@ static int siu_dai_prepare(struct snd_pcm_substream *substream,
                ret = siu_dai_spbstart(port_info);
                if (ret < 0)
                        goto fail;
+       } else {
+               ret = 0;
        }
 
        port_info->play_cap |= self;
index 36ed703..91c804c 100644 (file)
@@ -42,21 +42,12 @@ static int control_info(struct snd_kcontrol *kcontrol,
 
        switch (dev->chip.usb_id) {
        case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_AUDIO8DJ):
-               if (pos == 0) {
-                       /* current input mode of A8DJ */
-                       uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
-                       uinfo->value.integer.min = 0;
-                       uinfo->value.integer.max = 2;
-                       return 0;
-               }
-               break;
-
        case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_AUDIO4DJ):
                if (pos == 0) {
-                       /* current input mode of A4DJ */
+                       /* current input mode of A8DJ and A4DJ */
                        uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
                        uinfo->value.integer.min = 0;
-                       uinfo->value.integer.max = 1;
+                       uinfo->value.integer.max = 2;
                        return 0;
                }
                break;
@@ -86,14 +77,6 @@ static int control_get(struct snd_kcontrol *kcontrol,
        struct snd_usb_caiaqdev *dev = caiaqdev(chip->card);
        int pos = kcontrol->private_value;
 
-       if (dev->chip.usb_id ==
-               USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_AUDIO4DJ)) {
-               /* A4DJ has only one control */
-               /* do not expose hardware input mode 0 */
-               ucontrol->value.integer.value[0] = dev->control_state[0] - 1;
-               return 0;
-       }
-
        if (pos & CNT_INTVAL)
                ucontrol->value.integer.value[0]
                        = dev->control_state[pos & ~CNT_INTVAL];
@@ -112,20 +95,9 @@ static int control_put(struct snd_kcontrol *kcontrol,
        int pos = kcontrol->private_value;
        unsigned char cmd = EP1_CMD_WRITE_IO;
 
-       switch (dev->chip.usb_id) {
-       case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_AUDIO4DJ): {
-               /* A4DJ has only one control */
-               /* do not expose hardware input mode 0 */
-               dev->control_state[0] = ucontrol->value.integer.value[0] + 1;
-               snd_usb_caiaq_send_command(dev, EP1_CMD_WRITE_IO,
-                               dev->control_state, sizeof(dev->control_state));
-               return 1;
-       }
-
-       case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_TRAKTORKONTROLX1):
+       if (dev->chip.usb_id ==
+               USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_TRAKTORKONTROLX1))
                cmd = EP1_CMD_DIMM_LEDS;
-               break;
-       }
 
        if (pos & CNT_INTVAL) {
                dev->control_state[pos & ~CNT_INTVAL]
index 8052718..cdfb856 100644 (file)
@@ -36,7 +36,7 @@
 #include "input.h"
 
 MODULE_AUTHOR("Daniel Mack <daniel@caiaq.de>");
-MODULE_DESCRIPTION("caiaq USB audio, version 1.3.20");
+MODULE_DESCRIPTION("caiaq USB audio, version 1.3.21");
 MODULE_LICENSE("GPL");
 MODULE_SUPPORTED_DEVICE("{{Native Instruments, RigKontrol2},"
                         "{Native Instruments, RigKontrol3},"
@@ -320,12 +320,6 @@ static void __devinit setup_card(struct snd_usb_caiaqdev *dev)
                }
 
                break;
-       case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_AUDIO4DJ):
-               /* Audio 4 DJ - default input mode to phono */
-               dev->control_state[0] = 2;
-               snd_usb_caiaq_send_command(dev, EP1_CMD_WRITE_IO,
-                       dev->control_state, 1);
-               break;
        }
 
        if (dev->spec.num_analog_audio_out +
index ef07a6d..28ee1ce 100644 (file)
@@ -149,6 +149,47 @@ int snd_usb_add_audio_endpoint(struct snd_usb_audio *chip, int stream, struct au
        return 0;
 }
 
+static int parse_uac_endpoint_attributes(struct snd_usb_audio *chip,
+                                        struct usb_host_interface *alts,
+                                        int protocol, int iface_no)
+{
+       /* parsed with a v1 header here. that's ok as we only look at the
+        * header first which is the same for both versions */
+       struct uac_iso_endpoint_descriptor *csep;
+       struct usb_interface_descriptor *altsd = get_iface_desc(alts);
+       int attributes = 0;
+
+       csep = snd_usb_find_desc(alts->endpoint[0].extra, alts->endpoint[0].extralen, NULL, USB_DT_CS_ENDPOINT);
+
+       /* Creamware Noah has this descriptor after the 2nd endpoint */
+       if (!csep && altsd->bNumEndpoints >= 2)
+               csep = snd_usb_find_desc(alts->endpoint[1].extra, alts->endpoint[1].extralen, NULL, USB_DT_CS_ENDPOINT);
+
+       if (!csep || csep->bLength < 7 ||
+           csep->bDescriptorSubtype != UAC_EP_GENERAL) {
+               snd_printk(KERN_WARNING "%d:%u:%d : no or invalid"
+                          " class specific endpoint descriptor\n",
+                          chip->dev->devnum, iface_no,
+                          altsd->bAlternateSetting);
+               return 0;
+       }
+
+       if (protocol == UAC_VERSION_1) {
+               attributes = csep->bmAttributes;
+       } else {
+               struct uac2_iso_endpoint_descriptor *csep2 =
+                       (struct uac2_iso_endpoint_descriptor *) csep;
+
+               attributes = csep->bmAttributes & UAC_EP_CS_ATTR_FILL_MAX;
+
+               /* emulate the endpoint attributes of a v1 device */
+               if (csep2->bmControls & UAC2_CONTROL_PITCH)
+                       attributes |= UAC_EP_CS_ATTR_PITCH_CONTROL;
+       }
+
+       return attributes;
+}
+
 int snd_usb_parse_audio_endpoints(struct snd_usb_audio *chip, int iface_no)
 {
        struct usb_device *dev;
@@ -158,8 +199,8 @@ int snd_usb_parse_audio_endpoints(struct snd_usb_audio *chip, int iface_no)
        int i, altno, err, stream;
        int format = 0, num_channels = 0;
        struct audioformat *fp = NULL;
-       unsigned char *fmt, *csep;
        int num, protocol;
+       struct uac_format_type_i_continuous_descriptor *fmt;
 
        dev = chip->dev;
 
@@ -256,8 +297,8 @@ int snd_usb_parse_audio_endpoints(struct snd_usb_audio *chip, int iface_no)
                                   dev->devnum, iface_no, altno);
                        continue;
                }
-               if (((protocol == UAC_VERSION_1) && (fmt[0] < 8)) ||
-                   ((protocol == UAC_VERSION_2) && (fmt[0] != 6))) {
+               if (((protocol == UAC_VERSION_1) && (fmt->bLength < 8)) ||
+                   ((protocol == UAC_VERSION_2) && (fmt->bLength != 6))) {
                        snd_printk(KERN_ERR "%d:%u:%d : invalid UAC_FORMAT_TYPE desc\n",
                                   dev->devnum, iface_no, altno);
                        continue;
@@ -268,7 +309,9 @@ int snd_usb_parse_audio_endpoints(struct snd_usb_audio *chip, int iface_no)
                 * with the previous one, except for a larger packet size, but
                 * is actually a mislabeled two-channel setting; ignore it.
                 */
-               if (fmt[4] == 1 && fmt[5] == 2 && altno == 2 && num == 3 &&
+               if (fmt->bNrChannels == 1 &&
+                   fmt->bSubframeSize == 2 &&
+                   altno == 2 && num == 3 &&
                    fp && fp->altsetting == 1 && fp->channels == 1 &&
                    fp->formats == SNDRV_PCM_FMTBIT_S16_LE &&
                    protocol == UAC_VERSION_1 &&
@@ -276,17 +319,6 @@ int snd_usb_parse_audio_endpoints(struct snd_usb_audio *chip, int iface_no)
                                                        fp->maxpacksize * 2)
                        continue;
 
-               csep = snd_usb_find_desc(alts->endpoint[0].extra, alts->endpoint[0].extralen, NULL, USB_DT_CS_ENDPOINT);
-               /* Creamware Noah has this descriptor after the 2nd endpoint */
-               if (!csep && altsd->bNumEndpoints >= 2)
-                       csep = snd_usb_find_desc(alts->endpoint[1].extra, alts->endpoint[1].extralen, NULL, USB_DT_CS_ENDPOINT);
-               if (!csep || csep[0] < 7 || csep[2] != UAC_EP_GENERAL) {
-                       snd_printk(KERN_WARNING "%d:%u:%d : no or invalid"
-                                  " class specific endpoint descriptor\n",
-                                  dev->devnum, iface_no, altno);
-                       csep = NULL;
-               }
-
                fp = kzalloc(sizeof(*fp), GFP_KERNEL);
                if (! fp) {
                        snd_printk(KERN_ERR "cannot malloc\n");
@@ -305,7 +337,7 @@ int snd_usb_parse_audio_endpoints(struct snd_usb_audio *chip, int iface_no)
                if (snd_usb_get_speed(dev) == USB_SPEED_HIGH)
                        fp->maxpacksize = (((fp->maxpacksize >> 11) & 3) + 1)
                                        * (fp->maxpacksize & 0x7ff);
-               fp->attributes = csep ? csep[3] : 0;
+               fp->attributes = parse_uac_endpoint_attributes(chip, alts, protocol, iface_no);
 
                /* some quirks for attributes here */
 
index b87cf87..fe29d61 100644 (file)
@@ -278,12 +278,11 @@ err:
  * parse the format type I and III descriptors
  */
 static int parse_audio_format_i(struct snd_usb_audio *chip,
-                               struct audioformat *fp,
-                               int format, void *_fmt,
+                               struct audioformat *fp, int format,
+                               struct uac_format_type_i_continuous_descriptor *fmt,
                                struct usb_host_interface *iface)
 {
        struct usb_interface_descriptor *altsd = get_iface_desc(iface);
-       struct uac_format_type_i_discrete_descriptor *fmt = _fmt;
        int protocol = altsd->bInterfaceProtocol;
        int pcm_format, ret;
 
@@ -320,7 +319,7 @@ static int parse_audio_format_i(struct snd_usb_audio *chip,
        switch (protocol) {
        case UAC_VERSION_1:
                fp->channels = fmt->bNrChannels;
-               ret = parse_audio_format_rates_v1(chip, fp, _fmt, 7);
+               ret = parse_audio_format_rates_v1(chip, fp, (unsigned char *) fmt, 7);
                break;
        case UAC_VERSION_2:
                /* fp->channels is already set in this case */
@@ -392,12 +391,12 @@ static int parse_audio_format_ii(struct snd_usb_audio *chip,
 }
 
 int snd_usb_parse_audio_format(struct snd_usb_audio *chip, struct audioformat *fp,
-                      int format, unsigned char *fmt, int stream,
-                      struct usb_host_interface *iface)
+                              int format, struct uac_format_type_i_continuous_descriptor *fmt,
+                              int stream, struct usb_host_interface *iface)
 {
        int err;
 
-       switch (fmt[3]) {
+       switch (fmt->bFormatType) {
        case UAC_FORMAT_TYPE_I:
        case UAC_FORMAT_TYPE_III:
                err = parse_audio_format_i(chip, fp, format, fmt, iface);
@@ -407,10 +406,11 @@ int snd_usb_parse_audio_format(struct snd_usb_audio *chip, struct audioformat *f
                break;
        default:
                snd_printd(KERN_INFO "%d:%u:%d : format type %d is not supported yet\n",
-                          chip->dev->devnum, fp->iface, fp->altsetting, fmt[3]);
-               return -1;
+                          chip->dev->devnum, fp->iface, fp->altsetting,
+                          fmt->bFormatType);
+               return -ENOTSUPP;
        }
-       fp->fmt_type = fmt[3];
+       fp->fmt_type = fmt->bFormatType;
        if (err < 0)
                return err;
 #if 1
@@ -421,10 +421,10 @@ int snd_usb_parse_audio_format(struct snd_usb_audio *chip, struct audioformat *f
        if (chip->usb_id == USB_ID(0x041e, 0x3000) ||
            chip->usb_id == USB_ID(0x041e, 0x3020) ||
            chip->usb_id == USB_ID(0x041e, 0x3061)) {
-               if (fmt[3] == UAC_FORMAT_TYPE_I &&
+               if (fmt->bFormatType == UAC_FORMAT_TYPE_I &&
                    fp->rates != SNDRV_PCM_RATE_48000 &&
                    fp->rates != SNDRV_PCM_RATE_96000)
-                       return -1;
+                       return -ENOTSUPP;
        }
 #endif
        return 0;
index 8298c4e..387924f 100644 (file)
@@ -1,8 +1,9 @@
 #ifndef __USBAUDIO_FORMAT_H
 #define __USBAUDIO_FORMAT_H
 
-int snd_usb_parse_audio_format(struct snd_usb_audio *chip, struct audioformat *fp,
-                              int format, unsigned char *fmt, int stream,
-                              struct usb_host_interface *iface);
+int snd_usb_parse_audio_format(struct snd_usb_audio *chip,
+                              struct audioformat *fp, int format,
+                              struct uac_format_type_i_continuous_descriptor *fmt,
+                              int stream, struct usb_host_interface *iface);
 
 #endif /*  __USBAUDIO_FORMAT_H */
index 97dd176..03ce971 100644 (file)
@@ -1126,7 +1126,7 @@ static int parse_audio_feature_unit(struct mixer_build *state, int unitid, void
        } else {
                struct uac2_feature_unit_descriptor *ftr = _ftr;
                csize = 4;
-               channels = (hdr->bLength - 6) / 4;
+               channels = (hdr->bLength - 6) / 4 - 1;
                bmaControls = ftr->bmaControls;
        }
 
index 2bf0d77..056587d 100644 (file)
@@ -120,10 +120,6 @@ static int init_pitch_v1(struct snd_usb_audio *chip, int iface,
 
        ep = get_endpoint(alts, 0)->bEndpointAddress;
 
-       /* if endpoint doesn't have pitch control, bail out */
-       if (!(fmt->attributes & UAC_EP_CS_ATTR_PITCH_CONTROL))
-               return 0;
-
        data[0] = 1;
        if ((err = snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0), UAC_SET_CUR,
                                   USB_TYPE_CLASS|USB_RECIP_ENDPOINT|USB_DIR_OUT,
@@ -137,8 +133,32 @@ static int init_pitch_v1(struct snd_usb_audio *chip, int iface,
        return 0;
 }
 
+static int init_pitch_v2(struct snd_usb_audio *chip, int iface,
+                        struct usb_host_interface *alts,
+                        struct audioformat *fmt)
+{
+       struct usb_device *dev = chip->dev;
+       unsigned char data[1];
+       unsigned int ep;
+       int err;
+
+       ep = get_endpoint(alts, 0)->bEndpointAddress;
+
+       data[0] = 1;
+       if ((err = snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0), UAC2_CS_CUR,
+                                  USB_TYPE_CLASS | USB_RECIP_ENDPOINT | USB_DIR_OUT,
+                                  UAC2_EP_CS_PITCH << 8, 0,
+                                  data, sizeof(data), 1000)) < 0) {
+               snd_printk(KERN_ERR "%d:%d:%d: cannot set enable PITCH (v2)\n",
+                          dev->devnum, iface, fmt->altsetting);
+               return err;
+       }
+
+       return 0;
+}
+
 /*
- * initialize the picth control and sample rate
+ * initialize the pitch control and sample rate
  */
 int snd_usb_init_pitch(struct snd_usb_audio *chip, int iface,
                       struct usb_host_interface *alts,
@@ -146,13 +166,16 @@ int snd_usb_init_pitch(struct snd_usb_audio *chip, int iface,
 {
        struct usb_interface_descriptor *altsd = get_iface_desc(alts);
 
+       /* if endpoint doesn't have pitch control, bail out */
+       if (!(fmt->attributes & UAC_EP_CS_ATTR_PITCH_CONTROL))
+               return 0;
+
        switch (altsd->bInterfaceProtocol) {
        case UAC_VERSION_1:
                return init_pitch_v1(chip, iface, alts, fmt);
 
        case UAC_VERSION_2:
-               /* not implemented yet */
-               return 0;
+               return init_pitch_v2(chip, iface, alts, fmt);
        }
 
        return -EINVAL;
index 2cab8e8..909fa76 100644 (file)
@@ -43,6 +43,9 @@ OPTIONS
 -c::
         scale counter values
 
+-B::
+        print large numbers with thousands' separators according to locale
+
 EXAMPLES
 --------
 
index 77bcc9b..08278ed 100644 (file)
@@ -277,7 +277,7 @@ static void hist_entry__print_hits(struct hist_entry *self)
        printf("%*s: %Lu\n", BITS_PER_LONG / 2, "h->sum", h->sum);
 }
 
-static void annotate_sym(struct hist_entry *he)
+static int hist_entry__tty_annotate(struct hist_entry *he)
 {
        struct map *map = he->ms.map;
        struct dso *dso = map->dso;
@@ -288,7 +288,7 @@ static void annotate_sym(struct hist_entry *he)
        struct objdump_line *pos, *n;
 
        if (hist_entry__annotate(he, &head) < 0)
-               return;
+               return -1;
 
        if (full_paths)
                d_filename = filename;
@@ -317,30 +317,59 @@ static void annotate_sym(struct hist_entry *he)
 
        if (print_line)
                free_source_line(he, len);
+
+       return 0;
 }
 
 static void hists__find_annotations(struct hists *self)
 {
-       struct rb_node *nd;
+       struct rb_node *first = rb_first(&self->entries), *nd = first;
+       int key = KEY_RIGHT;
 
-       for (nd = rb_first(&self->entries); nd; nd = rb_next(nd)) {
+       while (nd) {
                struct hist_entry *he = rb_entry(nd, struct hist_entry, rb_node);
                struct sym_priv *priv;
 
-               if (he->ms.sym == NULL)
-                       continue;
+               if (he->ms.sym == NULL || he->ms.map->dso->annotate_warned)
+                       goto find_next;
 
                priv = symbol__priv(he->ms.sym);
-               if (priv->hist == NULL)
+               if (priv->hist == NULL) {
+find_next:
+                       if (key == KEY_LEFT)
+                               nd = rb_prev(nd);
+                       else
+                               nd = rb_next(nd);
                        continue;
+               }
 
-               annotate_sym(he);
-               /*
-                * Since we have a hist_entry per IP for the same symbol, free
-                * he->ms.sym->hist to signal we already processed this symbol.
-                */
-               free(priv->hist);
-               priv->hist = NULL;
+               if (use_browser) {
+                       key = hist_entry__tui_annotate(he);
+                       if (is_exit_key(key))
+                               break;
+                       switch (key) {
+                       case KEY_RIGHT:
+                       case '\t':
+                               nd = rb_next(nd);
+                               break;
+                       case KEY_LEFT:
+                               if (nd == first)
+                                       continue;
+                               nd = rb_prev(nd);
+                       default:
+                               break;
+                       }
+               } else {
+                       hist_entry__tty_annotate(he);
+                       nd = rb_next(nd);
+                       /*
+                        * Since we have a hist_entry per IP for the same
+                        * symbol, free he->ms.sym->hist to signal we already
+                        * processed this symbol.
+                        */
+                       free(priv->hist);
+                       priv->hist = NULL;
+               }
        }
 }
 
@@ -416,6 +445,8 @@ int cmd_annotate(int argc, const char **argv, const char *prefix __used)
 {
        argc = parse_options(argc, argv, options, annotate_usage, 0);
 
+       setup_browser();
+
        symbol_conf.priv_size = sizeof(struct sym_priv);
        symbol_conf.try_vmlinux_path = true;
 
@@ -435,8 +466,6 @@ int cmd_annotate(int argc, const char **argv, const char *prefix __used)
                sym_hist_filter = argv[0];
        }
 
-       setup_pager();
-
        if (field_sep && *field_sep == '.') {
                pr_err("'.' is the only non valid --field-separator argument\n");
                return -1;
index 61c6d70..e4a4da3 100644 (file)
@@ -65,8 +65,10 @@ static int parse_probe_event(const char *str)
        int ret;
 
        pr_debug("probe-definition(%d): %s\n", params.nevents, str);
-       if (++params.nevents == MAX_PROBES)
-               die("Too many probes (> %d) are specified.", MAX_PROBES);
+       if (++params.nevents == MAX_PROBES) {
+               pr_err("Too many probes (> %d) were specified.", MAX_PROBES);
+               return -1;
+       }
 
        /* Parse a perf-probe command into event */
        ret = parse_perf_probe_command(str, pev);
@@ -84,7 +86,9 @@ static int parse_probe_event_argv(int argc, const char **argv)
        len = 0;
        for (i = 0; i < argc; i++)
                len += strlen(argv[i]) + 1;
-       buf = xzalloc(len + 1);
+       buf = zalloc(len + 1);
+       if (buf == NULL)
+               return -ENOMEM;
        len = 0;
        for (i = 0; i < argc; i++)
                len += sprintf(&buf[len], "%s ", argv[i]);
index cb46c7d..9bc8905 100644 (file)
@@ -25,6 +25,7 @@
 
 #include <unistd.h>
 #include <sched.h>
+#include <sys/mman.h>
 
 enum write_mode_t {
        WRITE_FORCE,
@@ -60,13 +61,8 @@ static bool                  call_graph                      =  false;
 static bool                    inherit_stat                    =  false;
 static bool                    no_samples                      =  false;
 static bool                    sample_address                  =  false;
-static bool                    multiplex                       =  false;
-static int                     multiplex_fd                    =     -1;
 
 static long                    samples                         =      0;
-static struct timeval          last_read;
-static struct timeval          this_read;
-
 static u64                     bytes_written                   =      0;
 
 static struct pollfd           *event_array;
@@ -86,7 +82,7 @@ struct mmap_data {
        unsigned int            prev;
 };
 
-static struct mmap_data                *mmap_array[MAX_NR_CPUS][MAX_COUNTERS];
+static struct mmap_data                mmap_array[MAX_NR_CPUS];
 
 static unsigned long mmap_read_head(struct mmap_data *md)
 {
@@ -146,8 +142,6 @@ static void mmap_read(struct mmap_data *md)
        void *buf;
        int diff;
 
-       gettimeofday(&this_read, NULL);
-
        /*
         * If we're further behind than half the buffer, there's a chance
         * the writer will bite our tail and mess up the samples under us.
@@ -158,23 +152,13 @@ static void mmap_read(struct mmap_data *md)
         */
        diff = head - old;
        if (diff < 0) {
-               struct timeval iv;
-               unsigned long msecs;
-
-               timersub(&this_read, &last_read, &iv);
-               msecs = iv.tv_sec*1000 + iv.tv_usec/1000;
-
-               fprintf(stderr, "WARNING: failed to keep up with mmap data."
-                               "  Last read %lu msecs ago.\n", msecs);
-
+               fprintf(stderr, "WARNING: failed to keep up with mmap data\n");
                /*
                 * head points to a known good entry, start there.
                 */
                old = head;
        }
 
-       last_read = this_read;
-
        if (old != head)
                samples++;
 
@@ -380,27 +364,30 @@ try_again:
                 */
                if (group && group_fd == -1)
                        group_fd = fd[nr_cpu][counter][thread_index];
-               if (multiplex && multiplex_fd == -1)
-                       multiplex_fd = fd[nr_cpu][counter][thread_index];
 
-               if (multiplex && fd[nr_cpu][counter][thread_index] != multiplex_fd) {
-
-                       ret = ioctl(fd[nr_cpu][counter][thread_index], PERF_EVENT_IOC_SET_OUTPUT, multiplex_fd);
-                       assert(ret != -1);
+               if (counter || thread_index) {
+                       ret = ioctl(fd[nr_cpu][counter][thread_index],
+                                       PERF_EVENT_IOC_SET_OUTPUT,
+                                       fd[nr_cpu][0][0]);
+                       if (ret) {
+                               error("failed to set output: %d (%s)\n", errno,
+                                               strerror(errno));
+                               exit(-1);
+                       }
                } else {
-                       event_array[nr_poll].fd = fd[nr_cpu][counter][thread_index];
-                       event_array[nr_poll].events = POLLIN;
-                       nr_poll++;
-
-                       mmap_array[nr_cpu][counter][thread_index].counter = counter;
-                       mmap_array[nr_cpu][counter][thread_index].prev = 0;
-                       mmap_array[nr_cpu][counter][thread_index].mask = mmap_pages*page_size - 1;
-                       mmap_array[nr_cpu][counter][thread_index].base = mmap(NULL, (mmap_pages+1)*page_size,
+                       mmap_array[nr_cpu].counter = counter;
+                       mmap_array[nr_cpu].prev = 0;
+                       mmap_array[nr_cpu].mask = mmap_pages*page_size - 1;
+                       mmap_array[nr_cpu].base = mmap(NULL, (mmap_pages+1)*page_size,
                                PROT_READ|PROT_WRITE, MAP_SHARED, fd[nr_cpu][counter][thread_index], 0);
-                       if (mmap_array[nr_cpu][counter][thread_index].base == MAP_FAILED) {
+                       if (mmap_array[nr_cpu].base == MAP_FAILED) {
                                error("failed to mmap with %d (%s)\n", errno, strerror(errno));
                                exit(-1);
                        }
+
+                       event_array[nr_poll].fd = fd[nr_cpu][counter][thread_index];
+                       event_array[nr_poll].events = POLLIN;
+                       nr_poll++;
                }
 
                if (filter != NULL) {
@@ -501,16 +488,11 @@ static struct perf_event_header finished_round_event = {
 
 static void mmap_read_all(void)
 {
-       int i, counter, thread;
+       int i;
 
        for (i = 0; i < nr_cpu; i++) {
-               for (counter = 0; counter < nr_counters; counter++) {
-                       for (thread = 0; thread < thread_num; thread++) {
-                               if (mmap_array[i][counter][thread].base)
-                                       mmap_read(&mmap_array[i][counter][thread]);
-                       }
-
-               }
+               if (mmap_array[i].base)
+                       mmap_read(&mmap_array[i]);
        }
 
        if (perf_header__has_feat(&session->header, HEADER_TRACE_INFO))
@@ -834,8 +816,6 @@ static const struct option options[] = {
                    "Sample addresses"),
        OPT_BOOLEAN('n', "no-samples", &no_samples,
                    "don't sample"),
-       OPT_BOOLEAN('M', "multiplex", &multiplex,
-                   "multiplex counter output in a single channel"),
        OPT_END()
 };
 
@@ -887,9 +867,7 @@ int cmd_record(int argc, const char **argv, const char *prefix __used)
        for (i = 0; i < MAX_NR_CPUS; i++) {
                for (j = 0; j < MAX_COUNTERS; j++) {
                        fd[i][j] = malloc(sizeof(int)*thread_num);
-                       mmap_array[i][j] = zalloc(
-                               sizeof(struct mmap_data)*thread_num);
-                       if (!fd[i][j] || !mmap_array[i][j])
+                       if (!fd[i][j])
                                return -ENOMEM;
                }
        }
index 1d3c100..3592057 100644 (file)
@@ -116,7 +116,7 @@ static int perf_session__add_hist_entry(struct perf_session *self,
         * so we don't allocated the extra space needed because the stdio
         * code will not use it.
         */
-       if (use_browser)
+       if (use_browser > 0)
                err = hist_entry__inc_addr_samples(he, al->addr);
 out_free_syms:
        free(syms);
@@ -288,6 +288,38 @@ static size_t hists__fprintf_nr_sample_events(struct hists *self,
        return ret + fprintf(fp, "\n#\n");
 }
 
+static int hists__tty_browse_tree(struct rb_root *tree, const char *help)
+{
+       struct rb_node *next = rb_first(tree);
+
+       while (next) {
+               struct hists *hists = rb_entry(next, struct hists, rb_node);
+               const char *evname = NULL;
+
+               if (rb_first(&hists->entries) != rb_last(&hists->entries))
+                       evname = __event_name(hists->type, hists->config);
+
+               hists__fprintf_nr_sample_events(hists, evname, stdout);
+               hists__fprintf(hists, NULL, false, stdout);
+               fprintf(stdout, "\n\n");
+               next = rb_next(&hists->rb_node);
+       }
+
+       if (sort_order == default_sort_order &&
+           parent_pattern == default_parent_pattern) {
+               fprintf(stdout, "#\n# (%s)\n#\n", help);
+
+               if (show_threads) {
+                       bool style = !strcmp(pretty_printing_style, "raw");
+                       perf_read_values_display(stdout, &show_threads_values,
+                                                style);
+                       perf_read_values_destroy(&show_threads_values);
+               }
+       }
+
+       return 0;
+}
+
 static int __cmd_report(void)
 {
        int ret = -EINVAL;
@@ -330,34 +362,14 @@ static int __cmd_report(void)
                hists = rb_entry(next, struct hists, rb_node);
                hists__collapse_resort(hists);
                hists__output_resort(hists);
-               if (use_browser)
-                       hists__browse(hists, help, input_name);
-               else {
-                       const char *evname = NULL;
-                       if (rb_first(&session->hists.entries) !=
-                           rb_last(&session->hists.entries))
-                               evname = __event_name(hists->type, hists->config);
-
-                       hists__fprintf_nr_sample_events(hists, evname, stdout);
-
-                       hists__fprintf(hists, NULL, false, stdout);
-                       fprintf(stdout, "\n\n");
-               }
-
                next = rb_next(&hists->rb_node);
        }
 
-       if (!use_browser && sort_order == default_sort_order &&
-           parent_pattern == default_parent_pattern) {
-               fprintf(stdout, "#\n# (%s)\n#\n", help);
+       if (use_browser > 0)
+               hists__tui_browse_tree(&session->hists_tree, help);
+       else
+               hists__tty_browse_tree(&session->hists_tree, help);
 
-               if (show_threads) {
-                       bool style = !strcmp(pretty_printing_style, "raw");
-                       perf_read_values_display(stdout, &show_threads_values,
-                                                style);
-                       perf_read_values_destroy(&show_threads_values);
-               }
-       }
 out_delete:
        perf_session__delete(session);
        return ret;
@@ -491,7 +503,7 @@ int cmd_report(int argc, const char **argv, const char *prefix __used)
         * so don't allocate extra space that won't be used in the stdio
         * implementation.
         */
-       if (use_browser)
+       if (use_browser > 0)
                symbol_conf.priv_size = sizeof(struct sym_priv);
 
        if (symbol__init() < 0)
index ff8c413..9a39ca3 100644 (file)
@@ -50,6 +50,7 @@
 
 #include <sys/prctl.h>
 #include <math.h>
+#include <locale.h>
 
 static struct perf_event_attr default_attrs[] = {
 
@@ -80,6 +81,8 @@ static pid_t                  *all_tids                       =  NULL;
 static int                     thread_num                      =  0;
 static pid_t                   child_pid                       = -1;
 static bool                    null_run                        =  false;
+static bool                    big_num                         =  false;
+
 
 static int                     *fd[MAX_NR_CPUS][MAX_COUNTERS];
 
@@ -377,7 +380,7 @@ static void nsec_printout(int counter, double avg)
 {
        double msecs = avg / 1e6;
 
-       fprintf(stderr, " %14.6f  %-24s", msecs, event_name(counter));
+       fprintf(stderr, " %18.6f  %-24s", msecs, event_name(counter));
 
        if (MATCH_EVENT(SOFTWARE, SW_TASK_CLOCK, counter)) {
                fprintf(stderr, " # %10.3f CPUs ",
@@ -389,7 +392,10 @@ static void abs_printout(int counter, double avg)
 {
        double total, ratio = 0.0;
 
-       fprintf(stderr, " %14.0f  %-24s", avg, event_name(counter));
+       if (big_num)
+               fprintf(stderr, " %'18.0f  %-24s", avg, event_name(counter));
+       else
+               fprintf(stderr, " %18.0f  %-24s", avg, event_name(counter));
 
        if (MATCH_EVENT(HARDWARE, HW_INSTRUCTIONS, counter)) {
                total = avg_stats(&runtime_cycles_stats);
@@ -426,7 +432,7 @@ static void print_counter(int counter)
        int scaled = event_scaled[counter];
 
        if (scaled == -1) {
-               fprintf(stderr, " %14s  %-24s\n",
+               fprintf(stderr, " %18s  %-24s\n",
                        "<not counted>", event_name(counter));
                return;
        }
@@ -477,7 +483,7 @@ static void print_stat(int argc, const char **argv)
                print_counter(counter);
 
        fprintf(stderr, "\n");
-       fprintf(stderr, " %14.9f  seconds time elapsed",
+       fprintf(stderr, " %18.9f  seconds time elapsed",
                        avg_stats(&walltime_nsecs_stats)/1e9);
        if (run_count > 1) {
                fprintf(stderr, "   ( +- %7.3f%% )",
@@ -534,6 +540,8 @@ static const struct option options[] = {
                    "repeat command and print average + stddev (max: 100)"),
        OPT_BOOLEAN('n', "null", &null_run,
                    "null run - dont start any counters"),
+       OPT_BOOLEAN('B', "big-num", &big_num,
+                   "print large numbers with thousands\' separators"),
        OPT_END()
 };
 
@@ -542,6 +550,8 @@ int cmd_stat(int argc, const char **argv, const char *prefix __used)
        int status;
        int i,j;
 
+       setlocale(LC_ALL, "");
+
        argc = parse_options(argc, argv, options, stat_usage,
                PARSE_OPT_STOP_AT_NON_OPTION);
        if (!argc && target_pid == -1 && target_tid == -1)
index 08e0e5d..6e48711 100644 (file)
 #include "util/parse-events.h"
 #include "util/debugfs.h"
 
-bool use_browser;
-
 const char perf_usage_string[] =
        "perf [--version] [--help] COMMAND [ARGS]";
 
 const char perf_more_info_string[] =
        "See 'perf help COMMAND' for more information on a specific command.";
 
+int use_browser = -1;
 static int use_pager = -1;
+
 struct pager_config {
        const char *cmd;
        int val;
@@ -49,6 +49,24 @@ int check_pager_config(const char *cmd)
        return c.val;
 }
 
+static int tui_command_config(const char *var, const char *value, void *data)
+{
+       struct pager_config *c = data;
+       if (!prefixcmp(var, "tui.") && !strcmp(var + 4, c->cmd))
+               c->val = perf_config_bool(var, value);
+       return 0;
+}
+
+/* returns 0 for "no tui", 1 for "use tui", and -1 for "not specified" */
+static int check_tui_config(const char *cmd)
+{
+       struct pager_config c;
+       c.cmd = cmd;
+       c.val = -1;
+       perf_config(tui_command_config, &c);
+       return c.val;
+}
+
 static void commit_pager_choice(void)
 {
        switch (use_pager) {
@@ -255,6 +273,9 @@ static int run_builtin(struct cmd_struct *p, int argc, const char **argv)
        if (p->option & RUN_SETUP)
                prefix = NULL; /* setup_perf_directory(); */
 
+       if (use_browser == -1)
+               use_browser = check_tui_config(p->cmd);
+
        if (use_pager == -1 && p->option & RUN_SETUP)
                use_pager = check_pager_config(p->cmd);
        if (use_pager == -1 && p->option & USE_PAGER)
index a791dd4..0e76aff 100644 (file)
@@ -1,86 +1,5 @@
 #include "cache.h"
 
-/*
- * Do not use this for inspecting *tracked* content.  When path is a
- * symlink to a directory, we do not want to say it is a directory when
- * dealing with tracked content in the working tree.
- */
-static int is_directory(const char *path)
-{
-       struct stat st;
-       return (!stat(path, &st) && S_ISDIR(st.st_mode));
-}
-
-/* We allow "recursive" symbolic links. Only within reason, though. */
-#define MAXDEPTH 5
-
-const char *make_absolute_path(const char *path)
-{
-       static char bufs[2][PATH_MAX + 1], *buf = bufs[0], *next_buf = bufs[1];
-       char cwd[1024] = "";
-       int buf_index = 1, len;
-
-       int depth = MAXDEPTH;
-       char *last_elem = NULL;
-       struct stat st;
-
-       if (strlcpy(buf, path, PATH_MAX) >= PATH_MAX)
-               die ("Too long path: %.*s", 60, path);
-
-       while (depth--) {
-               if (!is_directory(buf)) {
-                       char *last_slash = strrchr(buf, '/');
-                       if (last_slash) {
-                               *last_slash = '\0';
-                               last_elem = xstrdup(last_slash + 1);
-                       } else {
-                               last_elem = xstrdup(buf);
-                               *buf = '\0';
-                       }
-               }
-
-               if (*buf) {
-                       if (!*cwd && !getcwd(cwd, sizeof(cwd)))
-                               die ("Could not get current working directory");
-
-                       if (chdir(buf))
-                               die ("Could not switch to '%s'", buf);
-               }
-               if (!getcwd(buf, PATH_MAX))
-                       die ("Could not get current working directory");
-
-               if (last_elem) {
-                       len = strlen(buf);
-
-                       if (len + strlen(last_elem) + 2 > PATH_MAX)
-                               die ("Too long path name: '%s/%s'",
-                                               buf, last_elem);
-                       buf[len] = '/';
-                       strcpy(buf + len + 1, last_elem);
-                       free(last_elem);
-                       last_elem = NULL;
-               }
-
-               if (!lstat(buf, &st) && S_ISLNK(st.st_mode)) {
-                       len = readlink(buf, next_buf, PATH_MAX);
-                       if (len < 0)
-                               die ("Invalid symlink: %s", buf);
-                       if (PATH_MAX <= len)
-                               die("symbolic link too long: %s", buf);
-                       next_buf[len] = '\0';
-                       buf = next_buf;
-                       buf_index = 1 - buf_index;
-                       next_buf = bufs[buf_index];
-               } else
-                       break;
-       }
-
-       if (*cwd && chdir(cwd))
-               die ("Could not change back to '%s'", cwd);
-
-       return buf;
-}
-
 static const char *get_pwd_cwd(void)
 {
        static char cwd[PATH_MAX + 1];
index 0f60a39..70c5cf8 100644 (file)
@@ -6,6 +6,8 @@
  * Copyright (C) 2009, 2010 Red Hat Inc.
  * Copyright (C) 2009, 2010 Arnaldo Carvalho de Melo <acme@redhat.com>
  */
+#include "util.h"
+#include <stdio.h>
 #include "build-id.h"
 #include "event.h"
 #include "symbol.h"
@@ -37,3 +39,23 @@ struct perf_event_ops build_id__mark_dso_hit_ops = {
        .mmap   = event__process_mmap,
        .fork   = event__process_task,
 };
+
+char *dso__build_id_filename(struct dso *self, char *bf, size_t size)
+{
+       char build_id_hex[BUILD_ID_SIZE * 2 + 1];
+       const char *home;
+
+       if (!self->has_build_id)
+               return NULL;
+
+       build_id__sprintf(self->build_id, sizeof(self->build_id), build_id_hex);
+       home = getenv("HOME");
+       if (bf == NULL) {
+               if (asprintf(&bf, "%s/%s/.build-id/%.2s/%s", home,
+                            DEBUG_CACHE_DIR, build_id_hex, build_id_hex + 2) < 0)
+                       return NULL;
+       } else
+               snprintf(bf, size, "%s/%s/.build-id/%.2s/%s", home,
+                        DEBUG_CACHE_DIR, build_id_hex, build_id_hex + 2);
+       return bf;
+}
index 1d981d6..5dafb00 100644 (file)
@@ -5,4 +5,6 @@
 
 extern struct perf_event_ops build_id__mark_dso_hit_ops;
 
+char *dso__build_id_filename(struct dso *self, char *bf, size_t size);
+
 #endif
index 4b9aab7..65fe664 100644 (file)
 
 #define PERF_DIR_ENVIRONMENT "PERF_DIR"
 #define PERF_WORK_TREE_ENVIRONMENT "PERF_WORK_TREE"
-#define DEFAULT_PERF_DIR_ENVIRONMENT ".perf"
-#define DB_ENVIRONMENT "PERF_OBJECT_DIRECTORY"
-#define INDEX_ENVIRONMENT "PERF_INDEX_FILE"
-#define GRAFT_ENVIRONMENT "PERF_GRAFT_FILE"
-#define TEMPLATE_DIR_ENVIRONMENT "PERF_TEMPLATE_DIR"
-#define CONFIG_ENVIRONMENT "PERF_CONFIG"
 #define EXEC_PATH_ENVIRONMENT "PERF_EXEC_PATH"
-#define CEILING_DIRECTORIES_ENVIRONMENT "PERF_CEILING_DIRECTORIES"
-#define PERFATTRIBUTES_FILE ".perfattributes"
-#define INFOATTRIBUTES_FILE "info/attributes"
-#define ATTRIBUTE_MACRO_PREFIX "[attr]"
+#define DEFAULT_PERF_DIR_ENVIRONMENT ".perf"
 #define PERF_DEBUGFS_ENVIRONMENT "PERF_DEBUGFS_DIR"
 
 typedef int (*config_fn_t)(const char *, const char *, void *);
 extern int perf_default_config(const char *, const char *, void *);
-extern int perf_config_from_file(config_fn_t fn, const char *, void *);
 extern int perf_config(config_fn_t fn, void *);
-extern int perf_parse_ulong(const char *, unsigned long *);
 extern int perf_config_int(const char *, const char *);
-extern unsigned long perf_config_ulong(const char *, const char *);
-extern int perf_config_bool_or_int(const char *, const char *, int *);
 extern int perf_config_bool(const char *, const char *);
-extern int perf_config_string(const char **, const char *, const char *);
-extern int perf_config_set(const char *, const char *);
-extern int perf_config_set_multivar(const char *, const char *, const char *, int);
-extern int perf_config_rename_section(const char *, const char *);
-extern const char *perf_etc_perfconfig(void);
-extern int check_repository_format_version(const char *var, const char *value, void *cb);
-extern int perf_config_system(void);
-extern int perf_config_global(void);
 extern int config_error_nonbool(const char *);
-extern const char *config_exclusive_filename;
-
-#define MAX_PERFNAME (1000)
-extern char perf_default_email[MAX_PERFNAME];
-extern char perf_default_name[MAX_PERFNAME];
-extern int user_ident_explicitly_given;
-
-extern const char *perf_log_output_encoding;
-extern const char *perf_mailmap_file;
-
-/* IO helper functions */
-extern void maybe_flush_or_die(FILE *, const char *);
-extern int copy_fd(int ifd, int ofd);
-extern int copy_file(const char *dst, const char *src, int mode);
-extern ssize_t write_in_full(int fd, const void *buf, size_t count);
-extern void write_or_die(int fd, const void *buf, size_t count);
-extern int write_or_whine(int fd, const void *buf, size_t count, const char *msg);
-extern int write_or_whine_pipe(int fd, const void *buf, size_t count, const char *msg);
-extern void fsync_or_die(int fd, const char *);
 
 /* pager.c */
 extern void setup_pager(void);
@@ -70,7 +30,7 @@ extern const char *pager_program;
 extern int pager_in_use(void);
 extern int pager_use_color;
 
-extern bool use_browser;
+extern int use_browser;
 
 #ifdef NO_NEWT_SUPPORT
 static inline void setup_browser(void)
@@ -83,9 +43,6 @@ void setup_browser(void);
 void exit_browser(bool wait_for_ok);
 #endif
 
-extern const char *editor_program;
-extern const char *excludes_file;
-
 char *alias_lookup(const char *alias);
 int split_cmdline(char *cmdline, const char ***argv);
 
@@ -115,22 +72,12 @@ static inline int is_absolute_path(const char *path)
        return path[0] == '/';
 }
 
-const char *make_absolute_path(const char *path);
 const char *make_nonrelative_path(const char *path);
-const char *make_relative_path(const char *abs, const char *base);
-int normalize_path_copy(char *dst, const char *src);
-int longest_ancestor_length(const char *path, const char *prefix_list);
 char *strip_path_suffix(const char *path, const char *suffix);
 
 extern char *mkpath(const char *fmt, ...) __attribute__((format (printf, 1, 2)));
 extern char *perf_path(const char *fmt, ...) __attribute__((format (printf, 1, 2)));
-/* perf_mkstemp() - create tmp file honoring TMPDIR variable */
-extern int perf_mkstemp(char *path, size_t len, const char *template);
 
-extern char *mksnpath(char *buf, size_t n, const char *fmt, ...)
-       __attribute__((format (printf, 3, 4)));
-extern char *perf_snpath(char *buf, size_t n, const char *fmt, ...)
-       __attribute__((format (printf, 3, 4)));
 extern char *perf_pathdup(const char *fmt, ...)
        __attribute__((format (printf, 1, 2)));
 
index 21a52e0..62b69ad 100644 (file)
@@ -15,6 +15,7 @@
 #include <errno.h>
 #include <math.h>
 
+#include "util.h"
 #include "callchain.h"
 
 bool ip_callchain__valid(struct ip_callchain *chain, event_t *event)
index 1cba1f5..1ca73e4 100644 (file)
@@ -5,7 +5,6 @@
 #include <linux/list.h>
 #include <linux/rbtree.h>
 #include "event.h"
-#include "util.h"
 #include "symbol.h"
 
 enum chain_mode {
index 8784649..dabe892 100644 (file)
@@ -16,7 +16,7 @@ static const char *config_file_name;
 static int config_linenr;
 static int config_file_eof;
 
-const char *config_exclusive_filename = NULL;
+static const char *config_exclusive_filename;
 
 static int get_next_char(void)
 {
@@ -291,19 +291,6 @@ static int perf_parse_long(const char *value, long *ret)
        return 0;
 }
 
-int perf_parse_ulong(const char *value, unsigned long *ret)
-{
-       if (value && *value) {
-               char *end;
-               unsigned long val = strtoul(value, &end, 0);
-               if (!parse_unit_factor(end, &val))
-                       return 0;
-               *ret = val;
-               return 1;
-       }
-       return 0;
-}
-
 static void die_bad_config(const char *name)
 {
        if (config_file_name)
@@ -319,15 +306,7 @@ int perf_config_int(const char *name, const char *value)
        return ret;
 }
 
-unsigned long perf_config_ulong(const char *name, const char *value)
-{
-       unsigned long ret;
-       if (!perf_parse_ulong(value, &ret))
-               die_bad_config(name);
-       return ret;
-}
-
-int perf_config_bool_or_int(const char *name, const char *value, int *is_bool)
+static int perf_config_bool_or_int(const char *name, const char *value, int *is_bool)
 {
        *is_bool = 1;
        if (!value)
@@ -348,14 +327,6 @@ int perf_config_bool(const char *name, const char *value)
        return !!perf_config_bool_or_int(name, value, &discard);
 }
 
-int perf_config_string(const char **dest, const char *var, const char *value)
-{
-       if (!value)
-               return config_error_nonbool(var);
-       *dest = strdup(value);
-       return 0;
-}
-
 static int perf_default_core_config(const char *var __used, const char *value __used)
 {
        /* Add other config variables here and to Documentation/config.txt. */
@@ -371,7 +342,7 @@ int perf_default_config(const char *var, const char *value, void *dummy __used)
        return 0;
 }
 
-int perf_config_from_file(config_fn_t fn, const char *filename, void *data)
+static int perf_config_from_file(config_fn_t fn, const char *filename, void *data)
 {
        int ret;
        FILE *f = fopen(filename, "r");
@@ -389,7 +360,7 @@ int perf_config_from_file(config_fn_t fn, const char *filename, void *data)
        return ret;
 }
 
-const char *perf_etc_perfconfig(void)
+static const char *perf_etc_perfconfig(void)
 {
        static const char *system_wide;
        if (!system_wide)
@@ -403,12 +374,12 @@ static int perf_env_bool(const char *k, int def)
        return v ? perf_config_bool(k, v) : def;
 }
 
-int perf_config_system(void)
+static int perf_config_system(void)
 {
        return !perf_env_bool("PERF_CONFIG_NOSYSTEM", 0);
 }
 
-int perf_config_global(void)
+static int perf_config_global(void)
 {
        return !perf_env_bool("PERF_CONFIG_NOGLOBAL", 0);
 }
@@ -450,426 +421,6 @@ int perf_config(config_fn_t fn, void *data)
 }
 
 /*
- * Find all the stuff for perf_config_set() below.
- */
-
-#define MAX_MATCHES 512
-
-static struct {
-       int baselen;
-       char* key;
-       int do_not_match;
-       regex_t* value_regex;
-       int multi_replace;
-       size_t offset[MAX_MATCHES];
-       enum { START, SECTION_SEEN, SECTION_END_SEEN, KEY_SEEN } state;
-       int seen;
-} store;
-
-static int matches(const char* key, const char* value)
-{
-       return !strcmp(key, store.key) &&
-               (store.value_regex == NULL ||
-                (store.do_not_match ^
-                 !regexec(store.value_regex, value, 0, NULL, 0)));
-}
-
-static int store_aux(const char* key, const char* value, void *cb __used)
-{
-       int section_len;
-       const char *ep;
-
-       switch (store.state) {
-       case KEY_SEEN:
-               if (matches(key, value)) {
-                       if (store.seen == 1 && store.multi_replace == 0) {
-                               warning("%s has multiple values", key);
-                       } else if (store.seen >= MAX_MATCHES) {
-                               error("too many matches for %s", key);
-                               return 1;
-                       }
-
-                       store.offset[store.seen] = ftell(config_file);
-                       store.seen++;
-               }
-               break;
-       case SECTION_SEEN:
-               /*
-                * What we are looking for is in store.key (both
-                * section and var), and its section part is baselen
-                * long.  We found key (again, both section and var).
-                * We would want to know if this key is in the same
-                * section as what we are looking for.  We already
-                * know we are in the same section as what should
-                * hold store.key.
-                */
-               ep = strrchr(key, '.');
-               section_len = ep - key;
-
-               if ((section_len != store.baselen) ||
-                   memcmp(key, store.key, section_len+1)) {
-                       store.state = SECTION_END_SEEN;
-                       break;
-               }
-
-               /*
-                * Do not increment matches: this is no match, but we
-                * just made sure we are in the desired section.
-                */
-               store.offset[store.seen] = ftell(config_file);
-               /* fallthru */
-       case SECTION_END_SEEN:
-       case START:
-               if (matches(key, value)) {
-                       store.offset[store.seen] = ftell(config_file);
-                       store.state = KEY_SEEN;
-                       store.seen++;
-               } else {
-                       if (strrchr(key, '.') - key == store.baselen &&
-                             !strncmp(key, store.key, store.baselen)) {
-                                       store.state = SECTION_SEEN;
-                                       store.offset[store.seen] = ftell(config_file);
-                       }
-               }
-       default:
-               break;
-       }
-       return 0;
-}
-
-static int store_write_section(int fd, const char* key)
-{
-       const char *dot;
-       int i, success;
-       struct strbuf sb = STRBUF_INIT;
-
-       dot = memchr(key, '.', store.baselen);
-       if (dot) {
-               strbuf_addf(&sb, "[%.*s \"", (int)(dot - key), key);
-               for (i = dot - key + 1; i < store.baselen; i++) {
-                       if (key[i] == '"' || key[i] == '\\')
-                               strbuf_addch(&sb, '\\');
-                       strbuf_addch(&sb, key[i]);
-               }
-               strbuf_addstr(&sb, "\"]\n");
-       } else {
-               strbuf_addf(&sb, "[%.*s]\n", store.baselen, key);
-       }
-
-       success = (write_in_full(fd, sb.buf, sb.len) == (ssize_t)sb.len);
-       strbuf_release(&sb);
-
-       return success;
-}
-
-static int store_write_pair(int fd, const char* key, const char* value)
-{
-       int i, success;
-       int length = strlen(key + store.baselen + 1);
-       const char *quote = "";
-       struct strbuf sb = STRBUF_INIT;
-
-       /*
-        * Check to see if the value needs to be surrounded with a dq pair.
-        * Note that problematic characters are always backslash-quoted; this
-        * check is about not losing leading or trailing SP and strings that
-        * follow beginning-of-comment characters (i.e. ';' and '#') by the
-        * configuration parser.
-        */
-       if (value[0] == ' ')
-               quote = "\"";
-       for (i = 0; value[i]; i++)
-               if (value[i] == ';' || value[i] == '#')
-                       quote = "\"";
-       if (i && value[i - 1] == ' ')
-               quote = "\"";
-
-       strbuf_addf(&sb, "\t%.*s = %s",
-                   length, key + store.baselen + 1, quote);
-
-       for (i = 0; value[i]; i++)
-               switch (value[i]) {
-               case '\n':
-                       strbuf_addstr(&sb, "\\n");
-                       break;
-               case '\t':
-                       strbuf_addstr(&sb, "\\t");
-                       break;
-               case '"':
-               case '\\':
-                       strbuf_addch(&sb, '\\');
-               default:
-                       strbuf_addch(&sb, value[i]);
-                       break;
-               }
-       strbuf_addf(&sb, "%s\n", quote);
-
-       success = (write_in_full(fd, sb.buf, sb.len) == (ssize_t)sb.len);
-       strbuf_release(&sb);
-
-       return success;
-}
-
-static ssize_t find_beginning_of_line(const char* contents, size_t size,
-       size_t offset_, int* found_bracket)
-{
-       size_t equal_offset = size, bracket_offset = size;
-       ssize_t offset;
-
-contline:
-       for (offset = offset_-2; offset > 0
-                       && contents[offset] != '\n'; offset--)
-               switch (contents[offset]) {
-                       case '=': equal_offset = offset; break;
-                       case ']': bracket_offset = offset; break;
-                       default: break;
-               }
-       if (offset > 0 && contents[offset-1] == '\\') {
-               offset_ = offset;
-               goto contline;
-       }
-       if (bracket_offset < equal_offset) {
-               *found_bracket = 1;
-               offset = bracket_offset+1;
-       } else
-               offset++;
-
-       return offset;
-}
-
-int perf_config_set(const char* key, const char* value)
-{
-       return perf_config_set_multivar(key, value, NULL, 0);
-}
-
-/*
- * If value==NULL, unset in (remove from) config,
- * if value_regex!=NULL, disregard key/value pairs where value does not match.
- * if multi_replace==0, nothing, or only one matching key/value is replaced,
- *     else all matching key/values (regardless how many) are removed,
- *     before the new pair is written.
- *
- * Returns 0 on success.
- *
- * This function does this:
- *
- * - it locks the config file by creating ".perf/config.lock"
- *
- * - it then parses the config using store_aux() as validator to find
- *   the position on the key/value pair to replace. If it is to be unset,
- *   it must be found exactly once.
- *
- * - the config file is mmap()ed and the part before the match (if any) is
- *   written to the lock file, then the changed part and the rest.
- *
- * - the config file is removed and the lock file rename()d to it.
- *
- */
-int perf_config_set_multivar(const char* key, const char* value,
-       const char* value_regex, int multi_replace)
-{
-       int i, dot;
-       int fd = -1, in_fd;
-       int ret = 0;
-       char* config_filename;
-       const char* last_dot = strrchr(key, '.');
-
-       if (config_exclusive_filename)
-               config_filename = strdup(config_exclusive_filename);
-       else
-               config_filename = perf_pathdup("config");
-
-       /*
-        * Since "key" actually contains the section name and the real
-        * key name separated by a dot, we have to know where the dot is.
-        */
-
-       if (last_dot == NULL) {
-               error("key does not contain a section: %s", key);
-               ret = 2;
-               goto out_free;
-       }
-       store.baselen = last_dot - key;
-
-       store.multi_replace = multi_replace;
-
-       /*
-        * Validate the key and while at it, lower case it for matching.
-        */
-       store.key = malloc(strlen(key) + 1);
-       dot = 0;
-       for (i = 0; key[i]; i++) {
-               unsigned char c = key[i];
-               if (c == '.')
-                       dot = 1;
-               /* Leave the extended basename untouched.. */
-               if (!dot || i > store.baselen) {
-                       if (!iskeychar(c) || (i == store.baselen+1 && !isalpha(c))) {
-                               error("invalid key: %s", key);
-                               free(store.key);
-                               ret = 1;
-                               goto out_free;
-                       }
-                       c = tolower(c);
-               } else if (c == '\n') {
-                       error("invalid key (newline): %s", key);
-                       free(store.key);
-                       ret = 1;
-                       goto out_free;
-               }
-               store.key[i] = c;
-       }
-       store.key[i] = 0;
-
-       /*
-        * If .perf/config does not exist yet, write a minimal version.
-        */
-       in_fd = open(config_filename, O_RDONLY);
-       if ( in_fd < 0 ) {
-               free(store.key);
-
-               if ( ENOENT != errno ) {
-                       error("opening %s: %s", config_filename,
-                             strerror(errno));
-                       ret = 3; /* same as "invalid config file" */
-                       goto out_free;
-               }
-               /* if nothing to unset, error out */
-               if (value == NULL) {
-                       ret = 5;
-                       goto out_free;
-               }
-
-               store.key = (char*)key;
-               if (!store_write_section(fd, key) ||
-                   !store_write_pair(fd, key, value))
-                       goto write_err_out;
-       } else {
-               struct stat st;
-               char *contents;
-               ssize_t contents_sz, copy_begin, copy_end;
-               int new_line = 0;
-
-               if (value_regex == NULL)
-                       store.value_regex = NULL;
-               else {
-                       if (value_regex[0] == '!') {
-                               store.do_not_match = 1;
-                               value_regex++;
-                       } else
-                               store.do_not_match = 0;
-
-                       store.value_regex = (regex_t*)malloc(sizeof(regex_t));
-                       if (regcomp(store.value_regex, value_regex,
-                                       REG_EXTENDED)) {
-                               error("invalid pattern: %s", value_regex);
-                               free(store.value_regex);
-                               ret = 6;
-                               goto out_free;
-                       }
-               }
-
-               store.offset[0] = 0;
-               store.state = START;
-               store.seen = 0;
-
-               /*
-                * After this, store.offset will contain the *end* offset
-                * of the last match, or remain at 0 if no match was found.
-                * As a side effect, we make sure to transform only a valid
-                * existing config file.
-                */
-               if (perf_config_from_file(store_aux, config_filename, NULL)) {
-                       error("invalid config file %s", config_filename);
-                       free(store.key);
-                       if (store.value_regex != NULL) {
-                               regfree(store.value_regex);
-                               free(store.value_regex);
-                       }
-                       ret = 3;
-                       goto out_free;
-               }
-
-               free(store.key);
-               if (store.value_regex != NULL) {
-                       regfree(store.value_regex);
-                       free(store.value_regex);
-               }
-
-               /* if nothing to unset, or too many matches, error out */
-               if ((store.seen == 0 && value == NULL) ||
-                               (store.seen > 1 && multi_replace == 0)) {
-                       ret = 5;
-                       goto out_free;
-               }
-
-               fstat(in_fd, &st);
-               contents_sz = xsize_t(st.st_size);
-               contents = mmap(NULL, contents_sz, PROT_READ,
-                       MAP_PRIVATE, in_fd, 0);
-               close(in_fd);
-
-               if (store.seen == 0)
-                       store.seen = 1;
-
-               for (i = 0, copy_begin = 0; i < store.seen; i++) {
-                       if (store.offset[i] == 0) {
-                               store.offset[i] = copy_end = contents_sz;
-                       } else if (store.state != KEY_SEEN) {
-                               copy_end = store.offset[i];
-                       } else
-                               copy_end = find_beginning_of_line(
-                                       contents, contents_sz,
-                                       store.offset[i]-2, &new_line);
-
-                       if (copy_end > 0 && contents[copy_end-1] != '\n')
-                               new_line = 1;
-
-                       /* write the first part of the config */
-                       if (copy_end > copy_begin) {
-                               if (write_in_full(fd, contents + copy_begin,
-                                                 copy_end - copy_begin) <
-                                   copy_end - copy_begin)
-                                       goto write_err_out;
-                               if (new_line &&
-                                   write_in_full(fd, "\n", 1) != 1)
-                                       goto write_err_out;
-                       }
-                       copy_begin = store.offset[i];
-               }
-
-               /* write the pair (value == NULL means unset) */
-               if (value != NULL) {
-                       if (store.state == START) {
-                               if (!store_write_section(fd, key))
-                                       goto write_err_out;
-                       }
-                       if (!store_write_pair(fd, key, value))
-                               goto write_err_out;
-               }
-
-               /* write the rest of the config */
-               if (copy_begin < contents_sz)
-                       if (write_in_full(fd, contents + copy_begin,
-                                         contents_sz - copy_begin) <
-                           contents_sz - copy_begin)
-                               goto write_err_out;
-
-               munmap(contents, contents_sz);
-       }
-
-       ret = 0;
-
-out_free:
-       free(config_filename);
-       return ret;
-
-write_err_out:
-       goto out_free;
-
-}
-
-/*
  * Call this to report error for your variable that should not
  * get a boolean value (i.e. "[my] var" means "true").
  */
index 2745605..67eeff5 100644 (file)
@@ -53,8 +53,8 @@ const char *perf_extract_argv0_path(const char *argv0)
                slash--;
 
        if (slash >= argv0) {
-               argv0_path = xstrndup(argv0, slash - argv0);
-               return slash + 1;
+               argv0_path = strndup(argv0, slash - argv0);
+               return argv0_path ? slash + 1 : NULL;
        }
 
        return argv0;
@@ -116,7 +116,7 @@ void setup_path(void)
        strbuf_release(&new_path);
 }
 
-const char **prepare_perf_cmd(const char **argv)
+static const char **prepare_perf_cmd(const char **argv)
 {
        int argc;
        const char **nargv;
index 31647ac..bc4b915 100644 (file)
@@ -5,7 +5,6 @@ extern void perf_set_argv_exec_path(const char *exec_path);
 extern const char *perf_extract_argv0_path(const char *path);
 extern const char *perf_exec_path(void);
 extern void setup_path(void);
-extern const char **prepare_perf_cmd(const char **argv);
 extern int execv_perf_cmd(const char **argv); /* NULL terminated */
 extern int execl_perf_cmd(const char *cmd, ...);
 extern const char *system_path(const char *path);
index 8847bec..1f62435 100644 (file)
@@ -221,29 +221,38 @@ static int __dsos__write_buildid_table(struct list_head *head, pid_t pid,
        return 0;
 }
 
+static int machine__write_buildid_table(struct machine *self, int fd)
+{
+       int err;
+       u16 kmisc = PERF_RECORD_MISC_KERNEL,
+           umisc = PERF_RECORD_MISC_USER;
+
+       if (!machine__is_host(self)) {
+               kmisc = PERF_RECORD_MISC_GUEST_KERNEL;
+               umisc = PERF_RECORD_MISC_GUEST_USER;
+       }
+
+       err = __dsos__write_buildid_table(&self->kernel_dsos, self->pid,
+                                         kmisc, fd);
+       if (err == 0)
+               err = __dsos__write_buildid_table(&self->user_dsos,
+                                                 self->pid, umisc, fd);
+       return err;
+}
+
 static int dsos__write_buildid_table(struct perf_header *header, int fd)
 {
        struct perf_session *session = container_of(header,
                        struct perf_session, header);
        struct rb_node *nd;
-       int err = 0;
-       u16 kmisc, umisc;
+       int err = machine__write_buildid_table(&session->host_machine, fd);
+
+       if (err)
+               return err;
 
        for (nd = rb_first(&session->machines); nd; nd = rb_next(nd)) {
                struct machine *pos = rb_entry(nd, struct machine, rb_node);
-               if (machine__is_host(pos)) {
-                       kmisc = PERF_RECORD_MISC_KERNEL;
-                       umisc = PERF_RECORD_MISC_USER;
-               } else {
-                       kmisc = PERF_RECORD_MISC_GUEST_KERNEL;
-                       umisc = PERF_RECORD_MISC_GUEST_USER;
-               }
-
-               err = __dsos__write_buildid_table(&pos->kernel_dsos, pos->pid,
-                                                 kmisc, fd);
-               if (err == 0)
-                       err = __dsos__write_buildid_table(&pos->user_dsos,
-                                                         pos->pid, umisc, fd);
+               err = machine__write_buildid_table(pos, fd);
                if (err)
                        break;
        }
@@ -363,12 +372,17 @@ static int __dsos__cache_build_ids(struct list_head *head, const char *debugdir)
        return err;
 }
 
-static int dsos__cache_build_ids(struct perf_header *self)
+static int machine__cache_build_ids(struct machine *self, const char *debugdir)
+{
+       int ret = __dsos__cache_build_ids(&self->kernel_dsos, debugdir);
+       ret |= __dsos__cache_build_ids(&self->user_dsos, debugdir);
+       return ret;
+}
+
+static int perf_session__cache_build_ids(struct perf_session *self)
 {
-       struct perf_session *session = container_of(self,
-                       struct perf_session, header);
        struct rb_node *nd;
-       int ret = 0;
+       int ret;
        char debugdir[PATH_MAX];
 
        snprintf(debugdir, sizeof(debugdir), "%s/%s", getenv("HOME"),
@@ -377,25 +391,30 @@ static int dsos__cache_build_ids(struct perf_header *self)
        if (mkdir(debugdir, 0755) != 0 && errno != EEXIST)
                return -1;
 
-       for (nd = rb_first(&session->machines); nd; nd = rb_next(nd)) {
+       ret = machine__cache_build_ids(&self->host_machine, debugdir);
+
+       for (nd = rb_first(&self->machines); nd; nd = rb_next(nd)) {
                struct machine *pos = rb_entry(nd, struct machine, rb_node);
-               ret |= __dsos__cache_build_ids(&pos->kernel_dsos, debugdir);
-               ret |= __dsos__cache_build_ids(&pos->user_dsos, debugdir);
+               ret |= machine__cache_build_ids(pos, debugdir);
        }
        return ret ? -1 : 0;
 }
 
-static bool dsos__read_build_ids(struct perf_header *self, bool with_hits)
+static bool machine__read_build_ids(struct machine *self, bool with_hits)
+{
+       bool ret = __dsos__read_build_ids(&self->kernel_dsos, with_hits);
+       ret |= __dsos__read_build_ids(&self->user_dsos, with_hits);
+       return ret;
+}
+
+static bool perf_session__read_build_ids(struct perf_session *self, bool with_hits)
 {
-       bool ret = false;
-       struct perf_session *session = container_of(self,
-                       struct perf_session, header);
        struct rb_node *nd;
+       bool ret = machine__read_build_ids(&self->host_machine, with_hits);
 
-       for (nd = rb_first(&session->machines); nd; nd = rb_next(nd)) {
+       for (nd = rb_first(&self->machines); nd; nd = rb_next(nd)) {
                struct machine *pos = rb_entry(nd, struct machine, rb_node);
-               ret |= __dsos__read_build_ids(&pos->kernel_dsos, with_hits);
-               ret |= __dsos__read_build_ids(&pos->user_dsos, with_hits);
+               ret |= machine__read_build_ids(pos, with_hits);
        }
 
        return ret;
@@ -404,12 +423,14 @@ static bool dsos__read_build_ids(struct perf_header *self, bool with_hits)
 static int perf_header__adds_write(struct perf_header *self, int fd)
 {
        int nr_sections;
+       struct perf_session *session;
        struct perf_file_section *feat_sec;
        int sec_size;
        u64 sec_start;
        int idx = 0, err;
 
-       if (dsos__read_build_ids(self, true))
+       session = container_of(self, struct perf_session, header);
+       if (perf_session__read_build_ids(session, true))
                perf_header__set_feat(self, HEADER_BUILD_ID);
 
        nr_sections = bitmap_weight(self->adds_features, HEADER_FEAT_BITS);
@@ -450,7 +471,7 @@ static int perf_header__adds_write(struct perf_header *self, int fd)
                }
                buildid_sec->size = lseek(fd, 0, SEEK_CUR) -
                                          buildid_sec->offset;
-               dsos__cache_build_ids(self);
+               perf_session__cache_build_ids(session);
        }
 
        lseek(fd, sec_start, SEEK_SET);
@@ -490,7 +511,6 @@ int perf_header__write(struct perf_header *self, int fd, bool at_exit)
 
        lseek(fd, sizeof(f_header), SEEK_SET);
 
-
        for (i = 0; i < self->attrs; i++) {
                attr = self->attr[i];
 
index fbb0097..6f2975a 100644 (file)
@@ -4,28 +4,6 @@
 #include "levenshtein.h"
 #include "help.h"
 
-/* most GUI terminals set COLUMNS (although some don't export it) */
-static int term_columns(void)
-{
-       char *col_string = getenv("COLUMNS");
-       int n_cols;
-
-       if (col_string && (n_cols = atoi(col_string)) > 0)
-               return n_cols;
-
-#ifdef TIOCGWINSZ
-       {
-               struct winsize ws;
-               if (!ioctl(1, TIOCGWINSZ, &ws)) {
-                       if (ws.ws_col)
-                               return ws.ws_col;
-               }
-       }
-#endif
-
-       return 80;
-}
-
 void add_cmdname(struct cmdnames *cmds, const char *name, size_t len)
 {
        struct cmdname *ent = malloc(sizeof(*ent) + len + 1);
@@ -96,9 +74,13 @@ static void pretty_print_string_list(struct cmdnames *cmds, int longest)
 {
        int cols = 1, rows;
        int space = longest + 1; /* min 1 SP between words */
-       int max_cols = term_columns() - 1; /* don't print *on* the edge */
+       struct winsize win;
+       int max_cols;
        int i, j;
 
+       get_term_dimensions(&win);
+       max_cols = win.ws_col - 1; /* don't print *on* the edge */
+
        if (space < max_cols)
                cols = max_cols / space;
        rows = (cmds->cnt + cols - 1) / cols;
@@ -324,7 +306,7 @@ const char *help_unknown_cmd(const char *cmd)
 
                main_cmds.names[0] = NULL;
                clean_cmdnames(&main_cmds);
-               fprintf(stderr, "WARNING: You called a Git program named '%s', "
+               fprintf(stderr, "WARNING: You called a perf program named '%s', "
                        "which does not exist.\n"
                        "Continuing under the assumption that you meant '%s'\n",
                        cmd, assumed);
index 9a71c94..cbf7eae 100644 (file)
@@ -1,4 +1,5 @@
 #include "util.h"
+#include "build-id.h"
 #include "hist.h"
 #include "session.h"
 #include "sort.h"
@@ -988,22 +989,42 @@ int hist_entry__annotate(struct hist_entry *self, struct list_head *head)
        struct symbol *sym = self->ms.sym;
        struct map *map = self->ms.map;
        struct dso *dso = map->dso;
-       const char *filename = dso->long_name;
+       char *filename = dso__build_id_filename(dso, NULL, 0);
+       bool free_filename = true;
        char command[PATH_MAX * 2];
        FILE *file;
+       int err = 0;
        u64 len;
 
-       if (!filename)
-               return -1;
+       if (filename == NULL) {
+               if (dso->has_build_id) {
+                       pr_err("Can't annotate %s: not enough memory\n",
+                              sym->name);
+                       return -ENOMEM;
+               }
+               goto fallback;
+       } else if (readlink(filename, command, sizeof(command)) < 0 ||
+                  strstr(command, "[kernel.kallsyms]") ||
+                  access(filename, R_OK)) {
+               free(filename);
+fallback:
+               /*
+                * If we don't have build-ids or the build-id file isn't in the
+                * cache, or is just a kallsyms file, well, lets hope that this
+                * DSO is the same as when 'perf record' ran.
+                */
+               filename = dso->long_name;
+               free_filename = false;
+       }
 
        if (dso->origin == DSO__ORIG_KERNEL) {
                if (dso->annotate_warned)
-                       return 0;
+                       goto out_free_filename;
+               err = -ENOENT;
                dso->annotate_warned = 1;
                pr_err("Can't annotate %s: No vmlinux file was found in the "
-                      "path:\n", sym->name);
-               vmlinux_path__fprintf(stderr);
-               return -1;
+                      "path\n", sym->name);
+               goto out_free_filename;
        }
 
        pr_debug("%s: filename=%s, sym=%s, start=%#Lx, end=%#Lx\n", __func__,
@@ -1025,14 +1046,17 @@ int hist_entry__annotate(struct hist_entry *self, struct list_head *head)
 
        file = popen(command, "r");
        if (!file)
-               return -1;
+               goto out_free_filename;
 
        while (!feof(file))
                if (hist_entry__parse_objdump_line(self, file, head) < 0)
                        break;
 
        pclose(file);
-       return 0;
+out_free_filename:
+       if (free_filename)
+               free(filename);
+       return err;
 }
 
 void hists__inc_nr_events(struct hists *self, u32 type)
index 6f17dcd..83fa33a 100644 (file)
@@ -98,12 +98,32 @@ void hists__filter_by_thread(struct hists *self, const struct thread *thread);
 #ifdef NO_NEWT_SUPPORT
 static inline int hists__browse(struct hists *self __used,
                                const char *helpline __used,
-                               const char *input_name __used)
+                               const char *ev_name __used)
 {
        return 0;
 }
+
+static inline int hists__tui_browse_tree(struct rb_root *self __used,
+                                        const char *help __used)
+{
+       return 0;
+}
+
+static inline int hist_entry__tui_annotate(struct hist_entry *self __used)
+{
+       return 0;
+}
+#define KEY_LEFT -1
+#define KEY_RIGHT -2
 #else
+#include <newt.h>
 int hists__browse(struct hists *self, const char *helpline,
-                 const char *input_name);
+                 const char *ev_name);
+int hist_entry__tui_annotate(struct hist_entry *self);
+
+#define KEY_LEFT NEWT_KEY_LEFT
+#define KEY_RIGHT NEWT_KEY_RIGHT
+
+int hists__tui_browse_tree(struct rb_root *self, const char *help);
 #endif
 #endif /* __PERF_HIST_H */
index ccb7c5b..d54c540 100644 (file)
@@ -1,7 +1,15 @@
 #define _GNU_SOURCE
 #include <stdio.h>
 #undef _GNU_SOURCE
-
+/*
+ * slang versions <= 2.0.6 have a "#if HAVE_LONG_LONG" that breaks
+ * the build if it isn't defined. Use the equivalent one that glibc
+ * has on features.h.
+ */
+#include <features.h>
+#ifndef HAVE_LONG_LONG
+#define HAVE_LONG_LONG __GLIBC_HAVE_LONG_LONG
+#endif
 #include <slang.h>
 #include <stdlib.h>
 #include <newt.h>
@@ -227,6 +235,15 @@ static bool dialog_yesno(const char *msg)
        return newtWinChoice(NULL, yes, no, (char *)msg) == 1;
 }
 
+static void ui__error_window(const char *fmt, ...)
+{
+       va_list ap;
+
+       va_start(ap, fmt);
+       newtWinMessagev((char *)"Error", (char *)"Ok", (char *)fmt, ap);
+       va_end(ap);
+}
+
 #define HE_COLORSET_TOP                50
 #define HE_COLORSET_MEDIUM     51
 #define HE_COLORSET_NORMAL     52
@@ -375,8 +392,11 @@ static int ui_browser__run(struct ui_browser *self, const char *title,
        newtFormAddHotKey(self->form, NEWT_KEY_DOWN);
        newtFormAddHotKey(self->form, NEWT_KEY_PGUP);
        newtFormAddHotKey(self->form, NEWT_KEY_PGDN);
+       newtFormAddHotKey(self->form, ' ');
        newtFormAddHotKey(self->form, NEWT_KEY_HOME);
        newtFormAddHotKey(self->form, NEWT_KEY_END);
+       newtFormAddHotKey(self->form, NEWT_KEY_TAB);
+       newtFormAddHotKey(self->form, NEWT_KEY_RIGHT);
 
        if (ui_browser__refresh_entries(self) < 0)
                return -1;
@@ -389,6 +409,8 @@ static int ui_browser__run(struct ui_browser *self, const char *title,
 
                if (es->reason != NEWT_EXIT_HOTKEY)
                        break;
+               if (is_exit_key(es->u.key))
+                       return es->u.key;
                switch (es->u.key) {
                case NEWT_KEY_DOWN:
                        if (self->index == self->nr_entries - 1)
@@ -411,6 +433,7 @@ static int ui_browser__run(struct ui_browser *self, const char *title,
                        }
                        break;
                case NEWT_KEY_PGDN:
+               case ' ':
                        if (self->first_visible_entry_idx + self->height > self->nr_entries - 1)
                                break;
 
@@ -461,12 +484,10 @@ static int ui_browser__run(struct ui_browser *self, const char *title,
                        }
                }
                        break;
-               case NEWT_KEY_ESCAPE:
+               case NEWT_KEY_RIGHT:
                case NEWT_KEY_LEFT:
-               case CTRL('c'):
-               case 'Q':
-               case 'q':
-                       return 0;
+               case NEWT_KEY_TAB:
+                       return es->u.key;
                default:
                        continue;
                }
@@ -658,18 +679,24 @@ static size_t hist_entry__append_browser(struct hist_entry *self,
        return ret;
 }
 
-static void hist_entry__annotate_browser(struct hist_entry *self)
+int hist_entry__tui_annotate(struct hist_entry *self)
 {
        struct ui_browser browser;
        struct newtExitStruct es;
        struct objdump_line *pos, *n;
        LIST_HEAD(head);
+       int ret;
 
        if (self->ms.sym == NULL)
-               return;
+               return -1;
 
-       if (hist_entry__annotate(self, &head) < 0)
-               return;
+       if (self->ms.map->dso->annotate_warned)
+               return -1;
+
+       if (hist_entry__annotate(self, &head) < 0) {
+               ui__error_window(browser__last_msg);
+               return -1;
+       }
 
        ui_helpline__push("Press <- or ESC to exit");
 
@@ -684,7 +711,7 @@ static void hist_entry__annotate_browser(struct hist_entry *self)
        }
 
        browser.width += 18; /* Percentage */
-       ui_browser__run(&browser, self->ms.sym->name, &es);
+       ret = ui_browser__run(&browser, self->ms.sym->name, &es);
        newtFormDestroy(browser.form);
        newtPopWindow();
        list_for_each_entry_safe(pos, n, &head, node) {
@@ -692,6 +719,7 @@ static void hist_entry__annotate_browser(struct hist_entry *self)
                objdump_line__free(pos);
        }
        ui_helpline__pop();
+       return ret;
 }
 
 static const void *newt__symbol_tree_get_current(newtComponent self)
@@ -814,6 +842,8 @@ static int hist_browser__populate(struct hist_browser *self, struct hists *hists
        newtFormAddHotKey(self->form, 'h');
        newtFormAddHotKey(self->form, NEWT_KEY_F1);
        newtFormAddHotKey(self->form, NEWT_KEY_RIGHT);
+       newtFormAddHotKey(self->form, NEWT_KEY_TAB);
+       newtFormAddHotKey(self->form, NEWT_KEY_UNTAB);
        newtFormAddComponents(self->form, self->tree, NULL);
        self->selection = newt__symbol_tree_get_current(self->tree);
 
@@ -845,7 +875,7 @@ static struct thread *hist_browser__selected_thread(struct hist_browser *self)
        return he ? he->thread : NULL;
 }
 
-static int hist_browser__title(char *bf, size_t size, const char *input_name,
+static int hist_browser__title(char *bf, size_t size, const char *ev_name,
                               const struct dso *dso, const struct thread *thread)
 {
        int printed = 0;
@@ -859,18 +889,18 @@ static int hist_browser__title(char *bf, size_t size, const char *input_name,
                printed += snprintf(bf + printed, size - printed,
                                    "%sDSO: %s", thread ? " " : "",
                                    dso->short_name);
-       return printed ?: snprintf(bf, size, "Report: %s", input_name);
+       return printed ?: snprintf(bf, size, "Event: %s", ev_name);
 }
 
-int hists__browse(struct hists *self, const char *helpline, const char *input_name)
+int hists__browse(struct hists *self, const char *helpline, const char *ev_name)
 {
        struct hist_browser *browser = hist_browser__new();
-       struct pstack *fstack = pstack__new(2);
+       struct pstack *fstack;
        const struct thread *thread_filter = NULL;
        const struct dso *dso_filter = NULL;
        struct newtExitStruct es;
        char msg[160];
-       int err = -1;
+       int key = -1;
 
        if (browser == NULL)
                return -1;
@@ -881,7 +911,7 @@ int hists__browse(struct hists *self, const char *helpline, const char *input_na
 
        ui_helpline__push(helpline);
 
-       hist_browser__title(msg, sizeof(msg), input_name,
+       hist_browser__title(msg, sizeof(msg), ev_name,
                            dso_filter, thread_filter);
        if (hist_browser__populate(browser, self, msg) < 0)
                goto out_free_stack;
@@ -899,11 +929,27 @@ int hists__browse(struct hists *self, const char *helpline, const char *input_na
                dso = browser->selection->map ? browser->selection->map->dso : NULL;
 
                if (es.reason == NEWT_EXIT_HOTKEY) {
-                       if (es.u.key == NEWT_KEY_F1)
+                       key = es.u.key;
+
+                       switch (key) {
+                       case NEWT_KEY_F1:
                                goto do_help;
+                       case NEWT_KEY_TAB:
+                       case NEWT_KEY_UNTAB:
+                               /*
+                                * Exit the browser, let hists__browser_tree
+                                * go to the next or previous
+                                */
+                               goto out_free_stack;
+                       default:;
+                       }
 
-                       switch (toupper(es.u.key)) {
+                       key = toupper(key);
+                       switch (key) {
                        case 'A':
+                               if (browser->selection->map == NULL &&
+                                   browser->selection->map->dso->annotate_warned)
+                                       continue;
                                goto do_annotate;
                        case 'D':
                                goto zoom_dso;
@@ -922,14 +968,14 @@ do_help:
                                continue;
                        default:;
                        }
-                       if (toupper(es.u.key) == 'Q' ||
-                           es.u.key == CTRL('c'))
-                               break;
-                       if (es.u.key == NEWT_KEY_ESCAPE) {
-                               if (dialog_yesno("Do you really want to exit?"))
+                       if (is_exit_key(key)) {
+                               if (key == NEWT_KEY_ESCAPE) {
+                                       if (dialog_yesno("Do you really want to exit?"))
+                                               break;
+                                       else
+                                               continue;
+                               } else
                                        break;
-                               else
-                                       continue;
                        }
 
                        if (es.u.key == NEWT_KEY_LEFT) {
@@ -947,6 +993,7 @@ do_help:
                }
 
                if (browser->selection->sym != NULL &&
+                   !browser->selection->map->dso->annotate_warned &&
                    asprintf(&options[nr_options], "Annotate %s",
                             browser->selection->sym->name) > 0)
                        annotate = nr_options++;
@@ -981,6 +1028,7 @@ do_help:
                        struct hist_entry *he;
 do_annotate:
                        if (browser->selection->map->dso->origin == DSO__ORIG_KERNEL) {
+                               browser->selection->map->dso->annotate_warned = 1;
                                ui_helpline__puts("No vmlinux file found, can't "
                                                 "annotate with just a "
                                                 "kallsyms file");
@@ -991,7 +1039,7 @@ do_annotate:
                        if (he == NULL)
                                continue;
 
-                       hist_entry__annotate_browser(he);
+                       hist_entry__tui_annotate(he);
                } else if (choice == zoom_dso) {
 zoom_dso:
                        if (dso_filter) {
@@ -1008,7 +1056,7 @@ zoom_out_dso:
                                pstack__push(fstack, &dso_filter);
                        }
                        hists__filter_by_dso(self, dso_filter);
-                       hist_browser__title(msg, sizeof(msg), input_name,
+                       hist_browser__title(msg, sizeof(msg), ev_name,
                                            dso_filter, thread_filter);
                        if (hist_browser__populate(browser, self, msg) < 0)
                                goto out;
@@ -1027,18 +1075,49 @@ zoom_out_thread:
                                pstack__push(fstack, &thread_filter);
                        }
                        hists__filter_by_thread(self, thread_filter);
-                       hist_browser__title(msg, sizeof(msg), input_name,
+                       hist_browser__title(msg, sizeof(msg), ev_name,
                                            dso_filter, thread_filter);
                        if (hist_browser__populate(browser, self, msg) < 0)
                                goto out;
                }
        }
-       err = 0;
 out_free_stack:
        pstack__delete(fstack);
 out:
        hist_browser__delete(browser);
-       return err;
+       return key;
+}
+
+int hists__tui_browse_tree(struct rb_root *self, const char *help)
+{
+       struct rb_node *first = rb_first(self), *nd = first, *next;
+       int key = 0;
+
+       while (nd) {
+               struct hists *hists = rb_entry(nd, struct hists, rb_node);
+               const char *ev_name = __event_name(hists->type, hists->config);
+
+               key = hists__browse(hists, help, ev_name);
+
+               if (is_exit_key(key))
+                       break;
+
+               switch (key) {
+               case NEWT_KEY_TAB:
+                       next = rb_next(nd);
+                       if (next)
+                               nd = next;
+                       break;
+               case NEWT_KEY_UNTAB:
+                       if (nd == first)
+                               continue;
+                       nd = rb_prev(nd);
+               default:
+                       break;
+               }
+       }
+
+       return key;
 }
 
 static struct newtPercentTreeColors {
@@ -1058,10 +1137,13 @@ static struct newtPercentTreeColors {
 void setup_browser(void)
 {
        struct newtPercentTreeColors *c = &defaultPercentTreeColors;
-       if (!isatty(1))
+
+       if (!isatty(1) || !use_browser || dump_trace) {
+               setup_pager();
                return;
+       }
 
-       use_browser = true;
+       use_browser = 1;
        newtInit();
        newtCls();
        ui_helpline__puts(" ");
@@ -1074,7 +1156,7 @@ void setup_browser(void)
 
 void exit_browser(bool wait_for_ok)
 {
-       if (use_browser) {
+       if (use_browser > 0) {
                if (wait_for_ok) {
                        char title[] = "Fatal Error", ok[] = "Ok";
                        newtWinMessage(title, ok, browser__last_msg);
index fd1f2fa..58a470d 100644 (file)
@@ -54,21 +54,6 @@ static char *cleanup_path(char *path)
        return path;
 }
 
-char *mksnpath(char *buf, size_t n, const char *fmt, ...)
-{
-       va_list args;
-       unsigned len;
-
-       va_start(args, fmt);
-       len = vsnprintf(buf, n, fmt, args);
-       va_end(args);
-       if (len >= n) {
-               strlcpy(buf, bad_path, n);
-               return buf;
-       }
-       return cleanup_path(buf);
-}
-
 static char *perf_vsnpath(char *buf, size_t n, const char *fmt, va_list args)
 {
        const char *perf_dir = get_perf_dir();
@@ -89,15 +74,6 @@ bad:
        return buf;
 }
 
-char *perf_snpath(char *buf, size_t n, const char *fmt, ...)
-{
-       va_list args;
-       va_start(args, fmt);
-       (void)perf_vsnpath(buf, n, fmt, args);
-       va_end(args);
-       return buf;
-}
-
 char *perf_pathdup(const char *fmt, ...)
 {
        char path[PATH_MAX];
@@ -143,184 +119,6 @@ char *perf_path(const char *fmt, ...)
        return cleanup_path(pathname);
 }
 
-
-/* perf_mkstemp() - create tmp file honoring TMPDIR variable */
-int perf_mkstemp(char *path, size_t len, const char *template)
-{
-       const char *tmp;
-       size_t n;
-
-       tmp = getenv("TMPDIR");
-       if (!tmp)
-               tmp = "/tmp";
-       n = snprintf(path, len, "%s/%s", tmp, template);
-       if (len <= n) {
-               errno = ENAMETOOLONG;
-               return -1;
-       }
-       return mkstemp(path);
-}
-
-
-const char *make_relative_path(const char *abs_path, const char *base)
-{
-       static char buf[PATH_MAX + 1];
-       int baselen;
-
-       if (!base)
-               return abs_path;
-
-       baselen = strlen(base);
-       if (prefixcmp(abs_path, base))
-               return abs_path;
-       if (abs_path[baselen] == '/')
-               baselen++;
-       else if (base[baselen - 1] != '/')
-               return abs_path;
-
-       strcpy(buf, abs_path + baselen);
-
-       return buf;
-}
-
-/*
- * It is okay if dst == src, but they should not overlap otherwise.
- *
- * Performs the following normalizations on src, storing the result in dst:
- * - Ensures that components are separated by '/' (Windows only)
- * - Squashes sequences of '/'.
- * - Removes "." components.
- * - Removes ".." components, and the components the precede them.
- * Returns failure (non-zero) if a ".." component appears as first path
- * component anytime during the normalization. Otherwise, returns success (0).
- *
- * Note that this function is purely textual.  It does not follow symlinks,
- * verify the existence of the path, or make any system calls.
- */
-int normalize_path_copy(char *dst, const char *src)
-{
-       char *dst0;
-
-       if (has_dos_drive_prefix(src)) {
-               *dst++ = *src++;
-               *dst++ = *src++;
-       }
-       dst0 = dst;
-
-       if (is_dir_sep(*src)) {
-               *dst++ = '/';
-               while (is_dir_sep(*src))
-                       src++;
-       }
-
-       for (;;) {
-               char c = *src;
-
-               /*
-                * A path component that begins with . could be
-                * special:
-                * (1) "." and ends   -- ignore and terminate.
-                * (2) "./"           -- ignore them, eat slash and continue.
-                * (3) ".." and ends  -- strip one and terminate.
-                * (4) "../"          -- strip one, eat slash and continue.
-                */
-               if (c == '.') {
-                       if (!src[1]) {
-                               /* (1) */
-                               src++;
-                       } else if (is_dir_sep(src[1])) {
-                               /* (2) */
-                               src += 2;
-                               while (is_dir_sep(*src))
-                                       src++;
-                               continue;
-                       } else if (src[1] == '.') {
-                               if (!src[2]) {
-                                       /* (3) */
-                                       src += 2;
-                                       goto up_one;
-                               } else if (is_dir_sep(src[2])) {
-                                       /* (4) */
-                                       src += 3;
-                                       while (is_dir_sep(*src))
-                                               src++;
-                                       goto up_one;
-                               }
-                       }
-               }
-
-               /* copy up to the next '/', and eat all '/' */
-               while ((c = *src++) != '\0' && !is_dir_sep(c))
-                       *dst++ = c;
-               if (is_dir_sep(c)) {
-                       *dst++ = '/';
-                       while (is_dir_sep(c))
-                               c = *src++;
-                       src--;
-               } else if (!c)
-                       break;
-               continue;
-
-       up_one:
-               /*
-                * dst0..dst is prefix portion, and dst[-1] is '/';
-                * go up one level.
-                */
-               dst--;  /* go to trailing '/' */
-               if (dst <= dst0)
-                       return -1;
-               /* Windows: dst[-1] cannot be backslash anymore */
-               while (dst0 < dst && dst[-1] != '/')
-                       dst--;
-       }
-       *dst = '\0';
-       return 0;
-}
-
-/*
- * path = Canonical absolute path
- * prefix_list = Colon-separated list of absolute paths
- *
- * Determines, for each path in prefix_list, whether the "prefix" really
- * is an ancestor directory of path.  Returns the length of the longest
- * ancestor directory, excluding any trailing slashes, or -1 if no prefix
- * is an ancestor.  (Note that this means 0 is returned if prefix_list is
- * "/".) "/foo" is not considered an ancestor of "/foobar".  Directories
- * are not considered to be their own ancestors.  path must be in a
- * canonical form: empty components, or "." or ".." components are not
- * allowed.  prefix_list may be null, which is like "".
- */
-int longest_ancestor_length(const char *path, const char *prefix_list)
-{
-       char buf[PATH_MAX+1];
-       const char *ceil, *colon;
-       int len, max_len = -1;
-
-       if (prefix_list == NULL || !strcmp(path, "/"))
-               return -1;
-
-       for (colon = ceil = prefix_list; *colon; ceil = colon+1) {
-               for (colon = ceil; *colon && *colon != PATH_SEP; colon++);
-               len = colon - ceil;
-               if (len == 0 || len > PATH_MAX || !is_absolute_path(ceil))
-                       continue;
-               strlcpy(buf, ceil, len+1);
-               if (normalize_path_copy(buf, buf) < 0)
-                       continue;
-               len = strlen(buf);
-               if (len > 0 && buf[len-1] == '/')
-                       buf[--len] = '\0';
-
-               if (!strncmp(path, buf, len) &&
-                   path[len] == '/' &&
-                   len > max_len) {
-                       max_len = len;
-               }
-       }
-
-       return max_len;
-}
-
 /* strip arbitrary amount of directory separators at end of path */
 static inline int chomp_trailing_dir_sep(const char *path, int len)
 {
@@ -354,5 +152,5 @@ char *strip_path_suffix(const char *path, const char *suffix)
 
        if (path_len && !is_dir_sep(path[path_len - 1]))
                return NULL;
-       return xstrndup(path, chomp_trailing_dir_sep(path, path_len));
+       return strndup(path, chomp_trailing_dir_sep(path, path_len));
 }
index 562b144..d964cb1 100644 (file)
@@ -668,6 +668,7 @@ static int convert_probe_point(Dwarf_Die *sp_die, struct probe_finder *pf)
        ret = dwarf_getlocation_addr(&fb_attr, pf->addr, &pf->fb_ops, &nops, 1);
        if (ret <= 0 || nops == 0) {
                pf->fb_ops = NULL;
+#if _ELFUTILS_PREREQ(0, 142)
        } else if (nops == 1 && pf->fb_ops[0].atom == DW_OP_call_frame_cfa &&
                   pf->cfi != NULL) {
                Dwarf_Frame *frame;
@@ -677,6 +678,7 @@ static int convert_probe_point(Dwarf_Die *sp_die, struct probe_finder *pf)
                                   (uintmax_t)pf->addr);
                        return -ENOENT;
                }
+#endif
        }
 
        /* Find each argument */
@@ -741,32 +743,36 @@ static int find_lazy_match_lines(struct list_head *head,
                                 const char *fname, const char *pat)
 {
        char *fbuf, *p1, *p2;
-       int fd, ret, line, nlines = 0;
+       int fd, line, nlines = -1;
        struct stat st;
 
        fd = open(fname, O_RDONLY);
        if (fd < 0) {
                pr_warning("Failed to open %s: %s\n", fname, strerror(-fd));
-               return fd;
+               return -errno;
        }
 
-       ret = fstat(fd, &st);
-       if (ret < 0) {
+       if (fstat(fd, &st) < 0) {
                pr_warning("Failed to get the size of %s: %s\n",
                           fname, strerror(errno));
-               return ret;
+               nlines = -errno;
+               goto out_close;
        }
-       fbuf = xmalloc(st.st_size + 2);
-       ret = read(fd, fbuf, st.st_size);
-       if (ret < 0) {
+
+       nlines = -ENOMEM;
+       fbuf = malloc(st.st_size + 2);
+       if (fbuf == NULL)
+               goto out_close;
+       if (read(fd, fbuf, st.st_size) < 0) {
                pr_warning("Failed to read %s: %s\n", fname, strerror(errno));
-               return ret;
+               nlines = -errno;
+               goto out_free_fbuf;
        }
-       close(fd);
        fbuf[st.st_size] = '\n';        /* Dummy line */
        fbuf[st.st_size + 1] = '\0';
        p1 = fbuf;
        line = 1;
+       nlines = 0;
        while ((p2 = strchr(p1, '\n')) != NULL) {
                *p2 = '\0';
                if (strlazymatch(p1, pat)) {
@@ -776,7 +782,10 @@ static int find_lazy_match_lines(struct list_head *head,
                line++;
                p1 = p2 + 1;
        }
+out_free_fbuf:
        free(fbuf);
+out_close:
+       close(fd);
        return nlines;
 }
 
@@ -953,11 +962,15 @@ int find_kprobe_trace_events(int fd, struct perf_probe_event *pev,
        if (!dbg) {
                pr_warning("No dwarf info found in the vmlinux - "
                        "please rebuild with CONFIG_DEBUG_INFO=y.\n");
+               free(pf.tevs);
+               *tevs = NULL;
                return -EBADF;
        }
 
+#if _ELFUTILS_PREREQ(0, 142)
        /* Get the call frame information from this dwarf */
        pf.cfi = dwarf_getcfi(dbg);
+#endif
 
        off = 0;
        line_list__init(&pf.lcache);
index 66f1980..e1f61dc 100644 (file)
@@ -29,6 +29,7 @@ extern int find_line_range(int fd, struct line_range *lr);
 
 #include <dwarf.h>
 #include <libdw.h>
+#include <version.h>
 
 struct probe_finder {
        struct perf_probe_event *pev;           /* Target probe event */
@@ -44,7 +45,9 @@ struct probe_finder {
        struct list_head        lcache;         /* Line cache for lazy match */
 
        /* For variable searching */
+#if _ELFUTILS_PREREQ(0, 142)
        Dwarf_CFI               *cfi;           /* Call Frame Information */
+#endif
        Dwarf_Op                *fb_ops;        /* Frame base attribute */
        struct perf_probe_arg   *pvar;          /* Current target variable */
        struct kprobe_trace_arg *tvar;          /* Current result variable */
index 2726fe4..01f0324 100644 (file)
@@ -1,8 +1,6 @@
 #include "cache.h"
 #include "quote.h"
 
-int quote_path_fully = 1;
-
 /* Help to copy the thing properly quoted for the shell safety.
  * any single quote is replaced with '\'', any exclamation point
  * is replaced with '\!', and the whole thing is enclosed in a
@@ -19,7 +17,7 @@ static inline int need_bs_quote(char c)
        return (c == '\'' || c == '!');
 }
 
-void sq_quote_buf(struct strbuf *dst, const char *src)
+static void sq_quote_buf(struct strbuf *dst, const char *src)
 {
        char *to_free = NULL;
 
@@ -41,23 +39,6 @@ void sq_quote_buf(struct strbuf *dst, const char *src)
        free(to_free);
 }
 
-void sq_quote_print(FILE *stream, const char *src)
-{
-       char c;
-
-       fputc('\'', stream);
-       while ((c = *src++)) {
-               if (need_bs_quote(c)) {
-                       fputs("'\\", stream);
-                       fputc(c, stream);
-                       fputc('\'', stream);
-               } else {
-                       fputc(c, stream);
-               }
-       }
-       fputc('\'', stream);
-}
-
 void sq_quote_argv(struct strbuf *dst, const char** argv, size_t maxlen)
 {
        int i;
@@ -71,415 +52,3 @@ void sq_quote_argv(struct strbuf *dst, const char** argv, size_t maxlen)
                        die("Too many or long arguments");
        }
 }
-
-char *sq_dequote_step(char *arg, char **next)
-{
-       char *dst = arg;
-       char *src = arg;
-       char c;
-
-       if (*src != '\'')
-               return NULL;
-       for (;;) {
-               c = *++src;
-               if (!c)
-                       return NULL;
-               if (c != '\'') {
-                       *dst++ = c;
-                       continue;
-               }
-               /* We stepped out of sq */
-               switch (*++src) {
-               case '\0':
-                       *dst = 0;
-                       if (next)
-                               *next = NULL;
-                       return arg;
-               case '\\':
-                       c = *++src;
-                       if (need_bs_quote(c) && *++src == '\'') {
-                               *dst++ = c;
-                               continue;
-                       }
-               /* Fallthrough */
-               default:
-                       if (!next || !isspace(*src))
-                               return NULL;
-                       do {
-                               c = *++src;
-                       } while (isspace(c));
-                       *dst = 0;
-                       *next = src;
-                       return arg;
-               }
-       }
-}
-
-char *sq_dequote(char *arg)
-{
-       return sq_dequote_step(arg, NULL);
-}
-
-int sq_dequote_to_argv(char *arg, const char ***argv, int *nr, int *alloc)
-{
-       char *next = arg;
-
-       if (!*arg)
-               return 0;
-       do {
-               char *dequoted = sq_dequote_step(next, &next);
-               if (!dequoted)
-                       return -1;
-               ALLOC_GROW(*argv, *nr + 1, *alloc);
-               (*argv)[(*nr)++] = dequoted;
-       } while (next);
-
-       return 0;
-}
-
-/* 1 means: quote as octal
- * 0 means: quote as octal if (quote_path_fully)
- * -1 means: never quote
- * c: quote as "\\c"
- */
-#define X8(x)   x, x, x, x, x, x, x, x
-#define X16(x)  X8(x), X8(x)
-static signed char const sq_lookup[256] = {
-       /*           0    1    2    3    4    5    6    7 */
-       /* 0x00 */   1,   1,   1,   1,   1,   1,   1, 'a',
-       /* 0x08 */ 'b', 't', 'n', 'v', 'f', 'r',   1,   1,
-       /* 0x10 */ X16(1),
-       /* 0x20 */  -1,  -1, '"',  -1,  -1,  -1,  -1,  -1,
-       /* 0x28 */ X16(-1), X16(-1), X16(-1),
-       /* 0x58 */  -1,  -1,  -1,  -1,'\\',  -1,  -1,  -1,
-       /* 0x60 */ X16(-1), X8(-1),
-       /* 0x78 */  -1,  -1,  -1,  -1,  -1,  -1,  -1,   1,
-       /* 0x80 */ /* set to 0 */
-};
-
-static inline int sq_must_quote(char c)
-{
-       return sq_lookup[(unsigned char)c] + quote_path_fully > 0;
-}
-
-/*
- * Returns the longest prefix not needing a quote up to maxlen if
- * positive.
- * This stops at the first \0 because it's marked as a character
- * needing an escape.
- */
-static ssize_t next_quote_pos(const char *s, ssize_t maxlen)
-{
-       ssize_t len;
-
-       if (maxlen < 0) {
-               for (len = 0; !sq_must_quote(s[len]); len++);
-       } else {
-               for (len = 0; len < maxlen && !sq_must_quote(s[len]); len++);
-       }
-       return len;
-}
-
-/*
- * C-style name quoting.
- *
- * (1) if sb and fp are both NULL, inspect the input name and counts the
- *     number of bytes that are needed to hold c_style quoted version of name,
- *     counting the double quotes around it but not terminating NUL, and
- *     returns it.
- *     However, if name does not need c_style quoting, it returns 0.
- *
- * (2) if sb or fp are not NULL, it emits the c_style quoted version
- *     of name, enclosed with double quotes if asked and needed only.
- *     Return value is the same as in (1).
- */
-static size_t quote_c_style_counted(const char *name, ssize_t maxlen,
-                                    struct strbuf *sb, FILE *fp, int no_dq)
-{
-#define EMIT(c)                                                        \
-       do {                                                    \
-               if (sb) strbuf_addch(sb, (c));                  \
-               if (fp) fputc((c), fp);                         \
-               count++;                                        \
-       } while (0)
-
-#define EMITBUF(s, l)                                          \
-       do {                                                    \
-               int __ret;                                      \
-               if (sb) strbuf_add(sb, (s), (l));               \
-               if (fp) __ret = fwrite((s), (l), 1, fp);        \
-               count += (l);                                   \
-       } while (0)
-
-       ssize_t len, count = 0;
-       const char *p = name;
-
-       for (;;) {
-               int ch;
-
-               len = next_quote_pos(p, maxlen);
-               if (len == maxlen || !p[len])
-                       break;
-
-               if (!no_dq && p == name)
-                       EMIT('"');
-
-               EMITBUF(p, len);
-               EMIT('\\');
-               p += len;
-               ch = (unsigned char)*p++;
-               if (sq_lookup[ch] >= ' ') {
-                       EMIT(sq_lookup[ch]);
-               } else {
-                       EMIT(((ch >> 6) & 03) + '0');
-                       EMIT(((ch >> 3) & 07) + '0');
-                       EMIT(((ch >> 0) & 07) + '0');
-               }
-       }
-
-       EMITBUF(p, len);
-       if (p == name)   /* no ending quote needed */
-               return 0;
-
-       if (!no_dq)
-               EMIT('"');
-       return count;
-}
-
-size_t quote_c_style(const char *name, struct strbuf *sb, FILE *fp, int nodq)
-{
-       return quote_c_style_counted(name, -1, sb, fp, nodq);
-}
-
-void quote_two_c_style(struct strbuf *sb, const char *prefix, const char *path, int nodq)
-{
-       if (quote_c_style(prefix, NULL, NULL, 0) ||
-           quote_c_style(path, NULL, NULL, 0)) {
-               if (!nodq)
-                       strbuf_addch(sb, '"');
-               quote_c_style(prefix, sb, NULL, 1);
-               quote_c_style(path, sb, NULL, 1);
-               if (!nodq)
-                       strbuf_addch(sb, '"');
-       } else {
-               strbuf_addstr(sb, prefix);
-               strbuf_addstr(sb, path);
-       }
-}
-
-void write_name_quoted(const char *name, FILE *fp, int terminator)
-{
-       if (terminator) {
-               quote_c_style(name, NULL, fp, 0);
-       } else {
-               fputs(name, fp);
-       }
-       fputc(terminator, fp);
-}
-
-void write_name_quotedpfx(const char *pfx, ssize_t pfxlen,
-                         const char *name, FILE *fp, int terminator)
-{
-       int needquote = 0;
-
-       if (terminator) {
-               needquote = next_quote_pos(pfx, pfxlen) < pfxlen
-                       || name[next_quote_pos(name, -1)];
-       }
-       if (needquote) {
-               fputc('"', fp);
-               quote_c_style_counted(pfx, pfxlen, NULL, fp, 1);
-               quote_c_style(name, NULL, fp, 1);
-               fputc('"', fp);
-       } else {
-               int ret;
-
-               ret = fwrite(pfx, pfxlen, 1, fp);
-               fputs(name, fp);
-       }
-       fputc(terminator, fp);
-}
-
-/* quote path as relative to the given prefix */
-char *quote_path_relative(const char *in, int len,
-                         struct strbuf *out, const char *prefix)
-{
-       int needquote;
-
-       if (len < 0)
-               len = strlen(in);
-
-       /* "../" prefix itself does not need quoting, but "in" might. */
-       needquote = (next_quote_pos(in, len) < len);
-       strbuf_setlen(out, 0);
-       strbuf_grow(out, len);
-
-       if (needquote)
-               strbuf_addch(out, '"');
-       if (prefix) {
-               int off = 0;
-               while (off < len && prefix[off] && prefix[off] == in[off])
-                       if (prefix[off] == '/') {
-                               prefix += off + 1;
-                               in += off + 1;
-                               len -= off + 1;
-                               off = 0;
-                       } else
-                               off++;
-
-               for (; *prefix; prefix++)
-                       if (*prefix == '/')
-                               strbuf_addstr(out, "../");
-       }
-
-       quote_c_style_counted (in, len, out, NULL, 1);
-
-       if (needquote)
-               strbuf_addch(out, '"');
-       if (!out->len)
-               strbuf_addstr(out, "./");
-
-       return out->buf;
-}
-
-/*
- * C-style name unquoting.
- *
- * Quoted should point at the opening double quote.
- * + Returns 0 if it was able to unquote the string properly, and appends the
- *   result in the strbuf `sb'.
- * + Returns -1 in case of error, and doesn't touch the strbuf. Though note
- *   that this function will allocate memory in the strbuf, so calling
- *   strbuf_release is mandatory whichever result unquote_c_style returns.
- *
- * Updates endp pointer to point at one past the ending double quote if given.
- */
-int unquote_c_style(struct strbuf *sb, const char *quoted, const char **endp)
-{
-       size_t oldlen = sb->len, len;
-       int ch, ac;
-
-       if (*quoted++ != '"')
-               return -1;
-
-       for (;;) {
-               len = strcspn(quoted, "\"\\");
-               strbuf_add(sb, quoted, len);
-               quoted += len;
-
-               switch (*quoted++) {
-                 case '"':
-                       if (endp)
-                               *endp = quoted;
-                       return 0;
-                 case '\\':
-                       break;
-                 default:
-                       goto error;
-               }
-
-               switch ((ch = *quoted++)) {
-               case 'a': ch = '\a'; break;
-               case 'b': ch = '\b'; break;
-               case 'f': ch = '\f'; break;
-               case 'n': ch = '\n'; break;
-               case 'r': ch = '\r'; break;
-               case 't': ch = '\t'; break;
-               case 'v': ch = '\v'; break;
-
-               case '\\': case '"':
-                       break; /* verbatim */
-
-               /* octal values with first digit over 4 overflow */
-               case '0': case '1': case '2': case '3':
-                                       ac = ((ch - '0') << 6);
-                       if ((ch = *quoted++) < '0' || '7' < ch)
-                               goto error;
-                                       ac |= ((ch - '0') << 3);
-                       if ((ch = *quoted++) < '0' || '7' < ch)
-                               goto error;
-                                       ac |= (ch - '0');
-                                       ch = ac;
-                                       break;
-                               default:
-                       goto error;
-                       }
-               strbuf_addch(sb, ch);
-               }
-
-  error:
-       strbuf_setlen(sb, oldlen);
-       return -1;
-}
-
-/* quoting as a string literal for other languages */
-
-void perl_quote_print(FILE *stream, const char *src)
-{
-       const char sq = '\'';
-       const char bq = '\\';
-       char c;
-
-       fputc(sq, stream);
-       while ((c = *src++)) {
-               if (c == sq || c == bq)
-                       fputc(bq, stream);
-               fputc(c, stream);
-       }
-       fputc(sq, stream);
-}
-
-void python_quote_print(FILE *stream, const char *src)
-{
-       const char sq = '\'';
-       const char bq = '\\';
-       const char nl = '\n';
-       char c;
-
-       fputc(sq, stream);
-       while ((c = *src++)) {
-               if (c == nl) {
-                       fputc(bq, stream);
-                       fputc('n', stream);
-                       continue;
-               }
-               if (c == sq || c == bq)
-                       fputc(bq, stream);
-               fputc(c, stream);
-       }
-       fputc(sq, stream);
-}
-
-void tcl_quote_print(FILE *stream, const char *src)
-{
-       char c;
-
-       fputc('"', stream);
-       while ((c = *src++)) {
-               switch (c) {
-               case '[': case ']':
-               case '{': case '}':
-               case '$': case '\\': case '"':
-                       fputc('\\', stream);
-               default:
-                       fputc(c, stream);
-                       break;
-               case '\f':
-                       fputs("\\f", stream);
-                       break;
-               case '\r':
-                       fputs("\\r", stream);
-                       break;
-               case '\n':
-                       fputs("\\n", stream);
-                       break;
-               case '\t':
-                       fputs("\\t", stream);
-                       break;
-               case '\v':
-                       fputs("\\v", stream);
-                       break;
-               }
-       }
-       fputc('"', stream);
-}
index b6a0197..172889e 100644 (file)
  *
  * Note that the above examples leak memory!  Remember to free result from
  * sq_quote() in a real application.
- *
- * sq_quote_buf() writes to an existing buffer of specified size; it
- * will return the number of characters that would have been written
- * excluding the final null regardless of the buffer size.
  */
 
-extern void sq_quote_print(FILE *stream, const char *src);
-
-extern void sq_quote_buf(struct strbuf *, const char *src);
 extern void sq_quote_argv(struct strbuf *, const char **argv, size_t maxlen);
 
-/* This unwraps what sq_quote() produces in place, but returns
- * NULL if the input does not look like what sq_quote would have
- * produced.
- */
-extern char *sq_dequote(char *);
-
-/*
- * Same as the above, but can be used to unwrap many arguments in the
- * same string separated by space. "next" is changed to point to the
- * next argument that should be passed as first parameter. When there
- * is no more argument to be dequoted, "next" is updated to point to NULL.
- */
-extern char *sq_dequote_step(char *arg, char **next);
-extern int sq_dequote_to_argv(char *arg, const char ***argv, int *nr, int *alloc);
-
-extern int unquote_c_style(struct strbuf *, const char *quoted, const char **endp);
-extern size_t quote_c_style(const char *name, struct strbuf *, FILE *, int no_dq);
-extern void quote_two_c_style(struct strbuf *, const char *, const char *, int);
-
-extern void write_name_quoted(const char *name, FILE *, int terminator);
-extern void write_name_quotedpfx(const char *pfx, ssize_t pfxlen,
-                                 const char *name, FILE *, int terminator);
-
-/* quote path as relative to the given prefix */
-char *quote_path_relative(const char *in, int len,
-                         struct strbuf *out, const char *prefix);
-
-/* quoting as a string literal for other languages */
-extern void perl_quote_print(FILE *stream, const char *src);
-extern void python_quote_print(FILE *stream, const char *src);
-extern void tcl_quote_print(FILE *stream, const char *src);
-
 #endif /* __PERF_QUOTE_H */
index 2b615ac..da8e9b2 100644 (file)
@@ -212,93 +212,3 @@ int run_command_v_opt(const char **argv, int opt)
        prepare_run_command_v_opt(&cmd, argv, opt);
        return run_command(&cmd);
 }
-
-int run_command_v_opt_cd_env(const char **argv, int opt, const char *dir, const char *const *env)
-{
-       struct child_process cmd;
-       prepare_run_command_v_opt(&cmd, argv, opt);
-       cmd.dir = dir;
-       cmd.env = env;
-       return run_command(&cmd);
-}
-
-int start_async(struct async *async)
-{
-       int pipe_out[2];
-
-       if (pipe(pipe_out) < 0)
-               return error("cannot create pipe: %s", strerror(errno));
-       async->out = pipe_out[0];
-
-       /* Flush stdio before fork() to avoid cloning buffers */
-       fflush(NULL);
-
-       async->pid = fork();
-       if (async->pid < 0) {
-               error("fork (async) failed: %s", strerror(errno));
-               close_pair(pipe_out);
-               return -1;
-       }
-       if (!async->pid) {
-               close(pipe_out[0]);
-               exit(!!async->proc(pipe_out[1], async->data));
-       }
-       close(pipe_out[1]);
-
-       return 0;
-}
-
-int finish_async(struct async *async)
-{
-       int ret = 0;
-
-       if (wait_or_whine(async->pid))
-               ret = error("waitpid (async) failed");
-
-       return ret;
-}
-
-int run_hook(const char *index_file, const char *name, ...)
-{
-       struct child_process hook;
-       const char **argv = NULL, *env[2];
-       char idx[PATH_MAX];
-       va_list args;
-       int ret;
-       size_t i = 0, alloc = 0;
-
-       if (access(perf_path("hooks/%s", name), X_OK) < 0)
-               return 0;
-
-       va_start(args, name);
-       ALLOC_GROW(argv, i + 1, alloc);
-       argv[i++] = perf_path("hooks/%s", name);
-       while (argv[i-1]) {
-               ALLOC_GROW(argv, i + 1, alloc);
-               argv[i++] = va_arg(args, const char *);
-       }
-       va_end(args);
-
-       memset(&hook, 0, sizeof(hook));
-       hook.argv = argv;
-       hook.no_stdin = 1;
-       hook.stdout_to_stderr = 1;
-       if (index_file) {
-               snprintf(idx, sizeof(idx), "PERF_INDEX_FILE=%s", index_file);
-               env[0] = idx;
-               env[1] = NULL;
-               hook.env = env;
-       }
-
-       ret = start_command(&hook);
-       free(argv);
-       if (ret) {
-               warning("Could not spawn %s", argv[0]);
-               return ret;
-       }
-       ret = finish_command(&hook);
-       if (ret == -ERR_RUN_COMMAND_WAITPID_SIGNAL)
-               warning("%s exited due to uncaught signal", argv[0]);
-
-       return ret;
-}
index d790287..1ef264d 100644 (file)
@@ -50,39 +50,9 @@ int start_command(struct child_process *);
 int finish_command(struct child_process *);
 int run_command(struct child_process *);
 
-extern int run_hook(const char *index_file, const char *name, ...);
-
 #define RUN_COMMAND_NO_STDIN 1
 #define RUN_PERF_CMD        2  /*If this is to be perf sub-command */
 #define RUN_COMMAND_STDOUT_TO_STDERR 4
 int run_command_v_opt(const char **argv, int opt);
 
-/*
- * env (the environment) is to be formatted like environ: "VAR=VALUE".
- * To unset an environment variable use just "VAR".
- */
-int run_command_v_opt_cd_env(const char **argv, int opt, const char *dir, const char *const *env);
-
-/*
- * The purpose of the following functions is to feed a pipe by running
- * a function asynchronously and providing output that the caller reads.
- *
- * It is expected that no synchronization and mutual exclusion between
- * the caller and the feed function is necessary so that the function
- * can run in a thread without interfering with the caller.
- */
-struct async {
-       /*
-        * proc writes to fd and closes it;
-        * returns 0 on success, non-zero on failure
-        */
-       int (*proc)(int fd, void *data);
-       void *data;
-       int out;        /* caller reads from here and closes it */
-       pid_t pid;
-};
-
-int start_async(struct async *async);
-int finish_async(struct async *async);
-
 #endif /* __PERF_RUN_COMMAND_H */
index 25bfca4..8f83a18 100644 (file)
@@ -5,6 +5,7 @@
 #include <byteswap.h>
 #include <unistd.h>
 #include <sys/types.h>
+#include <sys/mman.h>
 
 #include "session.h"
 #include "sort.h"
@@ -894,3 +895,10 @@ size_t perf_session__fprintf_dsos(struct perf_session *self, FILE *fp)
               __dsos__fprintf(&self->host_machine.user_dsos, fp) +
               machines__fprintf_dsos(&self->machines, fp);
 }
+
+size_t perf_session__fprintf_dsos_buildid(struct perf_session *self, FILE *fp,
+                                         bool with_hits)
+{
+       size_t ret = machine__fprintf_dsos_buildid(&self->host_machine, fp, with_hits);
+       return ret + machines__fprintf_dsos_buildid(&self->machines, fp, with_hits);
+}
index e7fce48..55c6881 100644 (file)
@@ -132,12 +132,8 @@ void perf_session__process_machines(struct perf_session *self,
 
 size_t perf_session__fprintf_dsos(struct perf_session *self, FILE *fp);
 
-static inline
-size_t perf_session__fprintf_dsos_buildid(struct perf_session *self, FILE *fp,
-                                         bool with_hits)
-{
-       return machines__fprintf_dsos_buildid(&self->machines, fp, with_hits);
-}
+size_t perf_session__fprintf_dsos_buildid(struct perf_session *self,
+                                         FILE *fp, bool with_hits);
 
 static inline
 size_t perf_session__fprintf_nr_events(struct perf_session *self, FILE *fp)
index 1118b99..ba785e9 100644 (file)
@@ -16,7 +16,7 @@ static void check_signum(int sig)
                die("BUG: signal out of range: %d", sig);
 }
 
-int sigchain_push(int sig, sigchain_fun f)
+static int sigchain_push(int sig, sigchain_fun f)
 {
        struct sigchain_signal *s = signals + sig;
        check_signum(sig);
index 1a53c11..959d64e 100644 (file)
@@ -3,7 +3,6 @@
 
 typedef void (*sigchain_fun)(int);
 
-int sigchain_push(int sig, sigchain_fun f);
 int sigchain_pop(int sig);
 
 void sigchain_push_common(sigchain_fun f);
index 5249d5a..92e0685 100644 (file)
@@ -41,16 +41,6 @@ char *strbuf_detach(struct strbuf *sb, size_t *sz)
        return res;
 }
 
-void strbuf_attach(struct strbuf *sb, void *buf, size_t len, size_t alloc)
-{
-       strbuf_release(sb);
-       sb->buf   = buf;
-       sb->len   = len;
-       sb->alloc = alloc;
-       strbuf_grow(sb, 0);
-       sb->buf[sb->len] = '\0';
-}
-
 void strbuf_grow(struct strbuf *sb, size_t extra)
 {
        if (sb->len + extra + 1 <= sb->len)
@@ -60,94 +50,7 @@ void strbuf_grow(struct strbuf *sb, size_t extra)
        ALLOC_GROW(sb->buf, sb->len + extra + 1, sb->alloc);
 }
 
-void strbuf_trim(struct strbuf *sb)
-{
-       char *b = sb->buf;
-       while (sb->len > 0 && isspace((unsigned char)sb->buf[sb->len - 1]))
-               sb->len--;
-       while (sb->len > 0 && isspace(*b)) {
-               b++;
-               sb->len--;
-       }
-       memmove(sb->buf, b, sb->len);
-       sb->buf[sb->len] = '\0';
-}
-void strbuf_rtrim(struct strbuf *sb)
-{
-       while (sb->len > 0 && isspace((unsigned char)sb->buf[sb->len - 1]))
-               sb->len--;
-       sb->buf[sb->len] = '\0';
-}
-
-void strbuf_ltrim(struct strbuf *sb)
-{
-       char *b = sb->buf;
-       while (sb->len > 0 && isspace(*b)) {
-               b++;
-               sb->len--;
-       }
-       memmove(sb->buf, b, sb->len);
-       sb->buf[sb->len] = '\0';
-}
-
-void strbuf_tolower(struct strbuf *sb)
-{
-       unsigned int i;
-
-       for (i = 0; i < sb->len; i++)
-               sb->buf[i] = tolower(sb->buf[i]);
-}
-
-struct strbuf **strbuf_split(const struct strbuf *sb, int delim)
-{
-       int alloc = 2, pos = 0;
-       char *n, *p;
-       struct strbuf **ret;
-       struct strbuf *t;
-
-       ret = calloc(alloc, sizeof(struct strbuf *));
-       p = n = sb->buf;
-       while (n < sb->buf + sb->len) {
-               int len;
-               n = memchr(n, delim, sb->len - (n - sb->buf));
-               if (pos + 1 >= alloc) {
-                       alloc = alloc * 2;
-                       ret = realloc(ret, sizeof(struct strbuf *) * alloc);
-               }
-               if (!n)
-                       n = sb->buf + sb->len - 1;
-               len = n - p + 1;
-               t = malloc(sizeof(struct strbuf));
-               strbuf_init(t, len);
-               strbuf_add(t, p, len);
-               ret[pos] = t;
-               ret[++pos] = NULL;
-               p = ++n;
-       }
-       return ret;
-}
-
-void strbuf_list_free(struct strbuf **sbs)
-{
-       struct strbuf **s = sbs;
-
-       while (*s) {
-               strbuf_release(*s);
-               free(*s++);
-       }
-       free(sbs);
-}
-
-int strbuf_cmp(const struct strbuf *a, const struct strbuf *b)
-{
-       int len = a->len < b->len ? a->len: b->len;
-       int cmp = memcmp(a->buf, b->buf, len);
-       if (cmp)
-               return cmp;
-       return a->len < b->len ? -1: a->len != b->len;
-}
-
-void strbuf_splice(struct strbuf *sb, size_t pos, size_t len,
+static void strbuf_splice(struct strbuf *sb, size_t pos, size_t len,
                                   const void *data, size_t dlen)
 {
        if (pos + len < pos)
@@ -166,11 +69,6 @@ void strbuf_splice(struct strbuf *sb, size_t pos, size_t len,
        strbuf_setlen(sb, sb->len + dlen - len);
 }
 
-void strbuf_insert(struct strbuf *sb, size_t pos, const void *data, size_t len)
-{
-       strbuf_splice(sb, pos, 0, data, len);
-}
-
 void strbuf_remove(struct strbuf *sb, size_t pos, size_t len)
 {
        strbuf_splice(sb, pos, len, NULL, 0);
@@ -183,13 +81,6 @@ void strbuf_add(struct strbuf *sb, const void *data, size_t len)
        strbuf_setlen(sb, sb->len + len);
 }
 
-void strbuf_adddup(struct strbuf *sb, size_t pos, size_t len)
-{
-       strbuf_grow(sb, len);
-       memcpy(sb->buf + sb->len, sb->buf + pos, len);
-       strbuf_setlen(sb, sb->len + len);
-}
-
 void strbuf_addf(struct strbuf *sb, const char *fmt, ...)
 {
        int len;
@@ -214,57 +105,6 @@ void strbuf_addf(struct strbuf *sb, const char *fmt, ...)
        strbuf_setlen(sb, sb->len + len);
 }
 
-void strbuf_expand(struct strbuf *sb, const char *format, expand_fn_t fn,
-                  void *context)
-{
-       for (;;) {
-               const char *percent;
-               size_t consumed;
-
-               percent = strchrnul(format, '%');
-               strbuf_add(sb, format, percent - format);
-               if (!*percent)
-                       break;
-               format = percent + 1;
-
-               consumed = fn(sb, format, context);
-               if (consumed)
-                       format += consumed;
-               else
-                       strbuf_addch(sb, '%');
-       }
-}
-
-size_t strbuf_expand_dict_cb(struct strbuf *sb, const char *placeholder,
-               void *context)
-{
-       struct strbuf_expand_dict_entry *e = context;
-       size_t len;
-
-       for (; e->placeholder && (len = strlen(e->placeholder)); e++) {
-               if (!strncmp(placeholder, e->placeholder, len)) {
-                       if (e->value)
-                               strbuf_addstr(sb, e->value);
-                       return len;
-               }
-       }
-       return 0;
-}
-
-size_t strbuf_fread(struct strbuf *sb, size_t size, FILE *f)
-{
-       size_t res;
-       size_t oldalloc = sb->alloc;
-
-       strbuf_grow(sb, size);
-       res = fread(sb->buf + sb->len, 1, size, f);
-       if (res > 0)
-               strbuf_setlen(sb, sb->len + res);
-       else if (oldalloc == 0)
-               strbuf_release(sb);
-       return res;
-}
-
 ssize_t strbuf_read(struct strbuf *sb, int fd, ssize_t hint)
 {
        size_t oldlen = sb->len;
@@ -291,70 +131,3 @@ ssize_t strbuf_read(struct strbuf *sb, int fd, ssize_t hint)
        sb->buf[sb->len] = '\0';
        return sb->len - oldlen;
 }
-
-#define STRBUF_MAXLINK (2*PATH_MAX)
-
-int strbuf_readlink(struct strbuf *sb, const char *path, ssize_t hint)
-{
-       size_t oldalloc = sb->alloc;
-
-       if (hint < 32)
-               hint = 32;
-
-       while (hint < STRBUF_MAXLINK) {
-               ssize_t len;
-
-               strbuf_grow(sb, hint);
-               len = readlink(path, sb->buf, hint);
-               if (len < 0) {
-                       if (errno != ERANGE)
-                               break;
-               } else if (len < hint) {
-                       strbuf_setlen(sb, len);
-                       return 0;
-               }
-
-               /* .. the buffer was too small - try again */
-               hint *= 2;
-       }
-       if (oldalloc == 0)
-               strbuf_release(sb);
-       return -1;
-}
-
-int strbuf_getline(struct strbuf *sb, FILE *fp, int term)
-{
-       int ch;
-
-       strbuf_grow(sb, 0);
-       if (feof(fp))
-               return EOF;
-
-       strbuf_reset(sb);
-       while ((ch = fgetc(fp)) != EOF) {
-               if (ch == term)
-                       break;
-               strbuf_grow(sb, 1);
-               sb->buf[sb->len++] = ch;
-       }
-       if (ch == EOF && sb->len == 0)
-               return EOF;
-
-       sb->buf[sb->len] = '\0';
-       return 0;
-}
-
-int strbuf_read_file(struct strbuf *sb, const char *path, ssize_t hint)
-{
-       int fd, len;
-
-       fd = open(path, O_RDONLY);
-       if (fd < 0)
-               return -1;
-       len = strbuf_read(sb, fd, hint);
-       close(fd);
-       if (len < 0)
-               return -1;
-
-       return len;
-}
index a3d121d..436ac31 100644 (file)
@@ -53,12 +53,6 @@ struct strbuf {
 extern void strbuf_init(struct strbuf *buf, ssize_t hint);
 extern void strbuf_release(struct strbuf *);
 extern char *strbuf_detach(struct strbuf *, size_t *);
-extern void strbuf_attach(struct strbuf *, void *, size_t, size_t);
-static inline void strbuf_swap(struct strbuf *a, struct strbuf *b) {
-       struct strbuf tmp = *a;
-       *a = *b;
-       *b = tmp;
-}
 
 /*----- strbuf size related -----*/
 static inline ssize_t strbuf_avail(const struct strbuf *sb) {
@@ -74,17 +68,6 @@ static inline void strbuf_setlen(struct strbuf *sb, size_t len) {
        sb->len = len;
        sb->buf[len] = '\0';
 }
-#define strbuf_reset(sb)  strbuf_setlen(sb, 0)
-
-/*----- content related -----*/
-extern void strbuf_trim(struct strbuf *);
-extern void strbuf_rtrim(struct strbuf *);
-extern void strbuf_ltrim(struct strbuf *);
-extern int strbuf_cmp(const struct strbuf *, const struct strbuf *);
-extern void strbuf_tolower(struct strbuf *);
-
-extern struct strbuf **strbuf_split(const struct strbuf *, int delim);
-extern void strbuf_list_free(struct strbuf **);
 
 /*----- add data in your buffer -----*/
 static inline void strbuf_addch(struct strbuf *sb, int c) {
@@ -93,45 +76,17 @@ static inline void strbuf_addch(struct strbuf *sb, int c) {
        sb->buf[sb->len] = '\0';
 }
 
-extern void strbuf_insert(struct strbuf *, size_t pos, const void *, size_t);
 extern void strbuf_remove(struct strbuf *, size_t pos, size_t len);
 
-/* splice pos..pos+len with given data */
-extern void strbuf_splice(struct strbuf *, size_t pos, size_t len,
-                          const void *, size_t);
-
 extern void strbuf_add(struct strbuf *, const void *, size_t);
 static inline void strbuf_addstr(struct strbuf *sb, const char *s) {
        strbuf_add(sb, s, strlen(s));
 }
-static inline void strbuf_addbuf(struct strbuf *sb, const struct strbuf *sb2) {
-       strbuf_add(sb, sb2->buf, sb2->len);
-}
-extern void strbuf_adddup(struct strbuf *sb, size_t pos, size_t len);
-
-typedef size_t (*expand_fn_t) (struct strbuf *sb, const char *placeholder, void *context);
-extern void strbuf_expand(struct strbuf *sb, const char *format, expand_fn_t fn, void *context);
-struct strbuf_expand_dict_entry {
-       const char *placeholder;
-       const char *value;
-};
-extern size_t strbuf_expand_dict_cb(struct strbuf *sb, const char *placeholder, void *context);
 
 __attribute__((format(printf,2,3)))
 extern void strbuf_addf(struct strbuf *sb, const char *fmt, ...);
 
-extern size_t strbuf_fread(struct strbuf *, size_t, FILE *);
 /* XXX: if read fails, any partial read is undone */
 extern ssize_t strbuf_read(struct strbuf *, int fd, ssize_t hint);
-extern int strbuf_read_file(struct strbuf *sb, const char *path, ssize_t hint);
-extern int strbuf_readlink(struct strbuf *sb, const char *path, ssize_t hint);
-
-extern int strbuf_getline(struct strbuf *, FILE *, int);
-
-extern void stripspace(struct strbuf *buf, int skip_comments);
-extern int launch_editor(const char *path, struct strbuf *buffer, const char *const *env);
-
-extern int strbuf_branchname(struct strbuf *sb, const char *name);
-extern int strbuf_check_branch_ref(struct strbuf *sb, const char *name);
 
 #endif /* __PERF_STRBUF_H */
index a06131f..aaa51ba 100644 (file)
@@ -11,6 +11,7 @@
 #include <sys/param.h>
 #include <fcntl.h>
 #include <unistd.h>
+#include "build-id.h"
 #include "symbol.h"
 #include "strlist.h"
 
@@ -1131,6 +1132,10 @@ bool __dsos__read_build_ids(struct list_head *head, bool with_hits)
        list_for_each_entry(pos, head, node) {
                if (with_hits && !pos->hit)
                        continue;
+               if (pos->has_build_id) {
+                       have_build_id = true;
+                       continue;
+               }
                if (filename__read_build_id(pos->long_name, pos->build_id,
                                            sizeof(pos->build_id)) > 0) {
                        have_build_id     = true;
@@ -1289,7 +1294,6 @@ int dso__load(struct dso *self, struct map *map, symbol_filter_t filter)
        int size = PATH_MAX;
        char *name;
        u8 build_id[BUILD_ID_SIZE];
-       char build_id_hex[BUILD_ID_SIZE * 2 + 1];
        int ret = -1;
        int fd;
        struct machine *machine;
@@ -1321,15 +1325,8 @@ int dso__load(struct dso *self, struct map *map, symbol_filter_t filter)
        }
 
        self->origin = DSO__ORIG_BUILD_ID_CACHE;
-
-       if (self->has_build_id) {
-               build_id__sprintf(self->build_id, sizeof(self->build_id),
-                                 build_id_hex);
-               snprintf(name, size, "%s/%s/.build-id/%.2s/%s",
-                        getenv("HOME"), DEBUG_CACHE_DIR,
-                        build_id_hex, build_id_hex + 2);
+       if (dso__build_id_filename(self, name, size) != NULL)
                goto open_file;
-       }
 more:
        do {
                self->origin++;
@@ -1345,6 +1342,7 @@ more:
                case DSO__ORIG_BUILDID:
                        if (filename__read_build_id(self->long_name, build_id,
                                                    sizeof(build_id))) {
+                               char build_id_hex[BUILD_ID_SIZE * 2 + 1];
                                build_id__sprintf(build_id, sizeof(build_id),
                                                  build_id_hex);
                                snprintf(name, size,
@@ -1933,6 +1931,12 @@ static size_t __dsos__fprintf_buildid(struct list_head *head, FILE *fp,
        return ret;
 }
 
+size_t machine__fprintf_dsos_buildid(struct machine *self, FILE *fp, bool with_hits)
+{
+       return __dsos__fprintf_buildid(&self->kernel_dsos, fp, with_hits) +
+              __dsos__fprintf_buildid(&self->user_dsos, fp, with_hits);
+}
+
 size_t machines__fprintf_dsos_buildid(struct rb_root *self, FILE *fp, bool with_hits)
 {
        struct rb_node *nd;
@@ -1940,8 +1944,7 @@ size_t machines__fprintf_dsos_buildid(struct rb_root *self, FILE *fp, bool with_
 
        for (nd = rb_first(self); nd; nd = rb_next(nd)) {
                struct machine *pos = rb_entry(nd, struct machine, rb_node);
-               ret += __dsos__fprintf_buildid(&pos->kernel_dsos, fp, with_hits);
-               ret += __dsos__fprintf_buildid(&pos->user_dsos, fp, with_hits);
+               ret += machine__fprintf_dsos_buildid(pos, fp, with_hits);
        }
        return ret;
 }
index 032469e..5d25b5e 100644 (file)
@@ -170,6 +170,7 @@ int machine__load_vmlinux_path(struct machine *self, enum map_type type,
 
 size_t __dsos__fprintf(struct list_head *head, FILE *fp);
 
+size_t machine__fprintf_dsos_buildid(struct machine *self, FILE *fp, bool with_hits);
 size_t machines__fprintf_dsos(struct rb_root *self, FILE *fp);
 size_t machines__fprintf_dsos_buildid(struct rb_root *self, FILE *fp, bool with_hits);
 
index cb54cd0..f55cc3a 100644 (file)
@@ -53,12 +53,6 @@ static unsigned long page_size;
 static ssize_t calc_data_size;
 static bool repipe;
 
-/* If it fails, the next read will report it */
-static void skip(int size)
-{
-       lseek(input_fd, size, SEEK_CUR);
-}
-
 static int do_read(int fd, void *buf, int size)
 {
        int rsize = size;
@@ -98,6 +92,19 @@ static int read_or_die(void *data, int size)
        return r;
 }
 
+/* If it fails, the next read will report it */
+static void skip(int size)
+{
+       char buf[BUFSIZ];
+       int r;
+
+       while (size) {
+               r = size > BUFSIZ ? BUFSIZ : size;
+               read_or_die(buf, r);
+               size -= r;
+       };
+}
+
 static unsigned int read4(void)
 {
        unsigned int data;
index 406d452..b3e86b1 100644 (file)
@@ -233,7 +233,12 @@ static inline unsigned long long __data2host8(unsigned long long data)
 
 #define data2host2(ptr)                __data2host2(*(unsigned short *)ptr)
 #define data2host4(ptr)                __data2host4(*(unsigned int *)ptr)
-#define data2host8(ptr)                __data2host8(*(unsigned long long *)ptr)
+#define data2host8(ptr)                ({                              \
+       unsigned long long __val;                               \
+                                                               \
+       memcpy(&__val, (ptr), sizeof(unsigned long long));      \
+       __data2host8(__val);                                    \
+})
 
 extern int header_page_ts_offset;
 extern int header_page_ts_size;
index 0795bf3..4e8b6b0 100644 (file)
@@ -81,7 +81,7 @@
 #include <inttypes.h>
 #include "../../../include/linux/magic.h"
 #include "types.h"
-
+#include <sys/ttydefaults.h>
 
 #ifndef NO_ICONV
 #include <iconv.h>
@@ -152,7 +152,6 @@ extern void warning(const char *err, ...) __attribute__((format (printf, 1, 2)))
 extern void set_die_routine(void (*routine)(const char *err, va_list params) NORETURN);
 
 extern int prefixcmp(const char *str, const char *prefix);
-extern time_t tm_to_time_t(const struct tm *tm);
 
 static inline const char *skip_prefix(const char *str, const char *prefix)
 {
@@ -160,119 +159,6 @@ static inline const char *skip_prefix(const char *str, const char *prefix)
        return strncmp(str, prefix, len) ? NULL : str + len;
 }
 
-#if defined(NO_MMAP) || defined(USE_WIN32_MMAP)
-
-#ifndef PROT_READ
-#define PROT_READ 1
-#define PROT_WRITE 2
-#define MAP_PRIVATE 1
-#define MAP_FAILED ((void*)-1)
-#endif
-
-#define mmap git_mmap
-#define munmap git_munmap
-extern void *git_mmap(void *start, size_t length, int prot, int flags, int fd, off_t offset);
-extern int git_munmap(void *start, size_t length);
-
-#else /* NO_MMAP || USE_WIN32_MMAP */
-
-#include <sys/mman.h>
-
-#endif /* NO_MMAP || USE_WIN32_MMAP */
-
-#ifdef NO_MMAP
-
-/* This value must be multiple of (pagesize * 2) */
-#define DEFAULT_PACKED_GIT_WINDOW_SIZE (1 * 1024 * 1024)
-
-#else /* NO_MMAP */
-
-/* This value must be multiple of (pagesize * 2) */
-#define DEFAULT_PACKED_GIT_WINDOW_SIZE \
-       (sizeof(void*) >= 8 \
-               ?  1 * 1024 * 1024 * 1024 \
-               : 32 * 1024 * 1024)
-
-#endif /* NO_MMAP */
-
-#ifdef NO_ST_BLOCKS_IN_STRUCT_STAT
-#define on_disk_bytes(st) ((st).st_size)
-#else
-#define on_disk_bytes(st) ((st).st_blocks * 512)
-#endif
-
-#define DEFAULT_PACKED_GIT_LIMIT \
-       ((1024L * 1024L) * (sizeof(void*) >= 8 ? 8192 : 256))
-
-#ifdef NO_PREAD
-#define pread git_pread
-extern ssize_t git_pread(int fd, void *buf, size_t count, off_t offset);
-#endif
-/*
- * Forward decl that will remind us if its twin in cache.h changes.
- * This function is used in compat/pread.c.  But we can't include
- * cache.h there.
- */
-extern ssize_t read_in_full(int fd, void *buf, size_t count);
-
-#ifdef NO_SETENV
-#define setenv gitsetenv
-extern int gitsetenv(const char *, const char *, int);
-#endif
-
-#ifdef NO_MKDTEMP
-#define mkdtemp gitmkdtemp
-extern char *gitmkdtemp(char *);
-#endif
-
-#ifdef NO_UNSETENV
-#define unsetenv gitunsetenv
-extern void gitunsetenv(const char *);
-#endif
-
-#ifdef NO_STRCASESTR
-#define strcasestr gitstrcasestr
-extern char *gitstrcasestr(const char *haystack, const char *needle);
-#endif
-
-#ifdef NO_STRLCPY
-#define strlcpy gitstrlcpy
-extern size_t gitstrlcpy(char *, const char *, size_t);
-#endif
-
-#ifdef NO_STRTOUMAX
-#define strtoumax gitstrtoumax
-extern uintmax_t gitstrtoumax(const char *, char **, int);
-#endif
-
-#ifdef NO_HSTRERROR
-#define hstrerror githstrerror
-extern const char *githstrerror(int herror);
-#endif
-
-#ifdef NO_MEMMEM
-#define memmem gitmemmem
-void *gitmemmem(const void *haystack, size_t haystacklen,
-                const void *needle, size_t needlelen);
-#endif
-
-#ifdef FREAD_READS_DIRECTORIES
-#ifdef fopen
-#undef fopen
-#endif
-#define fopen(a,b) git_fopen(a,b)
-extern FILE *git_fopen(const char*, const char*);
-#endif
-
-#ifdef SNPRINTF_RETURNS_BOGUS
-#define snprintf git_snprintf
-extern int git_snprintf(char *str, size_t maxsize,
-                       const char *format, ...);
-#define vsnprintf git_vsnprintf
-extern int git_vsnprintf(char *str, size_t maxsize,
-                        const char *format, va_list ap);
-#endif
-
 #ifdef __GLIBC_PREREQ
 #if __GLIBC_PREREQ(2, 1)
 #define HAVE_STRCHRNUL
@@ -293,28 +179,14 @@ static inline char *gitstrchrnul(const char *s, int c)
  * Wrappers:
  */
 extern char *xstrdup(const char *str);
-extern void *xmalloc(size_t size) __attribute__((weak));
-extern void *xmemdupz(const void *data, size_t len);
-extern char *xstrndup(const char *str, size_t len);
 extern void *xrealloc(void *ptr, size_t size) __attribute__((weak));
 
-static inline void *xzalloc(size_t size)
-{
-       void *buf = xmalloc(size);
-
-       return memset(buf, 0, size);
-}
 
 static inline void *zalloc(size_t size)
 {
        return calloc(1, size);
 }
 
-static inline size_t xsize_t(off_t len)
-{
-       return (size_t)len;
-}
-
 static inline int has_extension(const char *filename, const char *ext)
 {
        size_t len = strlen(filename);
@@ -351,8 +223,6 @@ extern unsigned char sane_ctype[256];
 #define isalpha(x) sane_istest(x,GIT_ALPHA)
 #define isalnum(x) sane_istest(x,GIT_ALPHA | GIT_DIGIT)
 #define isprint(x) sane_istest(x,GIT_PRINT)
-#define is_glob_special(x) sane_istest(x,GIT_GLOB_SPECIAL)
-#define is_regex_special(x) sane_istest(x,GIT_GLOB_SPECIAL | GIT_REGEX_SPECIAL)
 #define tolower(x) sane_case((unsigned char)(x), 0x20)
 #define toupper(x) sane_case((unsigned char)(x), 0)
 
@@ -363,38 +233,6 @@ static inline int sane_case(int x, int high)
        return x;
 }
 
-static inline int strtoul_ui(char const *s, int base, unsigned int *result)
-{
-       unsigned long ul;
-       char *p;
-
-       errno = 0;
-       ul = strtoul(s, &p, base);
-       if (errno || *p || p == s || (unsigned int) ul != ul)
-               return -1;
-       *result = ul;
-       return 0;
-}
-
-static inline int strtol_i(char const *s, int base, int *result)
-{
-       long ul;
-       char *p;
-
-       errno = 0;
-       ul = strtol(s, &p, base);
-       if (errno || *p || p == s || (int) ul != ul)
-               return -1;
-       *result = ul;
-       return 0;
-}
-
-#ifdef INTERNAL_QSORT
-void git_qsort(void *base, size_t nmemb, size_t size,
-              int(*compar)(const void *, const void *));
-#define qsort git_qsort
-#endif
-
 #ifndef DIR_HAS_BSD_GROUP_SEMANTICS
 # define FORCE_DIR_SET_GID S_ISGID
 #else
@@ -425,6 +263,19 @@ bool strglobmatch(const char *str, const char *pat);
 bool strlazymatch(const char *str, const char *pat);
 unsigned long convert_unit(unsigned long value, char *unit);
 
+#ifndef ESC
+#define ESC 27
+#endif
+
+static inline bool is_exit_key(int key)
+{
+       char up;
+       if (key == CTRL('c') || key == ESC)
+               return true;
+       up = toupper(key);
+       return up == 'Q';
+}
+
 #define _STR(x) #x
 #define STR(x) _STR(x)
 
index bf44ca8..73e900e 100644 (file)
@@ -23,46 +23,6 @@ char *xstrdup(const char *str)
        return ret;
 }
 
-void *xmalloc(size_t size)
-{
-       void *ret = malloc(size);
-       if (!ret && !size)
-               ret = malloc(1);
-       if (!ret) {
-               release_pack_memory(size, -1);
-               ret = malloc(size);
-               if (!ret && !size)
-                       ret = malloc(1);
-               if (!ret)
-                       die("Out of memory, malloc failed");
-       }
-#ifdef XMALLOC_POISON
-       memset(ret, 0xA5, size);
-#endif
-       return ret;
-}
-
-/*
- * xmemdupz() allocates (len + 1) bytes of memory, duplicates "len" bytes of
- * "data" to the allocated memory, zero terminates the allocated memory,
- * and returns a pointer to the allocated memory. If the allocation fails,
- * the program dies.
- */
-void *xmemdupz(const void *data, size_t len)
-{
-       char *p = xmalloc(len + 1);
-       memcpy(p, data, len);
-       p[len] = '\0';
-       return p;
-}
-
-char *xstrndup(const char *str, size_t len)
-{
-       char *p = memchr(str, '\0', len);
-
-       return xmemdupz(str, p ? (size_t)(p - str) : len);
-}
-
 void *xrealloc(void *ptr, size_t size)
 {
        void *ret = realloc(ptr, size);
@@ -78,73 +38,3 @@ void *xrealloc(void *ptr, size_t size)
        }
        return ret;
 }
-
-/*
- * xread() is the same a read(), but it automatically restarts read()
- * operations with a recoverable error (EAGAIN and EINTR). xread()
- * DOES NOT GUARANTEE that "len" bytes is read even if the data is available.
- */
-static ssize_t xread(int fd, void *buf, size_t len)
-{
-       ssize_t nr;
-       while (1) {
-               nr = read(fd, buf, len);
-               if ((nr < 0) && (errno == EAGAIN || errno == EINTR))
-                       continue;
-               return nr;
-       }
-}
-
-/*
- * xwrite() is the same a write(), but it automatically restarts write()
- * operations with a recoverable error (EAGAIN and EINTR). xwrite() DOES NOT
- * GUARANTEE that "len" bytes is written even if the operation is successful.
- */
-static ssize_t xwrite(int fd, const void *buf, size_t len)
-{
-       ssize_t nr;
-       while (1) {
-               nr = write(fd, buf, len);
-               if ((nr < 0) && (errno == EAGAIN || errno == EINTR))
-                       continue;
-               return nr;
-       }
-}
-
-ssize_t read_in_full(int fd, void *buf, size_t count)
-{
-       char *p = buf;
-       ssize_t total = 0;
-
-       while (count > 0) {
-               ssize_t loaded = xread(fd, p, count);
-               if (loaded <= 0)
-                       return total ? total : loaded;
-               count -= loaded;
-               p += loaded;
-               total += loaded;
-       }
-
-       return total;
-}
-
-ssize_t write_in_full(int fd, const void *buf, size_t count)
-{
-       const char *p = buf;
-       ssize_t total = 0;
-
-       while (count > 0) {
-               ssize_t written = xwrite(fd, p, count);
-               if (written < 0)
-                       return -1;
-               if (!written) {
-                       errno = ENOSPC;
-                       return -1;
-               }
-               count -= written;
-               p += written;
-               total += written;
-       }
-
-       return total;
-}